Skip to content

a link-admitted guest still needs a friendship: its room envelopes are carded, and approving the card ends in no_relationship #923

Closed

Description

@Birfy

Reported by the owner, 2026-10-24, with a screenshot of the approval card: "this is still broken, and I still cannot receive the messages other people send."

The card reads Group update for Chief in a group chat, Chief → Chief, with an Auto-send switch, and the error under it is Bot Social decide_approval: no active relationship between these bots. So the send was held, the owner approved it, and the approval itself was refused. Pressing Approve can never work on this card — the two Bots have no relationship by construction, because a link admits a seat, not a friendship.

Where each half is produced (read at the refs named; not executed here)

FlyBot side — why a room envelope became an approval card instead of being exempted. #883 added the connector exception: ticketPair in plugins/social/src/social.js:1556-1566 passes a send only when msg.via is room/room-share, meta.scope is the room, the text is that room's whole envelope, and the pair is in flybot.rooms.ticketPeers(). That list is ticketPeers in core/src/host/reach.js:212-241, and for a seat in one of this home's rooms it requires all four of:

:226  if (p.kind !== 'bot' || p.remote !== true || p.admittedBy !== 'ticket' || !p.hostBotId) continue
:227  if (!(room.members ?? []).includes(p.hostBotId)) continue
:228  peers.push({ from: p.hostBotId, to: handleOf(p), scope: String(room.id), ticket: p.ticket })

Hypothesis to check first: a guest admitted by a ticket that does not record which local Bot invited them gets a seat with no hostBotId (core/src/host/room-membership.js writes ...(invite.botId ? { hostBotId: invite.botId } : {})), so :226 skips it, the pair is absent, ticketPair returns undefined, and the envelope falls into the ordinary approval path — the card in the screenshot. The same list feeds reach at :259-261, which decides whether the send is allowed at all.

Bot Social side — why approving it cannot succeed. The held item is an outbound_message approval (src/platform.ts:2093-2096, status pending_approval). On approve, decideApprovalOnce (:1910) calls deliverMessage (:2156), which accepts the conversation only when the pair's relationship is a room one:

:2163  if (cRel && cRel.status === 'room') { …room grant checks… }
:2167  } else this.activeRelationship(sender.id, recipient.id)

and activeRelationship (:872-876) throws exactly 403 no_relationship — "no active relationship between these bots". So a card whose conversation is not status: 'room' is unreleasable. Auto-delivery never ran either: it needs roomRel from the send's own room context (:2101-2111, byRoom = !!roomRel && refs.length === 0).

The three things to pin — each with a test, not a paragraph

  1. Which condition at reach.js:226-228 fails for this guest, and why. Print ticketPeers() for the reported room and name the failing one. If it is hostBotId, find where the claim loses invite.botId (the #884 server-minted claim path) and fix that, rather than widening the exception.
  2. Why a seat the roster and the Members card already show can still miss the room gate. #904/#907 fixed the guest's display of the host's Bots; this is the send direction, and it must not be decided by display facts. Say which fact the gate reads and why it can be absent.
  3. What #883 covered and what it did not. It covered "a room envelope from an enumerated ticket pair skips the card". It did not cover (a) a seat whose pair cannot be enumerated, or (b) the settle path at all: once a room envelope is carded, deliverMessage:2167 can only refuse. So the second half of this issue is: a room envelope that is held must be releasable, or must never be carded.

Acceptance

  • Host side: a guest admitted by a link into one of my rooms, with no friendship in either direction, sends a group update and an ordinary line; both are received, and no approval card is produced for either.
  • Guest side: the same two sends in the other direction are received. The screenshot only proves the host→guest card; if this direction has a different cause, split it into a second issue with its own lines rather than folding it in silently.
  • Negative control: a Bot that is neither seated in the room nor a friend still gets the approval card, and the refusal still names the step the owner can take. The exception must not widen to strangers.
  • If a room envelope is ever carded (a DLP flag, an attachment, suspected injection — the cases #45/#57 keep), approving that card delivers; it must not end in no_relationship. State in the PR what makes the conversation a room one at that moment, or why the card is refused with a sentence that names the real step.
  • Every refusal keeps its own sentence: no raw no_relationship, no decide_approval on screen.
  • A red test on the current main for at least the host-side case. If the honest proof needs two Bot Social instances, say exactly what the harness drives and what it fakes — the #912 lesson: a check that silently does nothing looks green.

If the cause is on the Bot Social side

Then this issue keeps the FlyBot half (the pair must be enumerable, and a carded room envelope must be releasable with a sentence), and a separate issue on Birfy/bot-social carries the deliverMessage:2167 half: roomRel for a conversation that began under a room, so an approved room message does not fall back to friendship. Open it there with these lines rather than quietly widening this one.

Open question — the other screen, not blocking this issue

The screenshot proves the outbound card (host → guest). It cannot prove the inbound symptom, so the two directions are recorded separately:

  • If the guest's FlyBot shows "Not sent: … you are not friends with this Bot", it is the same no_relationship refusal on its sending side, and one server fix covers both directions. Tracked on Birfy/bot-social#67.
  • If the guest's FlyBot shows the message as sent while nothing arrives here, the message left and never landed: a delivery problem, to be traced separately with its own issue.

Waiting on the user's answer from the guest's screen. It does not block the two FlyBot tasks (the pair must be enumerable; a carded room envelope must be releasable). When the answer arrives, add it here; do not change this issue's acceptance because of it.

Writer side

Birfy/bot-social#67 carries the settle half: deliverMessage (:2156-2167) and activeRelationship (:876). It states the check is sender-side, so this issue's FlyBot half, on its own, cannot make the message arrive.

Related

#883 / #884 — the connector exception and the seat; #904 / #907 — the guest's view of the host's Bots, which is done and is not this; #881 / #866 — room_id on a claimed-room send, the sibling of this on another path; #59 / #60 / #61 on Birfy/bot-social — the server-side ticket work.

Activity

  1. added
    bugSomething isn't working
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    area:socialFriends, invites, grants, Bot Social
    on Oct 24, 2026
  2. added a commit that references this issue on Oct 24, 2026
    f27d234
  3. added
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Oct 24, 2026
  4. self-assigned this
    on Oct 24, 2026
  5. Birfy commented on Oct 24, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Dev3

    Claiming this one. Branch fix/923-room-link-seat-pair, PR #927 (Fixes #923), head f27d23493c55d35371bc19582622a79319492c8f, base 0b22aa53 (current main).

    Root cause, one layer below the line numbers in the issue: reach.js:226 is the symptom. The local-link accept (answerLocalInvite) seats the guest through addRoomParticipant, and that record carries none of the three fields ticketPeers requires (admittedBy: 'ticket', ticket, hostBotId) — only the server-ticket seatByTicket writes them. So a room opened from a locally minted link never enumerates the pair, its room envelopes name no room, Bot Social holds them as pending_approval, and approving ends in no active relationship.

    Fix: the accept records the link's ticket and the Bot it was minted for, and ticketPeers also reads the link's own homes record, so a room opened before this is the same pair without rewriting anything on load. A seat no link names (a friend's Bot added by hand) stays out, and that is asserted.

    The card in the report is outbound (host -> that guest): shownRoomShareText renders every op other than join/leave/line as Group update for <to>, and Approve / Auto-send from now on is the outbound pair hold. The reverse direction is not decided here; it needs the other machine's screen and, if the cause is the server-side fallback, Birfy/bot-social#67.

  6. added a commit that references this issue on Oct 24, 2026
    07f7546
  7. removed
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Oct 24, 2026
  8. Birfy commented on Oct 24, 2026

    @Birfy
    CollaboratorAuthor

    Residual from the #923 review, recorded here and not opened as a ticket yet — the condition that would make it one is written at the end.

    Reviewer2's second note: writing admittedBy: 'ticket' on a link seat also moves that seat into #884's ticket-seat branch (seatSender as the sender, roster sync no longer removes it, removal goes through settleTicket, room deletion adds a leave). Meanwhile reach.js:253 falls back to room.admin when the recorded hostBotId is no longer in room.members, and seatSender has no such fallback — so a room whose recorded host left the members can produce a pair and still send no envelope.

    Not opened now because no reachable shape is demonstrated: it needs the recorded host to leave room.members while the room survives. When such a room is observed (or when anyone touches seatSender), it becomes a bug | area:social issue with two tests: pair-but-no-envelope must send, and the friend-Bot negative control must still be refused.

    The first note is opened as #929, because that one does have a reachable shape: a link accepted before homes was written enumerates no pair, which is the same symptom on rooms that already exist.

  9. removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 24, 2026
  10. added 2 commits that reference this issue on Oct 24, 2026
    137cd2f
    c426020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:socialFriends, invites, grants, Bot SocialbugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions