duckworth: row 196 build+manifest round 1 (duckworth-0095) changes on Gander 05e940117

This commit is contained in:
jason.woltje committed 2026-10-21 15:14:10 -05:00
1 parent b9c7089576
commit 659dd4e498
2 files changed
+133

No files matched your search

+132
View File
@@ -0,0 +1,132 @@
# Row 196 build and manifest round 1 (duckworth-0095)
Reviewer Duckworth (Opus 5.5), 2026-10-21. The request is #1694 comment 30143, revs 2970/2971 (sage-2134).
Sage set the scope in sage-2137: the whole 39-path candidate against the approved DESIGN.md (r3), plus the
merge items and Gander's TESTER-PORT-AUDIT.md. BUILD-LOG.md and agents/gander/work/queue-252/ are out of scope.
My earlier row 196 reviews covered the design only (duckworth-0006, 0009, 0025), and 0093 covered the r5 hunk.
- Packet: gander-0058 on `gander/row196-r5`, record tip 93f3332b2.
- Candidate: 05e940117ff601a7a236e476f12149fe7b0a8493 on e54e05463.
- Manifest digest: d9fefc49ab1affb02d68b35b658772da4e82c724255611a7fe1d59b48b62006c, 39 paths.
## Verdict: changes
Both blockers are tests that DESIGN.md §7 requires. No product file needs to change.
### B1. T10's activation half has no test, so four mutants survive
DESIGN §5 says "The activation gate must report the same interpreter used by that bus. Missing, unsafe or
changed resolution during that activation refuses it". T10 says "Activation evidence records the same pinned
root-owned system ELF that the bus uses".
- `interpreter.test.mjs` tests `verifyInterpreterReceipt` on its own. Nothing tests the script that calls it.
- The two row196 cases in `stack-release.test.mjs` (:2283, :2310) replace the copy's `interpreter.mjs` with a
stub whose `verifyInterpreterReceipt` always returns true.
- No test reads the activation log's `interpreter` field, and no test reads the plan's `interpreter:` line.
- `git grep interpreter` over packages/cli/tests, packages/webui/tests and scripts/test-*.sh finds only those
two stub lines.
These mutants would survive the gate:
- Delete the gate's receipt block (`scripts/stack-release.sh:572-575`).
- Drop the `process.argv[4]` comparison at :574, so a bus started on another interpreter passes.
- Drop the refusal for a target with no verifier (:818), or the preflight refusal (:820).
- Drop the interpreter field from `log_line result started` (:1335).
Fix: one row196 release case with a stub `verifyInterpreterReceipt` that refuses, which must end the
activation at the gate. Add asserts that the plan prints `interpreter:` and that the started log entry
carries the same JSON. Also add a target copy without `interpreter.mjs`, which must refuse
`interpreter-unsafe` before any stop.
### B2. T7's rehearsal half has no test
T7 says "Render/plan/activate/rehearse fixture carries socket flag", and the §3 consumer table says
"Rehearsal uses a scratch socket". The MANIFEST says "The stack-release gate and rehearsal carry
--tester-socket".
- The rehearse fixture writes `script(rConsole)` as the copy's `webui/src/cli.mjs` (`stack-release.test.mjs:1784`).
That has no `MOSAIC_TESTER_SOCKET_V1` marker, so `stack-release.sh:2153-2154` never takes the socket branch.
- The rehearse tests never assert on the Console argv.
- A mutant that empties `tester_rehearsal` at :2154 survives.
Fix: give one rehearse copy the marker and assert that the stand-in Console got `--tester-socket`. Also
assert that the rehearsal's dataRoot is the scratch `$R/data`, not the live one. A legacy copy must get no flag.
The re-run is the whole `stack-release.test.mjs` file plus the mutants above, on Sage's slot. Because no
product blob changes, the bus, WebUI and scripts results carry over, unless Sage rules otherwise.
## What holds
1. Manifest integrity. 39 paths, and each sha256 matches its blob at 05e940117. The set equals
`git diff e54e05463 05e940117` minus agents/ and BUILD-LOG.md. `queue review verify-commit` printed
"matches the manifest candidate (39 paths)". 05e940117..93f3332b2 touches only agents/gander/.
2. D2, one verified principal (DESIGN §4).
- `principal.mjs` refuses unless the peer uid is the bus uid and the peer PID is proof.pid. Kinds are
exactly one, and identity comes only from businesses[b].human (owner) or proof.user (user).
- User needs MOSAIC_BUS_USER_NONCE, a parent that is this tree's `webui/src/cli.mjs` with argv[2]
`serve`, and the same parent uid. It re-reads child and parent after the walk.
- Tester keeps verifyTester's difference.
- `principal.test.mjs` covers shell, other copy, wrong verb, agent env, launched parent, a changed
parent start time, the wrong nonce kind, peer pid/uid mismatch and extra identity.
3. Request lifetime (§4, T6). `server.mjs` runs the helper only for proof kinds and pauses the socket while
it reads. It refuses `peercred-required` on helper failure and returns early if the socket finished or
died meanwhile. Only then does it bind. `boundCap` is released once: on finish, on socket close, and in
the async finalizers. A client hang-up now disconnects the cap before an async operation settles. T6
names close as a release path, so that's the design, and it fails closed.
4. Peer helper (§5). `peercred.mjs` and `peercred.py` meet every bound: 16 outstanding, 1 s, 256 bytes,
`env:{}`, `-I -S`, strict keys and safe integers, pid >= 2, gid validated only, kill and reap on close.
T2's test overlaps nine forked clients, one of them a copier, and checks bytes after helper exit.
`tester-cli.test.mjs` drives real Console-to-bus tester calls through the real helper.
5. Interpreter (§5, N5). `interpreter.mjs` matches the r3 decision. It does safety revalidation at each
start and appends a receipt with O_NOFOLLOW|O_NONBLOCK and fsync, and old receipts stay. `runtime.mjs`
inspects, records and creates the reader before `serve`.
6. D1, private tester socket (§3). `tester-socket.mjs` uses a 0700 parent, refuses an existing entry and a
path over 107 bytes, binds a temporary name, sets 0600 and checks it, rechecks, renames, then cleans up
by dev/ino.
- `serve.mjs` requires a socket for the tester and the tester for a socket. It pins Host to
`mosaic-tester.invalid` and uses TESTER_ORIGIN as the writer origin.
- A tester start failure closes the main listener (`cli.mjs:116`), as §3 requires.
- `--tester-port` now refuses with a migration error and never changes meaning.
7. Console nonce at spawn (§4). `webui/src/bus.mjs` puts MOSAIC_BUS_CLI_NONCE in the launch environment for
HUMAN_CLI, and `peercred-required` maps to 403.
8. Consumers (§3, §6). `webui-service.sh` renders `--tester-socket` only for a marked target, refuses the
retired port on a marked copy, and refuses both flags together.
- In `stack-release.sh`, inherited TCP on a new target refuses `tester-transport-migration` before any
stop. Explicit conversion drops the port and names it in the plan. Rollback to an unmarked copy drops
the socket and never adds TCP (M16).
- The path preflight and the proof-pair and SSO ordering (M28, M28b) are tested.
9. Row 164 re-pin. The two new pins are human.mjs 0c6d96ab… and webui/src/bus.mjs 934ab2f9…. Both changes
are the ones §4 names, the user nonce key and the Console's initial-spawn nonce plus status. The header
says why. `human-cli.mjs` and `transport.mjs` pins are unchanged.
10. Merge with 215.
- `server.mjs` keeps LIBRARY_VERBS in the awaited branch with `release()`.
- `runtime.mjs` adds the interpreter, receipt and reader after `reconcileGrants` and before `serve`,
with no second verifier.
- `webui/src/cli.mjs` passes `libraryFile`/`libraryStatus` to both handler sets.
- The r5 test hunk was checked in 0093.
- I judged the r2/r4 resolutions by the merged result above, not hunk by hunk.
11. TESTER-PORT-AUDIT.md. Running `git grep -l -- --tester-port` at a415de022 gives the same 15 files the
audit names. The only non-test producers left are `case2-unit.sh` and `case1-accept.sh`. Against a new
copy they fail closed, because serve refuses the retired flag. Row 253 (#1758) owns them.
12. Mutants. M1-M29 plus M28b, 30/30 killed per the gate record. Both blockers above are mutants this set
lacks.
## Notes (non-blocking)
- N1. `runtime.mjs:224` names `./user-cli.mjs`, which doesn't exist at 05e940117. `serve` gets no
`authenticateUser`, so a user envelope refuses `human-required`. DESIGN §4 line 147 puts user-cli with R3,
so this is correct. R3 must add the file and the wiring, and test both.
- N2. `testerNoSignIn` is now dead. `serve.mjs:503` refuses sign-in headers on every tester request,
which T1/M3 wants. The parameter (:475), its comment (:472) and the `cli.mjs:109` argument remain.
Remove them in a later row.
- N3. `serve()` with no `peerReader` calls `inspectInterpreter()` (`server.mjs:24`), so a cap-only caller
now needs a safe system Python. Runtime always passes one, and the change fails closed.
- N4. A rehearsal's Console socket is `<dataRoot>/rehearsals/<stamp>/data/console/tester.sock`, which is
longer than the live path that `console_for_target` preflights. A long dataRoot can pass activation
preflight and still fail the rehearsal Console start. That fails closed, so it only needs a sentence in
the rehearse docs. B2's test can cover it.
## My runs
None. I read everything with `git show`, `git diff` and `git grep` at e54e05463, a415de022, 05e940117 and
93f3332b2. My only node run was `queue review verify-commit`, at about 20:08Z, before sage-2135 (reported in
duckworth-0094).
+1
View File
@@ -1446,3 +1446,4 @@ are never rewritten or removed; corrections are new entries.
2026-10-21T11:52Z | Duckworth (Opus 5.5) | row 245 round 1 review (duckworth-0090) on Darkwing ef8b4439e | approve; N1-N3 fixed, mutants 13-16 cover N1 and N3; N1 not_root_only uid side untested; agents/duckworth/work/row245/REVIEW-r1.md |
2026-10-21T13:36Z | Duckworth (Opus 5.5) | row 249 round 1 review (duckworth-0092) on Gizmoduck 05595794b, manifest 3a969bb136bc | approve, no blockers. Webby's 248 N1 (X5 sleep 30) ruled defer to DEFERRED.md, with a cleanup condition |
2026-10-21T19:44Z | Duckworth (Opus 5.5) | row 196 r5 hunk check (duckworth-0093, mos-1198) on Gander a415de022 | hunk correct: only the activation line changes, assertions byte-identical to e54e05463, product blobs equal r4, both callback pairs exercised through the serve.mjs stub |
2026-10-21T20:14Z | Duckworth (Opus 5.5) | row 196 build+manifest round 1 (duckworth-0095), whole 39-path candidate 05e940117 vs DESIGN r3 | changes: B1 T10 activation gate/evidence untested, B2 T7 rehearsal socket untested; test-only, product holds; record held per sage-2135 |