review(filbert): row 59 round 2 packet, rebased on 3d298920 after rows 74, 58 and 60 (#1548)

Rebase only, per rev 571. Two conflicts resolved: the agents branch in
broker.mjs next to row 58's tasks line, and the bus.css widths line.
Merged hunks in broker.mjs and the bus README are named in build.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-11 19:46:40 -05:00
co-authored by Claude Opus 5.5
parent 3d298920bd
commit 93fc00e4a9
3 changed files with 153 additions and 27 deletions
+124
View File
@@ -1,3 +1,127 @@
# Row 59 (#1548): bus read, agent state per role, round 2
Filbert, 2026-10-12. 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 in the candidate is committed,
staged or pushed.
Base 3d298920, which is row 60's landing (1771bf98) plus Sage's queue and
log commits. `build.patch` applies there with `git apply --index`, and
`candidate-manifest.sha256` pins all 8 files after the patch. Round 1 was
manifest `90d5bddc…2f71` on f2f2861b.
## Round 2: rebase only
Sage set the scope at rev 571: rebase onto row 60's landing, a digest check,
and the merged hunks in `broker.mjs` and the bus README named here. Nothing
new goes in, so the round 1 questions and both reviewers' earlier reading
still apply.
### What landed on the manifest paths
Since f2f2861b, three rows changed 7 of the 8 files. Only
`packages/bus/tests/agents.test.mjs`, which is new, is untouched.
- Row 74 (351464ce): `packages/webui/README.md`, the preview section.
- Row 58 (aaf87213): `broker.mjs` (`#taskRows` and the `tasks` branch), the
bus README, the webui README, `bus.css`, `bus.js` (the Tasks view),
`bus-browser.test.mjs` and `bus.test.mjs`.
- Row 60 (1771bf98): `broker.mjs` (the constructor option, the reader
allowlist and the `credentials` case), the bus README, the webui README and
`bus.test.mjs`.
### Digest check
Round 1's patch applies to 3d298920 with `git apply --3way`, with two
conflicts (below). I compared the round 1 and round 2 patches by their added
and removed lines. They're equal except for one line in `bus.css`. Each of
the 7 changed files has a new digest because its base changed. The digest of
`agents.test.mjs` is unchanged.
### Merged hunks
Line numbers are in the round 2 tree.
`packages/bus/src/broker.mjs`:
- The `agents` branch (lines 852–859) was a conflict. Round 1 replaced
`if (verb === 'agents') return …` with a block that maps each claim
through `#agentState` for human and reader sessions. Row 58 had replaced
the next statement, the inline `tasks` query, with
`if (verb === 'tasks') return this.#taskRows(s.business);`. Git saw the
two as one hunk. The fix keeps row 58's line as it is (line 860) and puts
round 1's block above it, unchanged.
- `#agentState` (lines 910–931) sits after the end of `#dispatch`, as in
round 1. Row 60's `credentials` case (lines 886–905) is inside the switch,
above `default:`. They share no lines.
- Row 58's `#taskRows` (line 313), and row 60's constructor option and
reader allowlist (line 495), are untouched. The allowlist already had
`agents`, so the reader route needs no change.
`packages/bus/README.md` (applied with no conflict):
- Verb table: round 1 split the `inbox`, `tasks`, `agents` row so that
`agents` has its own row (lines 128–129). Row 60's `decisions` and
`credentials` rows follow `trail` (lines 130–132). They share no lines
with round 1's edit. The table now reads `inbox, tasks`, `agents`,
`trail`, `decisions`, `credentials`.
- The "Agent state (row 59, #1548)" section sits after the table (lines
134–160), as in round 1. Row 60's `credentials` text (from line 241) and
row 58's priority and requirement text (line 279) are further down and
untouched.
The other files:
- `bus.css`, line 28, was the second conflict. Row 58 raised
`table.s1-tasks` from `min-width:900px` to 1020px, and round 1 raised
`table.s1-agents` from 820px to 1000px on the same line. The line now has
both: 1020 and 1000. This is the one changed line that differs from round 1.
- `bus.js`: the Agents view hunk (lines 331–347) applied cleanly. Row 58's
Tasks view changes end above it, at line 324.
- `webui/README.md`: the "Agent state" section (lines 174–184) sits after
the slice 1 views text. Row 74's preview section, row 58's S5 bullet and
row 60's bus-read paragraph are elsewhere in the file.
- `bus-browser.test.mjs` (lines 39–42, 68–70, 230–249) and `bus.test.mjs`
(lines 76–77) applied cleanly, with no lines shared with row 58 or row 60.
## Gate (my run on the round 2 candidate)
I ran these in sequence in a worktree at 3d298920 with the patch, with
`TMPDIR` in my scratch and `DOCKER_HOST` pointed at a missing socket, from
2026-10-12T00:39:22Z to 00:43:55Z:
| Suite | Result |
|---|---|
| packages/bus | 94/0 (88 on 3d298920; 6 new) |
| packages/control-board | 124/0 |
| packages/webui | 62/0, the browser and preview tests included (round 1 adds assertions, not tests) |
| packages/cli | 114/0, since it reads `agents` |
| scripts/test-*.sh | all nine exit 0: auth 15, conductor 17, config 24, discord 66, extension-package 18, foundation 44, queue 27, release 4, task 26 |
`test-release` and `test-task` skipped their Docker cases because no daemon
was running. Sage's gate rerun covers those. No test calls a model. The logs
are in my scratch (`r59/gate2/`), not in the repository.
## Mutants (round 2)
I reran all 29 round 1 mutants on the round 2 tree from 00:43:56Z to
00:46:05Z, with the same runner and targets. Every target was found once.
28 were killed, as in round 1. The one survivor is again `b-msg-any-role`,
which round 1 explains as equivalent. Afterwards `git diff` was unchanged
and the tree checked against the manifest.
Correction to round 1: that packet says the diff was unchanged after the
mutants. In fact the full round 1 run recorded a changed diff, and only the
rerun of the fixed mutants recorded it unchanged. The likely cause is that I
edited the tests for the four survivors during the full run, but I didn't
confirm it. This round's full run settles the point.
---
The round 1 packet follows unchanged: the description, files, round 1
mutants, round 1 gate and questions. Its base, line numbers and overlap
notes are round 1's.
# Row 59 (#1548): bus read, agent state per role, round 1 # Row 59 (#1548): bus read, agent state per role, round 1
Filbert, 2026-10-11. Brief: `docs/plans/2026-10-10_design-implementation.md`, Filbert, 2026-10-11. Brief: `docs/plans/2026-10-10_design-implementation.md`,
+22 -20
View File
@@ -1,8 +1,8 @@
diff --git a/packages/bus/README.md b/packages/bus/README.md diff --git a/packages/bus/README.md b/packages/bus/README.md
index 5b57b7fc..30ed0ab5 100644 index 82597eeedf85222d2abe8e4902321c41a85c064d..08be63070f3ad4e6367d1dcc4670a3db97ef377a 100644
--- a/packages/bus/README.md --- a/packages/bus/README.md
+++ b/packages/bus/README.md +++ b/packages/bus/README.md
@@ -125,9 +125,37 @@ an operator inspect content and delivery evidence after an uncertain result. @@ -125,11 +125,39 @@ an operator inspect content and delivery evidence after an uncertain result.
| `message.read` | `{id}`; recipient holder's acknowledgement; no new holder can acknowledge old holder's delivery. | | `message.read` | `{id}`; recipient holder's acknowledgement; no new holder can acknowledge old holder's delivery. |
| `event.emit` | `{kind:'action.allowed',body:{action:'routine',target?},subject?}`; routine observations only. | | `event.emit` | `{kind:'action.allowed',body:{action:'routine',target?},subject?}`; routine observations only. |
| `launch.revoke`, `launch.restore` | No args; outside-agent human CLI only. | | `launch.revoke`, `launch.restore` | No args; outside-agent human CLI only. |
@@ -10,6 +10,8 @@ index 5b57b7fc..30ed0ab5 100644
+| `inbox`, `tasks` | No args; plain JSON projections scoped to capability's business. | +| `inbox`, `tasks` | No args; plain JSON projections scoped to capability's business. |
+| `agents` | No args; each held role's latest claim row in the capability's business. Human and reader sessions also get `state`, `working` and `inputNeeded` (Agent state, below); a role session gets the claim rows only. | +| `agents` | No args; each held role's latest claim row in the capability's business. Human and reader sessions also get `state`, `working` and `inputNeeded` (Agent state, below); a role session gets the claim rows only. |
| `trail` | `{subject}`: task ref, decision UUID or message UUID. Linked records in broker write order. | | `trail` | `{subject}`: task ref, decision UUID or message UUID. Linked records in broker write order. |
| `decisions` | `{list}`: `roles` or `recent`; human and reader sessions only. Open decisions routed to a role, or the last 50 closed. |
| `credentials` | No args; human and reader sessions only. `[{service,account,role,dateKind,date,state}]` for the capability's business, never a token, file path or variable name. |
+### Agent state (row 59, #1548) +### Agent state (row 59, #1548)
+ +
@@ -42,10 +44,10 @@ index 5b57b7fc..30ed0ab5 100644
evidence fails, the caller receives the static `storage-unavailable` code. `human` is the reserved evidence fails, the caller receives the static `storage-unavailable` code. `human` is the reserved
recipient. Message classes are REQUEST, ASSIGNMENT, REVIEW-REQUEST, REVIEW-RESULT, recipient. Message classes are REQUEST, ASSIGNMENT, REVIEW-REQUEST, REVIEW-RESULT,
diff --git a/packages/bus/src/broker.mjs b/packages/bus/src/broker.mjs diff --git a/packages/bus/src/broker.mjs b/packages/bus/src/broker.mjs
index 84db8799..d471d600 100644 index d731988331dcfa37a0f02c6c108595f10638fd08..5e86c60c75b9b92ad5467aa27b5a6bee2ab713ea 100644
--- a/packages/bus/src/broker.mjs --- a/packages/bus/src/broker.mjs
+++ b/packages/bus/src/broker.mjs +++ b/packages/bus/src/broker.mjs
@@ -825,11 +825,14 @@ export class Broker { @@ -849,11 +849,14 @@ export class Broker {
s.human || s.reader ? 'human' : s.role, s.human || s.reader ? 'human' : s.role,
) )
.map((d) => this.#decisionView(d)); .map((d) => this.#decisionView(d));
@@ -59,10 +61,10 @@ index 84db8799..d471d600 100644
+ // A role session gets the claims only: another role's decisions aren't its to read. + // A role session gets the claims only: another role's decisions aren't its to read.
+ return s.human || s.reader ? claims.map((c) => this.#agentState(c)) : claims; + return s.human || s.reader ? claims.map((c) => this.#agentState(c)) : claims;
+ } + }
if (verb === 'tasks') if (verb === 'tasks') return this.#taskRows(s.business);
return this.#store return this.#trail(s, identifier(a.subject));
.all('SELECT * FROM task_current WHERE business=? ORDER BY task_ref', s.business) }
@@ -861,6 +864,28 @@ export class Broker { @@ -904,6 +907,28 @@ export class Broker {
fail('unknown-verb'); fail('unknown-verb');
} }
} }
@@ -93,7 +95,7 @@ index 84db8799..d471d600 100644
'INSERT INTO role_claims(at,business,role,op,holder_run,harness,address,by,decision) VALUES(?,?,?,?,?,?,?,?,?)', 'INSERT INTO role_claims(at,business,role,op,holder_run,harness,address,by,decision) VALUES(?,?,?,?,?,?,?,?,?)',
diff --git a/packages/bus/tests/agents.test.mjs b/packages/bus/tests/agents.test.mjs diff --git a/packages/bus/tests/agents.test.mjs b/packages/bus/tests/agents.test.mjs
new file mode 100644 new file mode 100644
index 00000000..23b5126b index 0000000000000000000000000000000000000000..23b5126b32bd3964c30a52fbb48b50ce9a699553
--- /dev/null --- /dev/null
+++ b/packages/bus/tests/agents.test.mjs +++ b/packages/bus/tests/agents.test.mjs
@@ -0,0 +1,159 @@ @@ -0,0 +1,159 @@
@@ -257,10 +259,10 @@ index 00000000..23b5126b
+ assert.deepEqual([there.state, there.working.messages, there.inputNeeded.length], ['working', 1, 1]); + assert.deepEqual([there.state, there.working.messages, there.inputNeeded.length], ['working', 1, 1]);
+}); +});
diff --git a/packages/webui/README.md b/packages/webui/README.md diff --git a/packages/webui/README.md b/packages/webui/README.md
index e43118d5..092b6b0e 100644 index e2ffcb3bb5b96a45d22fe7ad4c10fbc7990e07a6..c866804c61aab892a5d660436edf7b6f51a59bd8 100644
--- a/packages/webui/README.md --- a/packages/webui/README.md
+++ b/packages/webui/README.md +++ b/packages/webui/README.md
@@ -101,6 +101,17 @@ note is `agents/dewey/work/wui/SLICE1-VIEWS.md`. @@ -171,6 +171,17 @@ note is `agents/dewey/work/wui/SLICE1-VIEWS.md`.
example `human-required` from a Console started inside an agent run), "No bus to read" (no system example `human-required` from a Console started inside an agent run), "No bus to read" (no system
config, or no bus host and no `--business`) or "The read failed". config, or no bus host and no `--business`) or "The read failed".
@@ -279,23 +281,23 @@ index e43118d5..092b6b0e 100644
The Console is restyled to `docs/design` (`IMPLEMENTING.md`). It is a restyle The Console is restyled to `docs/design` (`IMPLEMENTING.md`). It is a restyle
diff --git a/packages/webui/src/public/bus.css b/packages/webui/src/public/bus.css diff --git a/packages/webui/src/public/bus.css b/packages/webui/src/public/bus.css
index 39cf2991..54d2c736 100644 index acf496a53a486ff7df8cdd33fa42ac19cf5d6d2a..fc8507e370868d3fcb9f69b428fbd744361e98ab 100644
--- a/packages/webui/src/public/bus.css --- a/packages/webui/src/public/bus.css
+++ b/packages/webui/src/public/bus.css +++ b/packages/webui/src/public/bus.css
@@ -25,7 +25,7 @@ body.on-bus .frame,body.on-bus.has-inspector .frame{grid-template-columns:240px @@ -25,7 +25,7 @@ body.on-bus .frame,body.on-bus.has-inspector .frame{grid-template-columns:240px
.s1-plain{list-style:none;padding:0;margin:0 0 10px;display:grid;gap:6px}.s1-plain li{overflow-wrap:anywhere} .s1-plain{list-style:none;padding:0;margin:0 0 10px;display:grid;gap:6px}.s1-plain li{overflow-wrap:anywhere}
.chips .chip{gap:6px}.panel{margin-bottom:16px}.panel h3{font-size:1rem;margin:14px 0 6px} .chips .chip{gap:6px}.panel{margin-bottom:16px}.panel h3{font-size:1rem;margin:14px 0 6px}
/* Tables */ /* Tables */
-table.s1-table td,table.s1-table th{overflow-wrap:anywhere}table.s1-tasks{min-width:900px}table.s1-tasks .s1-title{min-width:220px;max-width:340px}table.s1-agents{min-width:820px} -table.s1-table td,table.s1-table th{overflow-wrap:anywhere}table.s1-tasks{min-width:1020px}table.s1-tasks .s1-title{min-width:220px;max-width:340px}table.s1-agents{min-width:820px}
+table.s1-table td,table.s1-table th{overflow-wrap:anywhere}table.s1-tasks{min-width:900px}table.s1-tasks .s1-title{min-width:220px;max-width:340px}table.s1-agents{min-width:1000px} +table.s1-table td,table.s1-table th{overflow-wrap:anywhere}table.s1-tasks{min-width:1020px}table.s1-tasks .s1-title{min-width:220px;max-width:340px}table.s1-agents{min-width:1000px}
table.s1-table tbody th{font-weight:600;text-transform:none;letter-spacing:0;color:var(--text);font-size:.9rem;white-space:normal} table.s1-table tbody th{font-weight:600;text-transform:none;letter-spacing:0;color:var(--text);font-size:.9rem;white-space:normal}
.s1-done summary{list-style:none}.s1-done summary h2{display:inline}.s1-done summary::before{content:'▸ ';color:var(--muted)}.s1-done[open] summary::before{content:'▾ '}.s1-done{margin-bottom:20px} .s1-done summary{list-style:none}.s1-done summary h2{display:inline}.s1-done summary::before{content:'▸ ';color:var(--muted)}.s1-done[open] summary::before{content:'▾ '}.s1-done{margin-bottom:20px}
.s1-marker{text-decoration:none}.s1-launch{display:flex;gap:8px;align-items:center;flex-wrap:wrap} .s1-marker{text-decoration:none}.s1-launch{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
index affb9b7b..8422a87a 100644 index 3be22af5695511ce6f34948c77e49939bf5d95e9..b22bcf009cf8c93c2811e095ac84b5d83efad8df 100644
--- a/packages/webui/src/public/bus.js --- a/packages/webui/src/public/bus.js
+++ b/packages/webui/src/public/bus.js +++ b/packages/webui/src/public/bus.js
@@ -325,12 +325,23 @@ @@ -328,12 +328,23 @@
} }
// Agents // Agents
@@ -323,7 +325,7 @@ index affb9b7b..8422a87a 100644
<p class="small muted">Board seats stay on the <a href="#/">control board</a>. This view is about roles.</p>`; <p class="small muted">Board seats stay on the <a href="#/">control board</a>. This view is about roles.</p>`;
} }
diff --git a/packages/webui/tests/bus-browser.test.mjs b/packages/webui/tests/bus-browser.test.mjs diff --git a/packages/webui/tests/bus-browser.test.mjs b/packages/webui/tests/bus-browser.test.mjs
index aebd3c30..200f9428 100644 index 9faa15eb47f10a804f766f7a9d7d1bfdba6b022b..9369de62f6d2fad6a9d991b60a7f32f9917bac61 100644
--- a/packages/webui/tests/bus-browser.test.mjs --- a/packages/webui/tests/bus-browser.test.mjs
+++ b/packages/webui/tests/bus-browser.test.mjs +++ b/packages/webui/tests/bus-browser.test.mjs
@@ -36,7 +36,10 @@ function seed(root) { @@ -36,7 +36,10 @@ function seed(root) {
@@ -349,7 +351,7 @@ index aebd3c30..200f9428 100644
} }
test('S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes', { timeout: 180000 }, async () => { test('S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes', { timeout: 180000 }, async () => {
@@ -212,6 +217,26 @@ test('S5 pages in a browser: real broker rows, inert text, Copy, routes, failure @@ -222,6 +227,26 @@ test('S5 pages in a browser: real broker rows, inert text, Copy, routes, failure
await go('#/agents', 'Agents'); await go('#/agents', 'Agents');
assert.deepEqual(await b.evaluate('[...document.querySelectorAll("#bus-view table.s1-agents tbody th")].map(e=>e.textContent)').then(r => r.sort()), ['coder', 'cto', 'pm']); assert.deepEqual(await b.evaluate('[...document.querySelectorAll("#bus-view table.s1-agents tbody th")].map(e=>e.textContent)').then(r => r.sort()), ['coder', 'cto', 'pm']);
assert.match(await b.evaluate('[...document.querySelectorAll("#bus-view table.s1-agents tbody tr")].find(r=>r.querySelector("th").textContent==="cto").textContent'), /cto-runclaude-codecto-thread/); assert.match(await b.evaluate('[...document.querySelectorAll("#bus-view table.s1-agents tbody tr")].find(r=>r.querySelector("th").textContent==="cto").textContent'), /cto-runclaude-codecto-thread/);
@@ -377,10 +379,10 @@ index aebd3c30..200f9428 100644
assert.ok((await text('#bus-view')).includes(`${label}: not in the Q1 module`), label); assert.ok((await text('#bus-view')).includes(`${label}: not in the Q1 module`), label);
await noOverflow('agents'); await noOverflow('agents');
diff --git a/packages/webui/tests/bus.test.mjs b/packages/webui/tests/bus.test.mjs diff --git a/packages/webui/tests/bus.test.mjs b/packages/webui/tests/bus.test.mjs
index 2d9ca6ba..5b078168 100644 index be677a5b2ef4d08d83cb918880a0c39efd3497a5..46d8016c3a2ef855a6041ba187f3ae4c9174c304 100644
--- a/packages/webui/tests/bus.test.mjs --- a/packages/webui/tests/bus.test.mjs
+++ b/packages/webui/tests/bus.test.mjs +++ b/packages/webui/tests/bus.test.mjs
@@ -70,6 +70,8 @@ test('bus routes serve the four reads from the reader view, unescaped JSON for t @@ -73,6 +73,8 @@ test('bus routes serve the four reads from the reader view, unescaped JSON for t
const agents = await read('/api/bus/agents'); const agents = await read('/api/bus/agents');
assert.deepEqual(agents.body.rows.map(r => [r.role, r.op]), [['coder', 'claim']]); assert.deepEqual(agents.body.rows.map(r => [r.role, r.op]), [['coder', 'claim']]);
@@ -1,8 +1,8 @@
bc354048f0df93aefd8a58cae1f2421df066efce48beefc7f23fe46763fe43bd packages/bus/README.md 247c449632ec33e8840edc290f26a479274c7c45c060fd7045c352f6e83124c9 packages/bus/README.md
4701efe9d7edf5869ed011395d114b8156b99f735b841f6cab0255a635ae5bca packages/bus/src/broker.mjs cedd7d0cc09f5792529b1c28897b19c9178fba1c4d8008e7fa6aae3ccd128c7d packages/bus/src/broker.mjs
4e5c3fe7793b9a026f274f0645c440907c467c9fdc11324f167bcb9bdd1fd31b packages/bus/tests/agents.test.mjs 4e5c3fe7793b9a026f274f0645c440907c467c9fdc11324f167bcb9bdd1fd31b packages/bus/tests/agents.test.mjs
f2b91ca5ffab6ae5ad19f66bc4d11cb6cf99777f1cf18a3c44449a0998ad664f packages/webui/README.md 4e39f69de7394f89e364d782352b475e633f93b0cee543d0e862eea9b3bbbb84 packages/webui/README.md
cd0fa9e4a7fa00275aacf89eb73007baa193c6a08ea135b9bf48f596d32e418a packages/webui/src/public/bus.css 15f3523f11eb50d7a87060c18dd1fa67c688353c641605dc8d3f540acfc6927e packages/webui/src/public/bus.css
899ef3e3a27ba14100135dd92a71110556113feb2585db99648624e50400aab2 packages/webui/src/public/bus.js 9b42aef343e1a06432cab8a606c627e9101f40b6d2d274c6c6ae0e646edc60e5 packages/webui/src/public/bus.js
acefab3ff732fa255b207cd3105589ee30f3f2c94a7ba651528f0f73fe4b0e53 packages/webui/tests/bus-browser.test.mjs 7fb9f7ea1253a7a8cb8949fd6eb8854c540ad772d7a1c70b1e667d943027438a packages/webui/tests/bus-browser.test.mjs
a3d33ee916598525d5e8ea86202b96fda9416dd798990ac39ee7b786cb68ee3a packages/webui/tests/bus.test.mjs f7f19a906a273cd2bf58910e894f049bfe661932c510bd1506c08bd4194e0a46 packages/webui/tests/bus.test.mjs