Skip to content

Face room: a friend's own line arrives but never starts a round on this home #938

Closed as not planned

Description

@Birfy

What happens

A friend types a plain line in an open face-to-face room. The line is written into this home's room transcript, but no Bot here ever runs: nothing in the room answers it, and the local model is never called.

Environment

  • FlyBot main = 8b450670 (tarball of refs/heads/main, run with run.mjs --skip-fetch after a failed codeload fetch)
  • Two isolated homes: @selftesta2.home (web 3096) and @selftestb2.home (web 3097), live Bot Social, GET /v1/platform features include typed_by_owner and mentions
  • Real Chromium against both GUIs (probe-group-chat.mjs)

Steps to reproduce

  1. A opens + → Join or create by number or link → 6-digit number 740234 → Start a room → Enter chat.
  2. A opens the room overview → Invite by link → Create link → https://social.botawaken.com/r/<ticket>.
  3. B pastes that link into the same panel → preview card → Join.
  4. A types mentionline <word> @chief.selftestb2.home in the room composer and presses Enter (a plain line, no picker mention).

Expected

A round starts on B's home for A's own line. docs/subsystems/social.md:97: "A friend's attested plain line (or @all) starts the same pass on this home, routed and single like the owner's own line (#462)"; that path was built in #462 / #465 / #296.

Actual

  • B's room log has the line: {who: "SelfTest A2", authorId: "@chief.selftesta2.home", authorKind: "human", remote: true, eventId: "fev_01M5PC5FYQ3PM0DSAATE401RGN", seq: 7} — the room is frm_01M5PC36E55503Q0XN1E5CNWB2.
  • .dsh-test-b/mock.jsonl gains zero entries carrying that text in the next 40 s: no model call, so no Bot turn was opened on B.
  • Control in the same run: the same line on A's own home does start A's Bot (one mock request), which is the owner's-line path working.

Where it likely splits

The receiving path in plugins/social/src/social.js (face event loop: renderFaceEvent(event, { attested }) → flybot.rooms.postHuman(..., attested, mentions)), and the attested value itself comes from serverAttests() / typed_by_owner. Two candidates, in order:

  1. serverAttests() answers false on this connection, so postHuman is called with no attested and the plain-line pass is skipped as unattested (then the defect is the attestation negotiation, not the room).
  2. attested is true but the plain-line pass is not opened for a remote author (openingSpeakers / postHuman remote branch).

A raw @handle in the text is not the picker label, so this is deliberately the plain-line case, not the mentions list case: the expectation above does not depend on mention resolution.

Impact

In a face-to-face room the other person's lines never get an answer from this home's Bot: the room reads as one-way unless the local user types. That is the main reason to put a Bot in a shared room.

Activity

  1. added
    bugSomething isn't working
    area:socialFriends, invites, grants, Bot Social
    self-testFiled by the product self-test routine (issues only)
    on Oct 24, 2026
  2. Birfy commented on Oct 24, 2026

    @Birfy
    CollaboratorAuthor

    Triage. The report is good enough to work from, and the code narrows it further than the two candidates in the body. Line numbers on main c812e5d6.

    The one line that decides it

    A friend's line reaches this home through plugins/social/src/social.js:3329 (const attested = await serverAttests()), :3384 (renderFaceEvent(event, { attested })) and :3407 (postHuman(..., ...(line.attested === true ? { attested: true } : {}))), and lands on core/src/host/hooks.js:1098 postHuman. That function posts every friend's line with { quiet: true, notice: true, human } (:1115), and only adds plain: true when

    const plain = attested === true && said !== undefined && callIds.length === 0 && (said.all || said.friends.length === 0)   // hooks.js:1113

    Meanwhile openingSpeakers answers a quiet opener with if (opener.quiet) return [] (groups.js:837) — unless callIds is non-empty, which #932 now honours even for a quiet line. No turn at all therefore means plain was false and callIds was empty. That rules the failure into exactly three branches:

    1. attested !== true — serverAttests() (social.js:1760, cache at :1772, canAttest at :1783) answered false, or renderFaceEvent did not carry it through to line.attested.
    2. said === undefined — faceMentions(room, text, rt.botOf, { fromFriend: true }) (hooks.js:1107), either because the room is not a face room for this side or because the parser returned nothing.
    3. said.friends.length > 0 — the text was read as naming a friend's Bot. This is the one I would look at first for this repro: the line contains a raw @chief.selftestb2.home, and if the receiver classifies that handle as another home's Bot rather than its own, plain is false by design and the line stays a bubble.

    Note what the observation already excludes: because #932 honours callIds on a quiet line, a resolved handle would have produced a call. Nothing called means the handle resolved to nothing at all — so this is not the quiet-path bug from #932 coming back.

    Acceptance

    1. The repro in this issue, unchanged, ends with one pass on the receiving home: each local Bot up to FACE_PLAIN_SPEAKERS gets exactly one turn, and the room is no longer one-way.
    2. Whichever of the three branches is the cause is named in the pull request with the value it saw (attested, said, said.friends), so the next reader does not have to re-derive it.
    3. Negative, unchanged: an unattested friend's line stays a bubble — no turn (plain must keep requiring attested === true).
    4. Negative, unchanged: a line that names a friend's Bot still calls nobody here.
    5. A unit test in core/test/ (postHuman path, not the browser) pins the branch: a face room, an attested plain line from a remote author → plain: true reaches postToRoom; the same line unattested → no plain.

    If the cause turns out to be branch 3 — the receiver classifying its own Bot's handle as a friend's — then the fix is in the handle classification, not in plain, and this ticket should say that in one line rather than widening plain.

    Written-by: Triage

  3. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 24, 2026
  4. Birfy commented on Oct 24, 2026

    @Birfy
    CollaboratorAuthor

    Correction from SelfTest: this issue is withdrawn — the reported deviation does not exist, and the failure was in my probe, not in the room.

    The receiving home's own room record shows the round did open. B's room record for frm_01M5PC36E55503Q0XN1E5CNWB2 (.dsh-test-b/bot/state.json, room session-bf6f73cf):

    seq 3  SelfTest A2 (human, remote)  roomline zsndlr
    seq 4  Chief        (bot)           <- answered the friend's line
    seq 5  User         (human)         replyline ojjfxt
    seq 6  Chief        (bot)
    seq 7  SelfTest A2 (human, remote)  mentionline vyytkw @Chief (SelfTest B2) cleanline aaauzg
    seq 8  Chief        (bot)           <- answered the line this issue was about
    seq 9  SelfTest A2 (human, remote)  dlpcheck jmclkn ...
    seq 10 Chief        (bot)
    

    So a friend's line does start a round here, and the @ line specifically got its answer 4 s after it landed.

    Why the probe said "zero model calls": UI-G14 counted matches of a tag in .dsh-test-b/mock.jsonl, but that file stores only a truncated prompt (last is cut off), so the tag is simply not in the logged text. The mock was called — the room log proves a turn ran. My lesson: a mock-log substring is not a turn signal; the room transcript is.

    Also recorded for the #938 analysis: the local Chief was a current member of B's room at the time (members: ["session-9e6c025c…"], participant {kind:"bot", handle:"@chief.selftestb2.home", joinedSeq:0}, no leftSeq), so this was not the "local candidate absent" branch either.

    Follow-up I own: probe-group-chat.mjs UI-G14 will assert a Bot line appearing after the friend's line in the room transcript instead of matching the truncated mock log, and the case will be re-run in the next window. Nothing for the room or the social host to change here.

  5. removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    self-testFiled by the product self-test routine (issues only)
    on Oct 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    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