review(filbert): row 60 round 2 packet, rebased on bbd22f1b, decisions row and Dewey's N1 (#1549)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-11 19:12:07 -05:00
co-authored by Claude Opus 5.5
parent bd4dff33ed
commit 5fc7635cd8
3 changed files with 148 additions and 29 deletions
+123 -9
View File
@@ -1,4 +1,118 @@
# Row 60 (#1549): bus read, credential status on the broker, round 1
# Row 60 (#1549): bus read, credential status on the broker, round 2
Filbert, 2026-10-12. Brief: `docs/plans/2026-10-10_design-implementation.md`,
"Bus read: credential status on the broker" (blob a360554c). Darkwing and
Dewey review. Sage commits after the gate; nothing is committed, staged or
pushed yet.
Base bbd22f1b. `build.patch` applies there with `git apply --index`;
`candidate-manifest.sha256` pins all 9 files after the patch. Round 1 was
manifest `99d67e44…eedc` on 4100fa68, approved by Dewey (27431) and
Darkwing (27531). HEAD is now bd4dff33; none of the 9 paths changed since
bbd22f1b. I applied the patch to a clean worktree of bbd22f1b and all 9
manifest lines check OK.
## Round 2 changes
Sage scoped the round at rev 569: the digest check, the `serve.mjs` merge
with row 74's preview checks, the missing `decisions` row (question 2), and
Dewey's N1.
### Rebase and digest check
Since 4100fa68, row 74 (351464ce) changed `packages/webui/src/serve.mjs` and
`packages/webui/README.md`, and row 58 (aaf87213) changed
`packages/bus/src/broker.mjs`, `packages/bus/README.md`,
`packages/webui/README.md` and `packages/webui/tests/bus.test.mjs`. Round 1's
patch applies to bbd22f1b with `git apply --3way` and no conflict, and no
round 1 hunk shares lines with a row 58 or row 74 hunk:
- `serve.mjs`: the only change is still the `BUS_VERBS` line and its
comment (line 51). Row 74's preview checks are in the request handler
(lines 114–135) and are untouched. The bus route lookup at line 160 runs
after them, so in preview mode `/api/bus/credentials` gets the same Host,
Origin, sign-in and read-only checks as the other bus reads.
- `broker.mjs`: the `credentials` case sits before `default:`, after row
58's `tasks` change; the constructor option and the reader allowlist are
in places row 58 doesn't touch.
- `webui/tests/bus.test.mjs`: the round 1 tests follow the `decisions` route
test; row 58's assertion is in the first route test.
- `webui/README.md`: the bus-read paragraph is in "Data and boundaries",
below row 74's preview section and row 58's S5 bullet.
Compared by added and removed lines, round 2's patch equals round 1's
except for the two `packages/bus/README.md` edits below. Against round 1's
manifest, 4 digests are unchanged (`runtime.mjs`, `views.mjs`,
`credential-status.test.mjs`, `webui/src/bus.mjs`; no row changed them).
5 are new: `broker.mjs`, `webui/tests/bus.test.mjs` and `webui/README.md`
because row 58 changed their base, `serve.mjs` and `webui/README.md`
because row 74 did, and `packages/bus/README.md` because of row 58 and the
two edits.
### Question 2: the `decisions` row
`packages/bus/README.md` verb table gains a `decisions` row after `trail`:
`{list}`, `roles` or `recent`, human and reader sessions only. It's taken
from row 57's paragraph and the broker's `decisions` case (`keys(a,
['list'], ['list'])`, `RECENT` 50).
### Dewey's N1
Round 1 said the two views differ "since a token file rotated after startup
isn't reread until restart". Dewey is right that this is wrong: the dates
come from the business file, which the host reads at startup (the broker
keeps a `structuredClone` of it, and `Credentials` keeps the reference
dates). The sentence now says the host takes the dates and accounts from the
business file at startup, only the state moves with the clock, the two
differ only when the business file changes after startup (a date, an
account, or a credential added or removed), and rotating a token file
without editing its date changes neither.
### Not changed
- Question 1: the Business view stays as it is, as both reviewers advised.
- Dewey's N2 (the preview serves this read from the edge, states and dates
only): row 74's preview section already says the GET routes, the bus
views included, are unchanged. I didn't edit that section. Sage has N2.
- Darkwing's nit on the round 1 gate table (`release 4` should be 14): 4 is
what my run printed. Without a Docker daemon, `test-release` passes 4 and
skips its state-machine cases; Dewey's run with Docker passed 14. The
table below says which cases were skipped.
## Gate (my run on the round 2 candidate)
Worktree at bbd22f1b with the patch, run in sequence with `TMPDIR` in my
scratch and `DOCKER_HOST` pointed at a missing socket, 2026-10-12T00:06:32Z
to 00:10:01Z:
| Suite | Result |
|---|---|
| packages/bus | 88/0 (84 on bbd22f1b; 4 new) |
| packages/control-board | 124/0 |
| packages/webui | 62/0 (61 on bbd22f1b; 1 new), the browser and preview tests included |
| packages/cli | 113/0, since it starts the runtime |
| 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 without a daemon
(`test-release` passes 14 with one). Sage's gate rerun covers those. No test
calls a model. `test-discord` passed on the first run this time. Logs are in
my scratch (`r60/gate2/`), not kept in the repository.
## Mutants (round 2)
All 16 round 1 mutants (`run.mjs`, kept in my scratch) were rerun on the
rebased tree, 2026-10-12T00:10:13Z to 00:11:18Z. The runner is unchanged:
each mutant edits one file, runs the tests that own it, and restores the
file. All 16 are killed, and the worktree diff is unchanged afterwards. The
list is round 1's (below). The README edits are docs only and have no
mutants.
---
The round 1 packet follows unchanged, for the description, files, round 1
gate and questions. Its base, line numbers and overlap notes are round 1's.
## Round 1 packet
Filbert, 2026-10-11. Brief: `docs/plans/2026-10-10_design-implementation.md`,
"Bus read: credential status on the broker" (blob a360554c). Darkwing and
@@ -10,7 +124,7 @@ Base 4100fa68. `build.patch` applies there with `git apply --index`;
c949f9ef, with docs and queue commits only; no file under `packages/`
changed. I applied the patch there and all 9 manifest lines check OK.
## What the read is
### What the read is
A new socket verb, `credentials`. No args. Human and reader sessions only; a
role session gets `human-required`, as with `decisions`. It returns one row
@@ -38,7 +152,7 @@ the host should load. This read asks the host: what it did load, checked
against its own clock. A token file rotated after startup isn't reread until
restart, and this read is how to see what the host is running with.
## How the broker gets it
### How the broker gets it
The broker gets one new constructor option, `credentialStatus`, a function.
The runtime passes `() => credentials.status()`. The broker never gets the
@@ -53,7 +167,7 @@ also goes through the known-token check every read goes through.
The existing trusted launch op `credentialStatus(business, role)` in the
runtime is unchanged.
## WebUI
### WebUI
GET `/api/bus/credentials` is a new bus route: `credentials` joins
`BUS_VERBS` in `serve.mjs`, and `views.mjs` gains `credentials()`. It runs
@@ -63,7 +177,7 @@ through the same human transport as the other bus reads, so the same 403,
No page uses it yet. The brief names the endpoint and its tests, not a view
change. The Business view still shows the file's metadata. See question 1.
## Files
### Files
- `packages/bus/src/broker.mjs`: the `credentialStatus` option, the
`credentials` case, and `credentials` in the reader allowlist.
@@ -95,7 +209,7 @@ change. The Business view still shows the file's metadata. See question 1.
- `packages/webui/README.md`: the route, and the exposure note now names
the credential status.
## Mutants
### Mutants
16 mutants (`run.mjs`, kept in my scratch). Each edits one file, runs the
tests that own it, and restores the file:
@@ -114,7 +228,7 @@ All 16 are killed, and the diff is unchanged afterwards:
- views, 2: the wrong verb, an extra argument.
- serve, 1: `credentials` missing from `BUS_VERBS`.
## Gate (my run on the candidate)
### Gate (my run on the candidate)
Worktree at 4100fa68 with the patch, run in sequence with `TMPDIR` in my
scratch and `DOCKER_HOST` pointed at a missing socket:
@@ -140,7 +254,7 @@ scratch (`r60/gate/`), not kept in the repository.
The mutants ran before one comment-only change: I rewrapped the header
comment of `packages/webui/src/bus.mjs`. The gate ran after it.
## Overlap
### Overlap
- Row 59 (#1548, in review) edits the `agents` branch of `broker.mjs`, adds
`#agentState` after `#dispatch`, and replaces the `inbox, tasks, agents`
@@ -154,7 +268,7 @@ comment of `packages/webui/src/bus.mjs`. The gate ran after it.
- Row 58 (Darkwing) edits the `tasks` branch, which this patch doesn't
touch.
## Questions for reviewers
### Questions for reviewers
1. Should the Business view use this read, or show it next to the file's
metadata (for example, flag a difference)? I left the view alone because
+20 -15
View File
@@ -1,16 +1,17 @@
diff --git a/packages/bus/README.md b/packages/bus/README.md
index 5b57b7fc..a9517a1d 100644
index 7fa51f92..82597eee 100644
--- a/packages/bus/README.md
+++ b/packages/bus/README.md
@@ -127,6 +127,7 @@ an operator inspect content and delivery evidence after an uncertain result.
@@ -127,6 +127,8 @@ an operator inspect content and delivery evidence after an uncertain result.
| `launch.revoke`, `launch.restore` | No args; outside-agent human CLI only. |
| `inbox`, `tasks`, `agents` | No args; plain JSON projections scoped to capability's business. |
| `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. |
Unknown keys, roles, references, actions and verbs refuse. If writing refusal
evidence fails, the caller receives the static `storage-unavailable` code. `human` is the reserved
@@ -196,7 +197,7 @@ must not invoke the human CLI or work around its proof.
@@ -196,7 +198,7 @@ must not invoke the human CLI or work around its proof.
Human resolutions and launch toggles append `human.input`. Agent capabilities
and read-only WebUI capabilities cannot reach these operations. Reader
@@ -19,7 +20,7 @@ index 5b57b7fc..a9517a1d 100644
business; they do not confer mutation or resolution authority.
`decisions` (row 57, #1546) is for human and reader sessions only; a role
@@ -208,6 +209,26 @@ each with `closed`, the closing `decision_events` row. Every row is the
@@ -208,6 +210,30 @@ each with `closed`, the closing `decision_events` row. Every row is the
decision as `trail` gives the same session for its id, so the lists add no
data a reader couldn't already read one id at a time; they add the listing.
@@ -40,14 +41,18 @@ index 5b57b7fc..a9517a1d 100644
+`credentials.use`, and the result passes the known-token check like every
+other read. The rows match the Business view's credentials in the WebUI
+(`packages/webui/src/reads.mjs`), which reads the business file instead: the
+file says what the host should load, this read says what it did load, since
+a token file rotated after startup isn't reread until restart.
+file says what the host should load, this read says what it did load. The
+host takes the dates and accounts from the business file at startup, and
+only the state moves with the clock. So the two differ only when the
+business file changes after startup: a date, an account, or a credential
+added or removed. Rotating a token file without editing its date changes
+neither.
+
## Shared reads and adapter boundary
```js
diff --git a/packages/bus/src/broker.mjs b/packages/bus/src/broker.mjs
index 84db8799..18c9bc0d 100644
index 595ca232..d7319883 100644
--- a/packages/bus/src/broker.mjs
+++ b/packages/bus/src/broker.mjs
@@ -86,12 +86,15 @@ export class Broker {
@@ -67,7 +72,7 @@ index 84db8799..18c9bc0d 100644
for (const table of [
'events',
'role_claims',
@@ -468,7 +471,7 @@ export class Broker {
@@ -489,7 +492,7 @@ export class Broker {
if (!object(args)) fail('invalid-request');
this.#secretCheck(args);
return this.#store.transaction(() => {
@@ -76,7 +81,7 @@ index 84db8799..18c9bc0d 100644
fail('read-only');
const result = this.#dispatch(s, request.verb, args);
this.#secretCheck(result);
@@ -857,6 +860,28 @@ export class Broker {
@@ -875,6 +878,28 @@ export class Broker {
.map((d) => ({ ...this.#decisionView(d), closed: this.#closed(d.id) }));
fail('invalid-request');
}
@@ -353,10 +358,10 @@ index 00000000..f3f20142
+ assert.deepEqual(clean(stored, words), []);
+});
diff --git a/packages/webui/README.md b/packages/webui/README.md
index e43118d5..fedb9195 100644
index bb7fc399..e2ffcb3b 100644
--- a/packages/webui/README.md
+++ b/packages/webui/README.md
@@ -194,19 +194,23 @@ session reader or transport is implemented here. The session reader is
@@ -264,19 +264,23 @@ session reader or transport is implemented here. The session reader is
`packages/conversation`, served by the board.
GET `/api/bus/inbox`, `/api/bus/tasks`, `/api/bus/agents`,
@@ -404,7 +409,7 @@ index 8fee21a6..ca1bab89 100644
import { spawn } from 'node:child_process';
import { HUMAN_CLI } from '../../cli/src/transport.mjs';
diff --git a/packages/webui/src/serve.mjs b/packages/webui/src/serve.mjs
index 4d51942a..c0f10275 100644
index a1861d5d..89327c07 100644
--- a/packages/webui/src/serve.mjs
+++ b/packages/webui/src/serve.mjs
@@ -46,8 +46,9 @@ async function body(req) {
@@ -420,10 +425,10 @@ index 4d51942a..c0f10275 100644
async function busRead(res, bus, verb, search) {
let arg;
diff --git a/packages/webui/tests/bus.test.mjs b/packages/webui/tests/bus.test.mjs
index 2d9ca6ba..2b429352 100644
index cf477052..be677a5b 100644
--- a/packages/webui/tests/bus.test.mjs
+++ b/packages/webui/tests/bus.test.mjs
@@ -123,15 +123,38 @@ test('decisions route serves the roles and recent lists from the reader view', a
@@ -126,15 +126,38 @@ test('decisions route serves the roles and recent lists from the reader view', a
assert.equal(calls.length, before);
});
@@ -463,7 +468,7 @@ index 2d9ca6ba..2b429352 100644
}
for (const path of ['/api/bus/decide', '/api/bus/seen', '/api/bus/', '/api/bus/inbox/x', '/api/bus/request', '/api/bus/raise']) {
assert.equal((await fetch(base + path)).status, 404, path);
@@ -216,6 +239,7 @@ test('busReader takes --business, else the live bus host, else refuses', async t
@@ -219,6 +242,7 @@ test('busReader takes --business, else the live bus host, else refuses', async t
const pinned = busReader({ dataRoot, socket: 'sock', business: 'pinned', cli });
assert.equal((await pinned.inbox()).request.business, 'pinned');
assert.deepEqual((await pinned.decisions('recent')).request, { business: 'pinned', verb: 'decisions', args: { list: 'recent' } });
@@ -1,9 +1,9 @@
ac3905b4d151953caa8dee651bed69e83cc3912859a5619443ec1c6b2af05523 packages/bus/README.md
41d7288c9d3e96f5a380eec280b0703d95daf379f6668f5f39662041e914be1e packages/bus/src/broker.mjs
9181c5591db00c37e9c9dd3a2c3864f7b892537388b0ddd3b168fc6806edef07 packages/bus/README.md
7b15a43645bc008a62fa460e578af5b8d0ccdf6b2b6d163f398a3c6bf55d64d1 packages/bus/src/broker.mjs
869d8f3b795e7f1b43c44272958e66c1acc1e8fb721476882eaa9bdcac15ab55 packages/bus/src/runtime.mjs
61a009467c232b56265b6a9ae5b26f0b5858b605b1087dd3561586da538ef221 packages/bus/src/views.mjs
689cd3f0a704a7c1eba323dbcbb3abcb5689f672da6da79d2d7e605e261e4d89 packages/bus/tests/credential-status.test.mjs
0bb2f0cb524eaf3db2d36d94c4b7e8e8ea9416a79840494a8ab6d54f47d4b0fc packages/webui/README.md
41256e4dd972995147a2f0ed835b250e03b7165e4ebdfbf6f51cfd98a705752f packages/webui/README.md
3fbf7a40058541759e44ff531de91f8fcecb0a0b2a9fef12e71cdc6aa1d45feb packages/webui/src/bus.mjs
c676c1d7ea9eacd6c26825d9708c0fb2b1e0ff3d2a67531c44ecc13a9c07d5ff packages/webui/src/serve.mjs
f2aff38eb038b576403d0333f8c40a1e83fdc46a66fd33086840e133965559b0 packages/webui/tests/bus.test.mjs
cacb239d76d164415d9782cdf23d6d3dcf1da21a21d9a9a8220f2e7297bce478 packages/webui/src/serve.mjs
7993864f4d9f2b4a3b81026652adf6a0bf4c1341a4c6e39caaeb010b3631ace6 packages/webui/tests/bus.test.mjs