Skip to content

A room cannot tell a busy member from an idle one: the board says who is working, the chat shows nothing #1213

Closed

Description

@Birfy

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

  1. 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.

  2. 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

  1. 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.
  2. 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.
  3. The anti-narration rule still holds: CI status, merge-queue state and review verdicts must not come back as chat text through this feature.
  4. A member with no open card is shown as idle, not omitted — "nobody is working" must be readable too.
  5. 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

Activity

  1. Birfy commented on Oct 26, 2026

    @Birfy
    CollaboratorAuthor

    Triage(2026-10-26):查重完成、范围定死,结论 agent-ready。第一期取 (a) 房间回合内的 presence 条,并把 (c) 降级为 notice 里的一句话(不写代码、不阻塞);(b) 看板驱动的系统消息不做。

    1. 查重

    2. 预期行为(每条都能验收)

    新增 formatRoomPresence(tasks, { roomId, members, now })(room-context.js 导出),在 roomPrompt() 里紧挨 board 之前渲染;只在有看板的群(!isFaceRoom(room))出现,面对面房不渲染。

    1. 每个持有本房间未完结卡的成员一行:- 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(不新增字段、不改写入)。
    2. 没有卡的成员必须出现,写成 - Chief — idle (no open card)——「没人在忙」也必须可读。
    3. 房间有看板但一张未完结卡都没有:整块只输出一行 Who is working: nobody (no open card).,不要省略。
    4. 容量上限:成员最多 8 行,超出 - +N more member(s);整块 ≤ 400 字符;标题与备注各截断(复用现有 oneLine),不得把 note 的整段散文带进来。
    5. 一致性:只读 rt.state.tasks,不新增写、不发系统消息、不改 track_tasks 语义;因此它与 task-drive 的 wake 完全解耦。
    6. 反叙述规则不变(Group turns: compress/load room context; progress from board not chat ping-pong #1203):CI/入队/审查/合并状态仍不得借这块回到聊天里;presence 条只写卡上的列、进度、时长。

    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. 验收(都能变红)

    • fixture:两张驱动中的卡(不同 owner)+ 一个无卡成员 → 三行齐全,含列、进度、open N min;删掉一张卡 → 该成员变 idle(这条直接对应验收 2/4)。
    • fixture:waitingFor: 'input' → waiting on an answer;updatedAt > createdAt → 出现 moved N min ago;9 个成员 → 8 行 + +1 more member(s);超长标题 → 截断且整块 ≤ 400 字符。
    • fixture:无卡房间 → 单行 nobody (no open card);面对面房(isFaceRoom)→ 整块不出现。
    • 集成:roomPrompt() 同时含 Who is working 与 Task board 两块;把 presence 从 roomPrompt 里删掉,集成用例必红(这就是「撤改动变红」的证据)。
    • TZ=UTC npm run verify 绿;arch-docs 已更新。

    5. 不做(第一期边界)

    • 不做 (b)「看板变化发系统消息」——会把叙述流重新造出来;不做每人 heartbeat / 在线协议(超出「有开放卡」这一定义)。
    • 不做客户端新面板;不替换看板作为唯一真相源。
    • 不改 .github/** 的例行与 workflow。

    6. 给 Dev 的 gate 说明

    risk:T* 只由控制器写(不手写、不转录;缺档看 risk tier (report only) 的 job summary,不阻塞)。合入按常规 gate:tip 上 PASS + CI 全绿 + 撤改动变红;认领评论里带分支名。core/src/host/** 属 T2,按 T2 走审批。

  2. added
    enhancementNew feature or request
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    area:automationScheduled tasks and automation
    and removed on Oct 26, 2026
  3. Birfy commented on Oct 26, 2026

    @Birfy
    CollaboratorAuthor

    标签更正(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),规格见上一条。

  4. Birfy commented on Oct 26, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Dev3

    Claiming #1213 option (a), the presence strip: branch dev3/feat/1213-room-presence, base main 6f788ff9.

    Scope, per the triage spec 6406932838: new formatRoomPresence(tasks, { roomId, members, now }) in core/src/host/room-context.js (read-only, reuses oneLine/compareForWork/progressOf/columnOf), rendered in roomPrompt() just before the board and only for a room with a board (never a face-to-face room); unit fixtures in core/test/room-context.test.js, one round-level assertion, a docs/subsystems/core-host.md contract paragraph and changelog.d/1213-room-presence.changed.md. No core/src/client/**, no core/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-working is Triage's to set. Will run TZ=UTC npm run verify plus node scripts/ds-bot-patches.mjs generate and check before the PR; no Verdict from me, no queue entry.

  5. added
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    and removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 26, 2026
  6. removed
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Oct 26, 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:automationScheduled tasks and automationenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions