Repository navigation
Description
The wire half of #852, tracked separately so the PR that delivers it can declare Fixes without closing the umbrella. The UI half lives in #876; #852 stays open until both land.
PR #864 (0d1f5db8, PASS) already implements everything below. This issue is its tracker, not new work — if anything here is missing from that PR, the PR is what changes.
What this half delivers
The two envelopes — core/src/shared/room-share.js:
- ops
askandinvite; fieldsticket,accept,lead,expiresAt. askparses and encodes with noroomId— an invitee cannot know the room before being admitted;inviterequires the room it is for.- the ticket keeps the 16–64 character shape feat(rooms): mint and withdraw a local room invite link (#852) #861 fixed.
Admission — core/src/host/room-membership.js:
answerLocalInviteanswers a knock from the room record. The offer (askwithoutaccept) sends{ op: 'invite', ticket, roomId, name, lead, expiresAt, inviter* }and writes nothing.- The accepting knock runs verify → consume → add →
join(thejoinis sent byaddRoomParticipant). acceptedByis written once, at accept. The same home retrying is idempotent success and the lostjoinis re-sent; another home is refusedconsumed; an add the room refuses rolls the claim back, so a refusal never burns the ticket.- The asker is
event.fromHandle— the transport's sender; nothing in the payload can name it. - Five local refusals, all changing nothing:
unknown,expired,consumed,no-sender,not-addable. - Both new ops are dispatched before the inbound-room projection, so neither a knock nor an offer can create a room on either side.
Plugins — plugins/social/src/face-rooms.js, social.js, client.js:
parseInviteLinkreads both link shapes ({ kind, ticket, handle? });parseFaceInviteLinkanswers only a face ticket.- the paste field and the scanner use the same parse, and a local link into another FlyBot is answered by name instead of being handed to Bot Social as one of its tickets.
- the host refuses a local link by name before the feature gate, because that ticket was never this machine's.
Paper: docs/subsystems/social.md (two link shapes, three named refusals), docs/subsystems/core-host.md (the four #861 follow-ups), changelog.d/852-local-room-invites.added.md, and the patches/ds-bot/files/ mirrors this repository requires.
Acceptance
- Each of the admission rules above has a case that fails when its line is reverted — the negative controls already exercised on
0d1f5db8: a knock without aroomId, an offer that must not consume the ticket, a second home answeredconsumed, a refused add that must not burn the ticket, a same-home retry that must re-send the lostjoin, an expired ticket that resolves no room, and one case per local refusal. - Neither
asknorinvitecreates a room, on either side. ds-botmirrors are byte-identical tocore/,ds-bot-patches checkandarch-docspass.- Nothing in this issue builds UI: no Members card row, no redemption dialog, no preview component. Those are 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, and this issue does not close it or A local room has no invite of its own: no link to hand over, only a picker of Bots the account already knows #852.
Why it is its own issue
#864 cannot write Fixes #852: the umbrella still has the UI half open, and closing it there would make the history claim the feature is finished. Part of #852 is not a reference the enqueue guard accepts, so the PR needs a ticket that it does close — this one.
Activity
feat(rooms): answer a local room's invite link (#852) (#864)