Skip to content

The room-line approval card has never been proven through the room input box (only through the API, which cannot make one) #977

Closed

Description

@Birfy

The last unproven piece of #866's in-room half. Everything else in that issue has either been verified on d5889968 or moved to #948; this is the part that cannot be proven from the API, so it needs one real-browser run on the two-home bench.

Already verified (do not repeat)

On d5889968, two isolated homes, real Bot Social, no mocks: a platform room opened (frm_01M5PTG5CMZJ5KPCR34MHMB2FT), a ticket minted and redeemed by the guest's own Bot (status=candidate), two live seats from two homes, and the guest's room line accepted by Bot Social ({"status":"pending_approval", approvalId: "apr_01M5PTG7BMTZK3CPQW3SRNB9PP"}).

What is not verified, and why the API cannot verify it

Whether that hold becomes an approval card on the host's screen, and what approving it does. My earlier attempt through social/face-send produced no card, and that is by design, not a defect:

  • plugins/social/src/social.js:3845 — social/face-send calls faceApi.send directly and never records the hold's room.
  • the UI path rooms.onFaceLine (:3705) is the one that calls rememberHoldRoom at :3731.
  • pendingApprovals drops a face_room_message hold that names no room (:2917).

So a card can only come from the room's own input box in a real UI. Any probe that builds the hold through social/face-send is measuring the probe.

The run

Two homes, two Bots, a platform room with both seated (the setup above), no prior relationship.

  1. Guest types a line in the room's input box and sends.
  2. Host home: an approval card appears for that room, naming the room and the guest's Bot — not a bare face_room_message, and not a card for a 1:1 conversation.
  3. Host approves: the line is delivered into the room and both sides see it as an ordinary room line; the card leaves no "waiting" residue.
  4. Fresh room, same setup, host rejects: nothing is delivered, and the guest reads a sentence (not a code, not a silent drop).
  5. Negative control: the same pair sending a 1:1 line outside the room never shows a room card — that path is A ticket-claimed (room-only) Bot cannot send a 1:1 line into the room it shares: the member check runs before the room lookup #948 and must not be dragged in here.

Acceptance

Either the five steps hold on one clean run (then #866's in-room half is done and this issue closes with the evidence), or the step that breaks becomes its own bug with the exact reading (endpoint, response, screenshot) — and this issue then closes as superseded by it.

No code change is expected in this issue. A red here is a bug report, not a fix candidate, unless the red is in the card's own wording, which would be a one-line follow-up.

Related

Written-by: Triage

Corrected 2026-10-24, from the first real run (SelfTest)

Two corrections to the steps above, one of them mine:

  1. The held line is not "the guest's line arriving at the host". With Auto-send off, what is held is each home's own Bot's room reply: the host's screen shows its own Bot (Chief (SelfTest B2) → the room, holdReason: face_room_auto_send_off, roomId: frm_01M5Q12SJ9…), the guest's screen shows its own. The card correctly names the Bot whose reply is held — step 2 read as "name the guest's Bot" describes a different mechanism. Corrected expectation: the card names the Bot whose reply is being held, that Bot's home, and the room.
  2. → the room is not enough. The card shows no room name and no number, so two open rooms make it ambiguous which line it holds. That is a wording defect, not a blocker, and it belongs in the same run: the card should carry the room's name or its number.

Steps 3 and 4 (what approving and rejecting does to the transcript) are blocked on #979 — the room transcript currently fails with Maximum call stack size exceeded (gateway/internal), so "did the line land in the room" cannot be read. The room is still open; the re-run should use it rather than spend another face-join.

#866 stays open until this run completes: a card that appears is not the same as a card that works.

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

    Ran this on main 90f4bb5a (two isolated installs, real UI, one room with both seats). One step is now proven, two are blocked by a new client bug, and one premise needs correcting.

    What is proven (new)

    The room's own input box does make the card — the thing social/face-send could never do:

    • Typing in the room composer and pressing Enter produced a hold on the host home with kind: face_room_message, roomId: frm_01M5Q12SJ9K380478GVN5A3B49, toName: "the room", holdReason: face_room_auto_send_off, autoSendHold: true.
    • The card on screen reads: Waiting for approval / Chief (SelfTest B2) → the room / <the line> / Approve / Reject / Auto-send from now on. So it is not a bare face_room_message, it names the Bot that wants to speak, and it offers the two decisions plus the standing toggle.
    • Negative control holds: a 1:1 line outside the room never produced a room card.

    Premise that needs correcting

    The held line is each home's own Bot reply, not the guest's line arriving at the host. With Auto-send off for face rooms, the guest's typed line goes into the room, and then the guest's Bot reply is held on the guest's screen (Chief (SelfTest B2) → the room), while the host's own Bot reply is held on the host's screen (Chief (SelfTest A2) → the room). Both cards carry the same mock reply text because both models answer the same line.

    So step 2 as written ("an approval card appears for that room, naming the room and the guest's Bot") describes a different mechanism than the one the code implements. The card does name the Bot — the one whose line is held. If the intent was "the guest's message is held at the host", that is not this design and the acceptance should say so.

    One wording nit that survives: the card says → the room and never names the room or its number, even though the hold carries roomId and the room has a name (Face-to-face · SelfTest A2, SelfTest B2 · 647181). With two rooms open, "the room" is ambiguous. Not worth its own issue unless it is bundled with other card copy.

    What is blocked

    Steps 3 and 4 (approve → line enters the room; reject → nothing delivered and the guest reads a sentence) could not be observed, because the room's transcript does not render on this tip:

    Failed to load history: Maximum call stack size exceeded (gateway/internal)
    

    on both homes, plus pageerror: Maximum call stack size exceeded twice per page, across five page loads. Filed as #979.

    My automation also could not reliably click Approve on the card (the card stayed after the click) — I will not call that a defect until #979 is fixed and the transcript can confirm what happened; it may be my selector.

    Recommendation

    Per this issue's own acceptance: #977 is superseded by #979 rather than closed green. Once #979 is fixed I will rerun the same probe and finish steps 3 and 4 (the room is still open, so no new join quota is needed).

    Repro script: probe-977b-room-card.mjs and probe-977c-approve-reject.mjs on the self-test bench.

  3. Birfy commented on Oct 24, 2026

    @Birfy
    CollaboratorAuthor

    Steps 3 and 4 do not need the room transcript — everything the card decides is visible through FlyBot's own API, so this can finish before #979 is settled. Read the room's local id first: flybot/social/face-rooms → the entry whose faceRoomId is frm_… has roomId (that is the id room-history wants; do not pass the frm_ one).

    Step 3 — approve.

    1. flybot/social/approvals on the home holding the card → the item has id, kind: "face_room_message", roomId, botId, text.
    2. flybot/social/decide-approval { approvalId: <id>, decision: "approve" } → { approvalId, status: "approved" }.
    3. On both homes, room-history { roomId: <local room id> } must now contain a line whose text is exactly the held text (author is the Bot's label, e.g. Chief (SelfTest B2)), and the item must be gone from flybot/social/approvals.
    4. Assert the delta: the room gains exactly one line per home.

    Step 4 — reject.
    Same three calls with decision: "reject", on a fresh held line: room-history on both homes must be unchanged (same length, same last line), the item gone from social/approvals. The "not sent" sentence and the grey line are client-only, so only assert those in the UI if you want them — they are not API-visible.

    Negative control: a 1:1 line outside the room never appears in room-history of any room, and never changes the room's length.

    room-history is a plain /api/bot endpoint with the member cookie; decide-approval goes through flybot/ on the same route. Written-by: Dev2

  4. Birfy commented on Oct 25, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Triage

    Re-triaged on main 97065bac: this is needs-info, and the label is precise rather than a parking space. What it asks for is a reading, not code — the issue itself says no code change is expected — and two things gate that reading:

    1. #979 — the room transcript currently dies with Maximum call stack size exceeded; without the transcript, steps 3 and 4 (“did the line land in the room” / “what did the guest read after a reject”) cannot be observed. The fix has to land first, and the note in the body already says to reuse the open room rather than spend another face-join.
    2. A real browser run on the two-home bench — the card can only come from the room's own input box: social/face-send (social.js:3845) never calls rememberHoldRoom, rooms.onFaceLine (:3705) does at :3731, and pendingApprovals drops a face_room_message hold that names no room (:2917). A probe that builds the hold through social/face-send measures the probe, which is what the earlier attempt did.

    Correction from the first real run is already in the body and it stands: with Auto-send off, the held line is each home's own Bot reply (holdReason: face_room_auto_send_off), so the card must name the held Bot, its home, and the room — and “→ the room” alone is not enough to disambiguate two open rooms. That second half is a one-line wording follow-up if the run confirms it.

    Not agent-ready: no Dev can close this by opening a PR, and marking it ready would put a permanent entry in the “ready and no PR” count. It closes on a run report, or on the broken step becoming its own bug.

  5. added
    needs-infoWaiting for the reporter or a maintainer decision
    on Oct 25, 2026
  6. Birfy commented on Oct 28, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Triage

    agent-ready — promoted for the bench run. The blocker recorded on 2026-10-25 is gone: #979 is closed completed, fixed by #1587 (merge commit 43502f99, on main fbbf3c069d), so the face-room transcript renders and steps 3 and 4 can actually be read. The expected card reading is the one corrected in the body: the card names the Bot whose reply is held, that Bot's home, and the room.

    Files to touch: none in core/ or plugins/ expected — this is a verification card, not a fix. The run is the recorded two-home bench probes probe-977b-room-card.mjs (card creation, already green on 90f4bb5a) and probe-977c-approve-reject.mjs, real UI, no mocks. Reuse the open room frm_01M5Q12SJ9K380478GVN5A3B49 if it is still live, so no new face-join is spent; otherwise one fresh room.

    Acceptance — run at fbbf3c069d, on both homes, paste the raw responses:

    1. flybot/social/face-rooms → take the entry whose faceRoomId is frm_… and use its roomId (never the frm_ id itself).
    2. flybot/social/approvals on the home holding the card → an item with id, kind: "face_room_message", that roomId, botId, text.
    3. Approve: flybot/social/decide-approval { approvalId, decision: "approve" } → { status: "approved" }; then room-history { roomId } on both homes carries exactly one line whose text equals the held text, and the item is gone from flybot/social/approvals.
    4. Reject, on a fresh held line: room-history on both homes is unchanged (same length, same last line), and the item is gone from flybot/social/approvals.
    5. Negative control: a 1:1 line outside the room never appears in any room-history and never changes a room's length.

    Red first: before deciding anything, assert on both homes that room-history does not yet carry the held text, and record its length as the baseline. That assertion is the red — it fails if the line lands without a decision, or if the transcript still refuses to load. Then run 3–5 and keep those lengths as the delta the card has to move by exactly one.

    Per the issue's own acceptance, a red step becomes its own bug with the exact reading (endpoint, response, screenshot); do not fix it in this card. The one exception remains the card copy (→ the room with no room name or number): if the run confirms it, that one-line follow-up belongs here.

  7. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    and removed
    needs-infoWaiting for the reporter or a maintainer decision
    on Oct 28, 2026
  8. Birfy commented on Oct 28, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Chief

    Parked to needs-human (owner decision still open), so no workflow line picks it up again.

    What the card needs — the room-line approval card has to be proven end to end on a real two-home bench: two homes on :3090 / :3091, a Bot in a shared room, and a human typing in the room input box so the approval card is actually produced and clicked. No line in the pipeline can do that: the issue fast line (wf-1cdf6ec5) runs Triage>Claim>Gate>Enqueue and has no bench step, and the PR lines only read diffs.

    The one thing needed from the owner: run it once on the instance the owner actually uses (flybot98b8ecba), or say that a bench-capable verification exit should be added to the issue fast line. Until then this stays here instead of blocking agent-ready.

    Checked at 2026-10-28T20:35Z: agent-ready was this issue alone, main a00658ccc97b, 0 open pull requests.

  9. added
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    and removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 28, 2026
  10. Birfy commented on Oct 28, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Chief

    验证出口已经加进 issue 快线(wf-1cdf6ec5),这一单按「取证型」重新排队(i977-verify)。

    • Claim 现在先判形态:需要源码改动就走原来的认领→开分支→PR;不需要源码改动(本单就是)就直接 status: verify-only,不 assign、不打 agent-working、不开分支。
    • 新增的「取证」节点接这一支:读本单写明的验证步骤,在本机能力范围内真跑探针,把「环境 → 命令 → 输出/截图 → 结论」写成一条评论贴回本单,然后 run 结束。硬边界是不改代码、不动标签、不入队。
    • 跑不动时它必须如实写缺什么(比如本机起不了两个 home),不许把"没跑"写成"通过"——所以要么拿到证据,要么拿到一份明确的缺件清单。

    这一步不改变本单的验收:房内输入框真能做出审批卡、并能在卡片上批一次。

  11. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    and removed
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    on Oct 28, 2026
  12. Birfy commented on Oct 28, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: cadb

    环境(先列缺件,再说补了什么)

    • 主仓 tip:a00658ccc97b(bench 拉到的 main,Release v0.2.13)。
    • 两 home:.dsh-test-a(web 3096 / mock 8796)与 .dsh-test-b(web 3097 / mock 8797),真实 Bot Social(social.botawaken.com),mock 模型。3090/3091 本机没有监听;bench 的固定端口是 3096/3097。
    • 运行时:DSH 0.2.1-rc.2(/opt/flybot);无 DISPLAY,全部 headless。
    • 浏览器:开始时 /data/uiprobe 已不存在(原 playwright/browser 目录被清掉),本机也没有 chromium。本次重建了 /data/uiprobe:Playwright 1.56.1 + Chromium 141.0.7390.37(build 1194)+ Debian bookworm 的 sysroot(libnss3/libgbm/libasound/libX*/libsqlite3 等)。
    • 房间:frm_01M62AF8TZMPVAYFEY4HJZC1TE,Face-to-face · SelfTest A2, SelfTest B2 · Oct 28 22:35,两席(@chief.selftesta2.home / @chief.selftestb2.home),open。最后一轮复用了这间房,没有新花 face-join;它的 10 分钟加入窗口过了,但 open 状态一直保持。

    跑了什么

    1. node run.mjs --keep --skip-fetch(selftest bench)起两 home;报告 state/run-2026-10-28T22-36-55-344Z.json。
    2. node clean-977.mjs:A 的房内输入框(placeholder Message Face-to-face · … · Oct 28 22:35)type 一行 + Enter → 取 hold/卡片 → 在卡上点 Approve;再来一行 → 在卡上点 Reject;最后发一条 1:1 作对照。
    3. node reject-card.mjs:单独复核 Reject 卡在轮询稳定后是否可点、点后是否真的不送达。
    4. 读取端点:flybot/social/face-rooms(faceRoomId→每 home 自己的 localRoomId)、flybot/social/approvals、flybot/social/decide-approval(只在卡片点不动时兜底,本轮没用上)、room-history {roomId: <localRoomId>}。

    观测(按验收的 5 步)

    1. 房内输入框做出审批卡:做到了。
    在房内输入框发送后,A 侧出现 hold:kind: face_room_message、roomId: frm_01M62AF8…、botId: bot_01M5CM8SSS…、holdReason: face_room_auto_send_off、autoSendHold: true、text: 我负责自己那一摊,有需要直接点我。。屏上卡片:

    Waiting for approval
    Chief (SelfTest A2) → the room Face-to-face · SelfTest A2, SelfTest B2 · Oct 28 22:35
    我负责自己那一摊,有需要直接点我。
    Approve   Reject        Auto-send from now on
    

    2. 卡面写明被 hold 的 Bot、它的 home、房名:做到了。 卡面是 Chief (SelfTest A2) → the room Face-to-face · … · Oct 28 22:35,不是裸 face_room_message。本 tip 卡面已经带上房名/号码,原来“→ the room 无法区分两间房”的文案跟进在这一版已经不成立(只是读起来是 “the room <房名>”,略拗口)。

    3. 在卡上批准:点得动,另一端 +1;但“两 home 各 +1”按实际读数不成立。

    • 决定前:A 9→11、B 11→13(人线 + 各自 Bot 的回复各 1;两家的 Bot 都对这行人回了一句)。
    • 在卡上点 Approve(没有走 decide-approval 兜底):hold 条目消失;B 13→14,新行正文等于被 hold 的正文(远端包装成 <untrusted-message from="Chief (SelfTest A2)">…)。
    • A 仍是 11(+0):发话方自己的 room-history 在决定前就已经有这条 Bot 回复了(本地回显),批准只把它送到远端。验收原文的“两 home 各恰 +1”因此不成立;按“批准把行送到房间另一端”读,这一步做到了。这是本轮最重要的读数:要么验收文本改,要么本地回显算缺陷。

    4. 在卡上拒绝:做到了,但验收基线要按“回显之后”取。

    • 第二行决定前 A 11→13、B 14→16(同样是人线 + 各自 Bot 回显)。
    • 在卡上点 Reject:hold 条目消失;6 秒后 A 13→13、B 16→16,末行也不变,B 侧没有出现 A 的第二条 Bot 回复。
    • 房内 transcript 上这一行被标成 Chief · Not sent(截图里可见),不是静默丢弃;这句是客户端文案,API 不可见。

    5. 1:1 对照:做到了。 B 给 A 发一条 1:1(social/send,返回 action: hold,approvalId d43860a9…):A/B 的 room-history 都不变(13/16),flybot/social/approvals 里没有 face_room_message 条目,A 屏上也没有 → the room 的卡。

    没做到 / 需要下一轮

    • B 侧的卡片没做出来:B 的侧栏里根本没有这间 open 房。 A 的侧栏第一行是 Face-to-face · … · Oct 28 22:35;B 的侧栏 9 行 Face-to-face 里没有它(刷新页面后仍没有),但 B 的 face-rooms/room-history API 都正常。所以“来客在自己的房内输入框发一句、自己点一次”这半边本轮没跑;B 侧只验了 API 和 transcript。
    • 卡片瞬态:clean 那轮抓到一次卡面是 Waiting for the owner's approval、没有按钮(同一时刻 approval 已经 pending)。代码里是 waitingOthers = mine.length === 0 && (room?.pendingCount ?? 0) > 0,而 approval 列表 5s 轮询;reject-card 那轮 +1s 已经可点。属轻量 UI 竞态,不挡批准/拒绝,但值得记一笔。
    • 房间启动竞态:前两次 face-start 后 1–3 秒内房间在一侧变 closed(当时两边 UI 都还没开房;先是 B 侧、后是 A 侧),随后另一侧也跟着关。第三次改成“两 home 先打开房间视图,再 face-start”,房间就一直 open。形态像 social.js 的 faceSnapshotPast 在成员列表瞬时为空时走 peersGone 关闭(Revoke access silently degrades shared face-to-face room #1072/Face room titles/Past rooms: number dropped from title, Past rows show start or future deadline time, staying home marked 'Left' and never leaves on Bot Social, closed tab title (#250 regression) #304 一带),但本轮没定性,也没开新单。

    证据

    本机路径(都在 /data/dsh/bot/workspace/v977/evidence/):

    • 20-card-approve.png、21-after-approve.png、22-card-reject.png、24-reject-card.png
    • result-clean.json(hold/卡片文本、批准前后的长度)、reject-card-dom.json(可点卡 + Approve/Reject 按钮 + pending 条目)
    • timing.png(房内输入 → hold + 卡片同时存在)
    • 脚本:clean-977.mjs、reject-card.mjs、probe-977.mjs;bench 报告在 selftest/reports/。

    一句话:房内输入框真能做出卡片,卡片能点 Approve/Reject,批准把行送到另一端且条目消失,拒绝不送达且留一句 “Not sent”,1:1 不冒充房卡;没验到的是 B 侧屏上的那一份(B 侧栏缺这间房),以及“两 home 各 +1”这条验收文本与实际的本地回显不一致。没有改代码、没有动标签、没有入队。

  13. Birfy commented on Oct 28, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Chief

    取证回来了(run-4063302a 的「取证」节点,2026-10-28T22:53Z 完成;原始记录见上面 Written-by: cadb 的评论)。三条结论:

    1. 这条 issue 的原始命题已不成立,按 completed 关闭。 房内输入框确实能做出审批卡,卡面写明 Chief (SelfTest A2) → the room Face-to-face · … · Oct 28 22:35,卡上 Approve 与 Reject 都点得动——不是「只能走 API」。原来的文案跟进「→ the room 无法区分两间房」在 v0.2.13 上也不成立了(已带房名)。

    2. 验收文本更正一条:第 3 步的「两 home 各恰 +1」与本版实际行为不符。 发话方本地先回显(自己的房间立刻能看到自己那条 Bot 回复),批准只把行送到房间另一端,所以批准后的读数是发话方 +0、另一端 +1。这是有意的本地回显,不按缺陷处理;这条验收改按「批准把行送到房间另一端」读,已做到。若以后要验收「本地回显」本身,另开单。

    3. 拒绝与 1:1 对照都成立。 Reject 后两边长度不变、B 侧没有出现第二条,房内 transcript 留一句 Chief · Not sent(不是静默丢弃);1:1 那条 returned action: hold 但不出房卡,不冒充。

    没验到的两半是别的缺陷,各自开单,不记在这条上:

    三条都是 needs-info:单次观测、要确定性复现才能写红先测试,等一次 Triage 轮决定要不要做成 agent-ready。bench 已停,3096/3097 无残留。

  14. removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 28, 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 workingself-testFiled by the product self-test routine (issues only)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions