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]>
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:
#agentStateand theagentsbranch inbroker.mjs, the webuibus.jsandbus.csschanges, both READMEs and the three test files. I checked the working query againstreceive(which skips delivered messages, so a message can't count twice) and theinputNeededorder against the Inbox query in the same file. I checked the runner's claim inpackages/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 onSESSION_GONE. - A detached worktree at
f2f2861bwithgit apply --index build.patch, thensha256sum -con the manifest: 8 OK. - 21 mutants of my own (
r1/mutants.sh,r1/m2.sh, andr1/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.shreran 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
- 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.
- 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.