# Row 59 (#1548): bus read, agent state per role, round 1 Filbert, 2026-10-11. Brief: `docs/plans/2026-10-10_design-implementation.md`, "Bus read: agent state per role" (blob a360554c). Darkwing and Dewey review. Sage commits after the gate; nothing is committed, staged or pushed yet. Base f2f2861b. `build.patch` applies there with `git apply --index`; `candidate-manifest.sha256` pins all 8 files after the patch. ## Which: state on the bus The brief allows either a seat-to-role mapping read from committed data or state on the bus. I chose state on the bus, because no committed data joins a seat to a role: - The business file's `holder` for a role instance is a display name. It isn't a seat id the control board knows. - The bus's claim names a run (`holder_run`), not a seat. A run is one launch; the next launch of the same role is a different run. So a mapping would be a new hand-kept file that goes stale on each launch. The bus already records everything the state needs: the claim, message deliveries and decisions. The state is derived at read time. Nothing new is written and the schema is unchanged. ## The derivation `agents` for a human or reader session returns each held claim row plus: - `working`: `{messages, since}` or null. Messages to the role, in the business, that the current holder received (`delivered`) and hasn't marked `read`; `since` is the oldest delivery. The runner (`packages/harness/src/runner.mjs`) receives a batch, takes one turn per message, replies, then marks each read, so this is "a turn is under way or queued". - `inputNeeded`: the role's open decisions routed to the human (`raised_by_role` is the role, `route_to` is `human`, no resolved, withdrawn or expired event), gated first and then oldest, the Inbox's order. Each item is `{id, question, class, blocking, task_ref, at}`; options and the rest of the decision stay in the Inbox. Seen is still open. - `state`: `working` if `working` is set, else `waiting` if `inputNeeded` is nonempty, else `idle`. Working wins, as on the control board (`scan.mjs`). A role session gets the claim rows unchanged, so a role can't read another role's decisions through `agents`. The CLI's `formatAgents` prints only role, holder, harness and time, so `mosaic agents` is unchanged; `--json` carries the new fields. The webui server passes `agents` rows through, so it has no change. Limits, in the bus README: - A message routed but not yet received shows idle. - A reply that fails for a reason other than a gone session leaves its message unread, so the role stays working. `since` shows for how long. - No error or offline state is derived. The control board has both; the bus has no record to derive them from. - A new holder doesn't inherit the last holder's unread deliveries. Decisions stay with the role across holders. - Unheld and released roles aren't listed, as before. ## The Agents view `#/agents` gets State and Attention columns, between Role and Holder: - State: the board's mark (`.st-*` from `shell.css`: ● working, ◐ waiting, – idle) with the word, plus "N messages since