Skip to content

Recognise a group link whose room is already here (#1176) - #1187

Merged
Birfy merged 1 commit into
mainfrom
dev3/fix/1176-invite-link-already-seated
Oct 25, 2026
Merged

Birfy merged 1 commit into
mainfrom
dev3/fix/1176-invite-link-already-seated

Conversation

@Birfy

@Birfy Birfy commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

Owner: Dev3
Reviewer: Reviewer2
Base: 604ffa3
Tip SHA: c87ae80
Fixes #1176

A group link for a room this home already holds was still read as a knock: the host matched the link
to a local copy only by sharedFrom.ticket, and a room the host added a Bot to by add-member carries
sharedFrom.remoteRoomId and no ticket, so joined was never computed (:1554-1556, :1564-1565) and
the client fell through to the Join card, whose claim changed nothing while the copy said "Asked to join"
(client.js:1698-1707).

Reproduce

main 604ffa3a (git-less box: files unpacked from the commit tree). In core/test/room-ticket-seat.test.js,
two homes and the fake Bot Social: mint a link on A, then project the same room onto B through the ordinary
add-member path (applyInboundRoomShare with { op: 'join', roomId: 'war', … }, no ticket). Before the fix
previewLocalInvite(link) answers { claimed: false, roomId: undefined, joined: undefined }; the shipped
dialog then draws the preview card and sends a claim on Join.

What changed

  • core/src/host/room-membership.js: the preview reply carries roomId and joined; joined no longer
    depends on claimed — it is true when this home holds the room (origin === 'inbound-share', not hidden,
    a local non-remote Bot in it) and either sharedFrom.ticket === ticket or sharedFrom.remoteRoomId === roomId.
  • plugins/social/client.js: previewLocal branches on seen?.joined before seen?.claimed, says
    This group is already in your sidebar., draws no Join card and sends no claim. The old
    "You already joined this group with that link." sentence is gone, so one state has one sentence.
  • Tests: core/test/room-ticket-seat.test.js — the add-member room is joined: true and the reply names
    roomId; a hidden copy of the same remote room id is not. plugins/social/test/invite-joined.test.js
    reads the shipped client.js for the branch order, the sentence, and that the branch offers nothing to join.
    plugins/social/test/social.test.js A local room's invite link has no Members-card row and no redemption dialog, and the preview card would be copied a second time #876 now covers claimed: false, joined: true and asserts no claim.
  • Mirrors: the two core/ files are whole-file entries under patches/ds-bot/files/; check passes.
  • Docs: core-host.md (how joined is decided, and the add-member match) and social.md (the dialog state).

What I ran

  • node --test "test/room-ticket-seat.test.js" "test/room-membership.test.js" "test/remote-room.test.js" "test/room-invite-preview.test.js" → 110 pass, 0 fail.
  • plugins/social: node --test "test/*.test.js" → 525 pass, 0 fail, 0 skipped.
  • node scripts/ds-bot-patches.mjs check → ok 10 patches apply cleanly in order, ok core/ equals ds-bot/ + patches/ds-bot/.
  • node .github/scripts/changelog.mjs check → 55 fragment(s) … well formed.

Red without the source change

Knives on this tip, each reverted afterwards:

  • Drop the remoteRoomId match → room-ticket-seat 20 pass / 1 fail, the failing one is #1176's add-member case.
  • Drop roomId from the preview reply → 19 / 2, both #1176 cases fail on roomId.
  • client.js: honour joined only when claimed (the old shape) → social.test.js 448 pass / 1 fail (#876, the claimed:false link).

Not run

  • Full core suite on this box: 28 files abort at import with ERR_MODULE_NOT_FOUND: esbuild/jsdom, the same
    set and the same reason on pristine 604ffa3a (checked side by side), so the evidence is the four suites above;
    CI's tests-ok runs the whole thing.
  • build-client --check and oxlint: esbuild is not installed here; no core/src/client/ file is touched and
    ds-bot-patches check compares core/client.js against patch 0008.
  • arch-docs: it needs git log and this box has no git; both owned docs are updated in this PR.

Risk

  • joined is now also computed for a link this home never claimed, from a room id match. It is matched only
    against this home's own origin: 'inbound-share' rooms that are visible and hold a local Bot, never against
    a room id alone, so a link cannot claim a room the host did not share here. claimed keeps its server meaning
    and the waiting state is unchanged.
  • Client: a link for a room already here no longer offers Join group. Re-pasting the same link repeats the
    sentence, and a claim with no room here still waits, so the way to knock again is untouched.

Routing: Reviewer: moved to Reviewer2 by Chief on 2026-10-25 so the queue runs on two gates — Reviewer holds #1183 (the contested Delivery board), Reviewer2 takes this one. Code and tip untouched.

A room this home was added to by add-member carries sharedFrom.remoteRoomId and no
ticket, so previewLocalInvite could not match the link to it: it answered claimed:false
and no joined, the dialog drew the Join card, and pressing it sent a claim that changed
nothing while the copy said "Asked to join".

- room-membership.js: carry the preview's roomId, and decide joined from this home's own
  rooms (inbound-share, visible, a local Bot, ticket or remoteRoomId match) instead of
  from claimed.
- plugins/social/client.js: read joined before claimed, say the group is already in the
  sidebar, draw no Join card and send no claim.
- Tests: two core cases (an add-member join is already joined; a hidden copy is not) and
  a source case pinning the branch order; social.test.js #876 now covers claimed:false.
- core-host.md / social.md say how joined is decided; changelog fragment.
@Birfy Birfy added the agent-pr Pull request opened by an agent label Oct 25, 2026
@Birfy Birfy self-assigned this Oct 25, 2026
@Birfy

Birfy commented Oct 25, 2026

Copy link
Copy Markdown
Collaborator Author

Verdict: PASS at c87ae80
Written-by: Reviewer2
Attestation: c87ae80 role=Reviewer2
Ran: tip unpacked from its own tarball (Node 22.23.3, TZ=UTC, DSH_HARNESS=/opt/flybot); base is the live main 604ffa3a, and diff -rq of the two trees lists exactly this PR's 10 files and nothing else

  • cd core && TMPDIR=/tmp node --test test/room-ticket-seat.test.js test/room-membership.test.js test/remote-room.test.js test/room-invite-preview.test.js → 110 pass, 0 fail
  • cd plugins/social && TMPDIR=/tmp node --import ./test/resolve.mjs --test --test-concurrency=1 "test/*.test.js" → 525 pass, 0 fail, 0 skipped
  • node scripts/ds-bot-patches.mjs check → ok 10 patches apply cleanly in order, ok core/ equals ds-bot/ + patches/ds-bot/; both patches/ds-bot/files/ mirrors are byte-identical to their core/ originals (room-membership.js a6cc361e…, room-ticket-seat.test.js 14e881c0…)
  • knife: drop the remoteRoomId arm of roomIsHere → room-ticket-seat 20 pass / 1 fail, red only #1176: … added to by add-member
  • knife: drop ...(roomId ? { roomId } : {}) from the preview reply → 19 / 2, both #1176 cases red on roomId
  • knife: both source files back to 604ffa3a → core 108 / 2 (exactly the two #1176 cases) and social 522 / 3 (the two invite-joined cases and #876); restoring the tip bytes returns 110/110 and 525/525

What I checked: the reported state is the one #1176 describes — a room the host seated by add-member carries sharedFrom.remoteRoomId and no ticket, so the old ticket-only match could never answer joined, and the dialog fell through to the Join card. joined no longer depends on claimed (room-membership.js:1560), and it still requires an inbound-share room of this home that is not hidden and holds a local non-remote Bot, so a room id alone never counts (the hidden-copy case is pinned). claimed keeps its server meaning; the client reads joined before claimed (client.js:1698), says one sentence, draws no card and sends no claim — social.test.js #876 asserts hostCalls is exactly ['room-invite-preview'], so the "no claim" half is behaviour, not text. room-invite-preview is a straight passthrough (channel.js:408), so roomId reaches the dialog. Both owned docs are updated (core-host.md how joined is decided and the add-member match, social.md the dialog state), the changelog fragment exists, Fixes #1176 is its own line and Tip SHA is this head.

Not run: full npm run verify (no git here, so arch-docs cannot diff; no oxlint); CI's tests-ok and Node 24 cover the whole core suite at this tip and are green, and no core/src/client/ file is touched, so client.js regeneration is not in play.

Non-blocking, for the record: (1) joined is now answered for a link this home never claimed, from a room-id match, so a copy of a room projected here by one home can pre-empt a link for the same remote room id from another; the branch sends nothing and grants no seat, but the sentence names no inviter, so the person has to read the sidebar to see who sent it. (2) A room already here no longer offers Join, so a second Bot cannot be added through this dialog — the same shape the old claimed && joined branch had (the reported state's claim changed nothing either), so this is not a narrowing this PR introduces; if that need shows up it is its own issue. (3) invite-joined.test.js slices previewLocal from its const line to the next \n {6}const …, so a reformat of that boundary swallows the rest of the file; the behavioural #876 case is the real cover.

@github-actions github-actions Bot added the pass label Oct 25, 2026
@Birfy
Birfy added this pull request to the merge queue Oct 25, 2026
Merged via the queue into main with commit 96abd5c Oct 25, 2026
12 of 14 checks passed
@Birfy
Birfy deleted the dev3/fix/1176-invite-link-already-seated branch October 25, 2026 14:43
@Birfy Birfy removed the agent-pr Pull request opened by an agent label Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Invite-link join stays on Asked to join when the Bot was already seated by add-member

1 participant