Files
stack/agents/darkwing/work/queue-59-review/review-r1.md
T
jason.woltjeandClaude Opus 5.5 3417d21483 review(59): Darkwing round 1 packet, approve (#1548 comment 27487)
Candidate 90d5bddc on f2f2861b. bus 86/0, webui 40/0, every scripts/test-*.sh
green 23:23:30Z-23:26:25Z; 20 of 21 mutants killed, any-role-msg equivalent.
Notes: a claim that outlives its runner, a stranded delivered message (bus
follow-up to Sage), overlap with row 58.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:32:46 -05:00

6.2 KiB

Row 59, bus read: agent state per role, round 1 review (Darkwing)

Issue #1548, request comment 27371, packet agents/filbert/work/queue-59/ (commit e82f7c32). Brief: docs/plans/2026-10-10_design-implementation.md, section "Bus read: agent state per role". Base f2f2861b. Candidate manifest sha256 90d5bddc545e429091bf43202ef70cb55eeb1ce964964f440a0c753decb82f71, 8 files, matching the request.

Verdict: approve. The broker derives each held role's state from the claim, delivery and decision rows it already keeps, writes nothing and changes no schema. A role session still gets the plain claim rows. The Agents view escapes everything it prints. Nothing blocks. I have two notes on the README's limits, one bus follow-up that predates this row, and answers to both questions.

Method

  • I read the whole patch: #agentState and the agents branch in broker.mjs, the webui bus.js and bus.css changes, both READMEs and the three test files. I checked the working query against receive (which skips delivered messages, so a message can't count twice) and the inputNeeded order against the Inbox query in the same file. I checked the runner's claim in packages/harness/src/runner.mjs, lines 311 to 350: it receives a batch, takes a turn per message, sends the result and marks the message read, and its catch only exits on SESSION_GONE.
  • A detached worktree at f2f2861b with git apply --index build.patch, then sha256sum -c on the manifest: 8 OK.
  • 21 mutants of my own (r1/mutants.sh, r1/m2.sh, and r1/mut.mjs, which replaces text that must match exactly once). Each ran one test file and restored the source from the worktree index. Four of the first pass matched twice and didn't apply; m2.sh reran them with longer matches.

Node v26.8.1, TMPDIR=~/darkwing-scratch/rv59/tmp.

Suites

On the candidate tree (f2f2861b plus build.patch), one at a time. The two package runs were at 22:36Z (r1/tests.sh); the scripts ran 23:23:30Z to 23:26:25Z (r1/gate.sh, r1/gate-summary.txt). test-task ran with DOCKER_HOST=unix:///nonexistent.

Suite Result
bus node --test 86 pass, 0 fail
webui node --test 40 pass, 0 fail
test-auth 15 passed, 0 failed
test-conductor 17 passed, 0 failed
test-config 24 passed, 0 failed
test-discord 66 passed, 0 failed
test-extension-package 18 passed, 0 failed
test-foundation 44 passed, 0 failed
test-queue 27 passed, 0 failed
test-release 14 passed, 0 failed
test-task 26 passed, 0 failed

None of the 8 files changed between f2f2861b and HEAD (d7548697) at the time of review.

Mutants

20 of 21 fail (r1/mutants.txt, r1/mutants-2.txt). I checked each browser kill's assertion; each failed on the check meant for it. question-raw fails the injected-markup count, not a layout check.

Mutant Fails
any-holder: deliveries to any holder count agents 5 (new holder)
read-ignored: read deliveries still count agents 2
any-business-msg: messages from every business agents 6
any-role-msg: m.to_role dropped survives, equivalent
since-max: newest delivery as since agents 2
any-route: decisions routed anywhere agents 3
closed-kept: closed decisions still listed agents 3
seq-order: no gated-first order agents 3
any-raiser: decisions raised by any role agents 3
waiting-wins: waiting over working agents 4, 5, 6
role-gets-state: role sessions get state too agents 1
reader-plain: readers get plain rows agents 1
blocking-raw: blocking not coerced to a boolean agents 3
working-zero: working set with no messages agents 1, 3, 5, 6
endpoint-plain: no state for anyone webui endpoint test
last-ask: the last decision shown first browser
no-more-link: no "+N more" browser
no-working-detail: no message count or since browser
no-blocking-chip browser
question-raw: the question printed unescaped browser
no-state-col: no State column browser

any-role-msg is equivalent, as the packet says. bindLaunch refuses a second launch record for a run name with another role (launch-record-mismatch), so within a business a holder run belongs to one role, and its deliveries are for that role's messages.

Notes

A. A runner whose claim outlives it. The README's limits say a reply that fails leaves its message unread. There is a second case. The launcher calls endLaunch when a run's process exits, and endLaunch releases the claim, so a crashed or killed runner normally clears. If the launcher itself dies, or endLaunch is refused (the launcher logs endLaunch refused and goes on), the claim stays and the role shows working, with a since that keeps growing, or idle, until someone revokes it. One clause in the limits paragraph would cover it. Not blocking.

B. A delivered message can be stranded (predates this row). receive skips messages that already have a delivery. If a holder receives a message and releases the role without marking it read, the next holder never gets it. Row 59 shows this correctly: the new holder is idle and doesn't inherit the old holder's count, as test 5 pins. But the message sits there unanswered with nothing pointing at it. That belongs in a bus row, not this one; I'm sending it to Sage.

C. Overlap with row 58. Rows 58 and 59 both change Broker in broker.mjs and the webui bus.js: row 58 changes #taskRows and the Tasks view, and row 59 changes the agents branch, #agentState and the Agents view. The hunks don't touch each other. Whichever row lands second should rebase and rerun the browser test.

Questions

  1. Working signal. Received and not read is the right signal. The runner marks a message read only after its turn's reply, and the bus has no per-turn record, so it is the only signal available. The other option, the claim's own activity, would need a new write. Note A is where this signal gets it wrong.
  2. Attention. The first decision and a count is right. The Inbox already lists every decision in the same order, and one line per role keeps the table readable. "+N more" links to the Inbox, so the rest is one click away.