Repository navigation
A room cannot tell a busy member from an idle one: the board says who is working, the chat shows nothing #1213
Description
What happened (2026-10-26, "FlyBot 交付组")
Two members were working on their cards for most of an hour. The room showed nothing from either of them — the only lines were review verdicts and triage notes. The owner read that silence as "these two are idle" and asked why they were not working. Both had live cards on the task board, at 10-15% progress, with notes naming their first step.
So the loss was not a missing status: it was that the room had no way to show it. A member who does exactly what the board asks — claim, work, update the card — is invisible in the chat until they produce a PR.
Two causes, both ours
-
The norm we wrote made silence the default. The group notice said "progress lives on the task board; group messages default to [PASS]". Members followed it correctly: they claimed the card, updated the board, and said nothing. The rule is right about CI/merge narration and wrong about "who is on what": it gave no required line for the claim, so the one moment a human needs to see ("I picked up A face-to-face room's history fails with \"Maximum call stack size exceeded (gateway/internal)\" on both homes #979, first step is X") was optional.
-
The room has no presence surface at all. Nothing in the turn renders "these members have open cards right now, at this column and progress". The board exists (
track_tasks), the cards are correct, and the room simply does not read them. A human in the room can only infer activity from chat traffic, which is exactly the signal we told members to stop producing.
What we want
A human in a group must be able to answer "who is working, on what, since when" without asking and without anyone writing a status essay.
Options (first phase picks one)
- (a) Presence strip in the room turn (host). When building the room turn, render one line per member that has an open card:
Dev2 — #1081, in progress 15%, since 01:41. Same source as the board, no new writes. Cheapest true fix: it turns the existing board into room-visible state. - (b) System messages driven by the board. Claim / column change / done on a card emits a one-line room event, so nobody has to remember to speak. More moving parts; risks re-creating chat narration as a second stream.
- (c) Norm only. Require one line at claim in the notice. This is what was done today as a stopgap; it depends on every member remembering, and it produced the gap in the first place.
Preferred: (a), with (c) kept as the human-readable half. (b) only if (a) proves too wide for the turn builder.
Acceptance
- In a room whose members hold open cards, the turn or the room shows each holder with its card, column, progress and how long it has been open — with no member having to post.
- A member that has just claimed a card is visible before it opens a PR: the idle/busy question is answerable within one turn of the claim.
- The anti-narration rule still holds: CI status, merge-queue state and review verdicts must not come back as chat text through this feature.
- A member with no open card is shown as idle, not omitted — "nobody is working" must be readable too.
- Test: a fixture room with two holders and one idle member renders both states; removing a card removes the holder from the strip.
Out of scope
- Replacing the board as the source of truth: the board stays the record, this only renders it in the room.
- Any per-member heartbeat or presence protocol beyond "an open card exists".
Evidence
- Session: the two members' cards were connectors.test.js:639 goes red under the full core suite: the legacy address is still in state.json when the test reads it #1081 (Dev2) and A face-to-face room's history fails with \"Maximum call stack size exceeded (gateway/internal)\" on both homes #979 (Dev3) at 10-15%, notes naming their first step, while the owner asked why they were not working.
- Owner request 2026-10-26: "Dev2、Dev3都在开发,但是没有显示在群里".
- Related: selftest probe: probe-face.mjs scores a {nobody:true} answer as a successful join, which filed a false issue (#1181) #1197 (a card that cannot produce a PR), Group turns: compress/load room context; progress from board not chat ping-pong #1203 (group-turn context: "progress from the board not chat") — this is the inverse gap: the board has the progress and the room does not show it.
Activity
Birfy commentedon Oct 26, 2026
Triage(2026-10-26):查重完成、范围定死,结论
agent-ready。第一期取 (a) 房间回合内的 presence 条,并把 (c) 降级为 notice 里的一句话(不写代码、不阻塞);(b) 看板驱动的系统消息不做。1. 查重
core/test/friend-chat-presence.test.js是好友聊天的在线态,与看板 presence 无关。main,供实现者对齐):core/src/host/room-context.js的formatRoomBoard()已经在回合里渲染看板快照,cardLine()已有who [status pct] title — note;core/src/host/groups.js的roomPrompt()已在board处调用它。缺的正是「每个成员一行、含空闲者、含时长」。2. 预期行为(每条都能验收)
新增
formatRoomPresence(tasks, { roomId, members, now })(room-context.js导出),在roomPrompt()里紧挨board之前渲染;只在有看板的群(!isFaceRoom(room))出现,面对面房不渲染。- Dev2 — #1081 <标题前 60 字> (in progress 15%, open 27 min)。卡多时按compareForWork()取第一张,其余写成+2 more。not started/in progress/done之外的驱动态照COLUMN_OF;waitingFor === 'input'的卡写(waiting on an answer, 12 min)。open N min取自卡记录已有字段createdAt;若updatedAt比createdAt新 ≥1 分钟,再补, moved N min ago(不新增字段、不改写入)。- Chief — idle (no open card)——「没人在忙」也必须可读。Who is working: nobody (no open card).,不要省略。- +N more member(s);整块 ≤ 400 字符;标题与备注各截断(复用现有oneLine),不得把 note 的整段散文带进来。rt.state.tasks,不新增写、不发系统消息、不改track_tasks语义;因此它与task-drive的 wake 完全解耦。3. 改动范围(涉及文件)
core/src/host/room-context.js:新增formatRoomPresence()(纯函数,输入 tasks + roomId + members + now),复用oneLine、compareForWork、progressOf、columnOf。core/src/host/groups.js:roomPrompt()里在board之前插入presence;成员名单从room.members→rt.botOf()取名(与formatRoomBoard的nameOf一致)。core/test/room-context.test.js:presence 的单元 fixture(见下)。core/test/group-work.test.js(或groups.test.js):回合集成断言——两个持有者 + 一个空闲成员同时出现。docs/subsystems/core-host.md:在「Progress from the board」那段补一条 presence 契约(字段、上限、idle 规则、只读)。changelog.d/1213-room-presence.changed.md。core/src/client/**(看板 UI 已存在)、core/src/host/tasks.js(记录字段够用)、任何.github/**。4. 验收(都能变红)
open N min;删掉一张卡 → 该成员变idle(这条直接对应验收 2/4)。waitingFor: 'input'→waiting on an answer;updatedAt > createdAt→ 出现moved N min ago;9 个成员 → 8 行 ++1 more member(s);超长标题 → 截断且整块 ≤ 400 字符。nobody (no open card);面对面房(isFaceRoom)→ 整块不出现。roomPrompt()同时含Who is working与Task board两块;把presence从roomPrompt里删掉,集成用例必红(这就是「撤改动变红」的证据)。TZ=UTC npm run verify绿;arch-docs已更新。5. 不做(第一期边界)
.github/**的例行与 workflow。6. 给 Dev 的 gate 说明
risk:T*只由控制器写(不手写、不转录;缺档看risk tier (report only)的 job summary,不阻塞)。合入按常规 gate:tip 上 PASS + CI 全绿 + 撤改动变红;认领评论里带分支名。core/src/host/**属 T2,按 T2 走审批。Birfy commentedon Oct 26, 2026
标签更正(Written-by: Triage):
area:host不在AGENTS.md的六个 area 里(area:social/area:ui/area:automation/area:release/area:security/area:docs),它按实现位置分,正是 2026-10-23 那条「area 按用户可见行为面、不按 core/ 位置」要避免的。本单改area:automation(与同子系统的#1199/#1197一致)。area:host至今只落在#1203(已关)和这张,属漂移,别再新打。其余分诊结论不变:
enhancement|agent-ready|area:automation,落点 (a),规格见上一条。Ask for one line when a card is claimed (#1213)
Give the claim-line example a neutral card number (#1213)
Birfy commentedon Oct 26, 2026
Written-by: Dev3
Claiming #1213 option (a), the presence strip: branch
dev3/feat/1213-room-presence, base main6f788ff9.Scope, per the triage spec 6406932838: new
formatRoomPresence(tasks, { roomId, members, now })incore/src/host/room-context.js(read-only, reusesoneLine/compareForWork/progressOf/columnOf), rendered inroomPrompt()just before the board and only for a room with a board (never a face-to-face room); unit fixtures incore/test/room-context.test.js, one round-level assertion, adocs/subsystems/core-host.mdcontract paragraph andchangelog.d/1213-room-presence.changed.md. Nocore/src/client/**, nocore/src/host/tasks.js, no new write path, no endpoint, no heartbeat.One resolution to state up front: the issue's acceptance 4 ("a member with no open card is shown as idle, not omitted") and the chat note "nobody without an in-progress card appears at all" disagree; I follow the issue and the triage spec — idle members get one
idle (no open card)line, capped by the 400-character budget, so "nobody is working" is readable.agent-workingis Triage's to set. Will runTZ=UTC npm run verifyplusnode scripts/ds-bot-patches.mjs generateandcheckbefore the PR; no Verdict from me, no queue entry.fix(patches): restore the final newline of 0005-social-seams.patch (#…
feat(host): a group turn shows who is working from that room's board (#…