diff --git a/agents/duckworth/work/row196/REVIEW-m2.md b/agents/duckworth/work/row196/REVIEW-m2.md new file mode 100644 index 000000000..f41ad2648 --- /dev/null +++ b/agents/duckworth/work/row196/REVIEW-m2.md @@ -0,0 +1,104 @@ +# Row 196 round 2, merge-scoped (duckworth-0098) + +Reviewer Duckworth (Opus 5.5), 2026-10-21. The request is gander-0067 and sage-2151. This is a merge-scoped +round under mos-1160. I approved everything outside B1/B2 in round 1 (duckworth-0096, rev 2973, comment +30145), so this round covers my B1 and B2, the new mutants M30-M37, and the row 222 hunks in +`packages/bus/src/runtime.mjs` and `packages/webui/src/cli.mjs`. + +- Candidate: 527b9a1e6cb1e85758e94eeb5d17c542413ceeb3 on f48725cfc, branch `gander/row196-r6-prd19`. +- Record tip: ec1e7fad8. `git diff --name-only 527b9a1e6 ec1e7fad8` touches only `agents/gander/work/queue-196/`. +- MANIFEST.md sha256 7ab900a3…74ba. CANDIDATE.sha256 digest 537d30ae…149d, 39 paths. + +## Verdict: approve, with one condition + +### Scope check + +1. Manifest. I hashed every candidate blob with `git show 527b9a1e6: | sha256sum`, and all 39 match + CANDIDATE.sha256. The path set equals `git diff --name-only f48725cfc 527b9a1e6` minus agents/ and + BUILD-LOG.md. Both file digests match the values in gander-0067. +2. Product blobs against round 1. I compared each of the 39 blobs at 527b9a1e6 with 05e940117. 35 are + byte-identical, including `scripts/stack-release.sh` and every other src file. Four differ: + - `packages/bus/src/runtime.mjs` and `packages/webui/src/cli.mjs` (the 222 merge) + - `packages/cli/tests/stack-release.test.mjs` (B1, B2) + - `docs/TOOLS.md` (N4) + + So the merge scope is complete. This check is stronger than the range-diff, which I didn't need. +3. Base. `0b33f5e9d..f48725cfc` touches only the library-sync brief, the PRD and the requirements files + and tests. None of those is a manifest path. + +### The 222 merge + +4. Both files are a clean 3-way merge. `git merge-file` of r5 (05e940117), the base e54e05463 and + f48725cfc gives rc 0, and the result is byte-identical to the candidate. No hand resolution exists to review. +5. `runtime.mjs`. + - 222's `librarySync` validation, slot options, `library.sync` broker callback, start-up sync pass, + 60 s timer and `stopSync` sit around the round 1 code without touching it. + - The interpreter inspect, receipt and peer reader still come after `reconcileGrants` and before + `serve` (:233-250). The timer starts after `serve`, and `close()` stops it first, as at 0b33f5e9d. + - The timer calls the library module in process, so it opens no socket and needs no principal. That + matches REBASE.md. +6. `webui/src/cli.mjs`. `librarySync` is set only under `--send`, on the human `busCaller`, so it goes + through the fresh `MOSAIC_BUS_CLI_NONCE`, SO_PEERCRED and `verifyPrincipal` path that round 1 approved. + The tester listener gets no `librarySync`, which is right for a human write. See N5. + +### B1 (T10 activation), resolved + +7. The new test `row196 interpreter activation…` (`stack-release.test.mjs:2325`) puts a fixture + `interpreter.mjs` in C1 that is driven by faults. It cleans up the module the way the earlier row196 + cases do. + - The plan case asserts that the `interpreter:` line is the identity JSON, and that the `started` log + entry's `interpreter` field is the same string. It also asserts that the verifier got `/bus`, + the identity and a positive start time. + - `interpreter-receipt-refuse` (the verifier throws) and `interpreter-drift` (the gate's second inspect + differs) both assert exit 1, the `interpreter-receipt` gate code with `rollback: ok`, the restored + pointer, start calls present and no `ok` entry. That's the split agreed in duckworth-0097. + - `missing-verifier` and `interpreter-unsafe` assert rc 3, `interpreter-unsafe` and zero starts and + stops, for `--plan` and for a real run. +8. The mutants hit the lines I named. + - M30 deletes :572-575. + - M31 drops the `argv[4]` compare at :574. + - M32 replaces :818. + - M33 replaces the :820 refusal. + - M34 drops the field at :1335. + - M35 drops the plan line at :1326. + + All six are killed (`killers/results.json`). Each run restored the script to b916a279…, which is the + candidate blob. M31's output shows that only the drift case failed, so that case alone is what catches + the compare. + +### B2 (T7 rehearsal), resolved + +9. `rConsole` now appends `{argv, dataRoot}` from its `MOSAIC_CONFIG` to `console-context.jsonl`. The new + test `row196 rehearsal selects socket…` (:2378) runs a marked copy and a legacy copy. Each run must + produce exactly one Console start. The argv must equal `serve --port

--business acme`, plus + `--tester-socket` only when the copy is marked. The legacy copy gets neither tester flag. The dataRoot + must be `/data`, the one in the rehearsal config, not the live one. The live files must be + untouched. +10. M36 empties `tester_rehearsal` for a marked copy, and M37 gives a legacy copy the flag. Both are killed. + +### Gate + +11. The totals add up. Bus 627, WebUI 304 plus 1 skip, CLI 744, extras 230 and shells 429 make 2334/0/1. + The CLI log shows both new tests passing (ok 519, ok 520). The kill table has 38/38. M17 was re-run + because 222 changed `broker.mjs`. The other 29 targets have the same blobs, and I confirmed that in + item 2 for every manifest path. + +## Condition + +- C1. Round 1's N2 (dead `testerNoSignIn`) is deferred only in `queue-196/BUILD.md:122`. Add it to + `docs/plans/DEFERRED.md`, binding the next row that touches `webui/src/serve.mjs`. It's doc-only, so it + can go in the post-landing doc append with the BUILD-LOG lines and needs no new gate. + +## Notes (non-blocking) + +- N5. At 527b9a1e6, `startServer` (`serve.mjs:475`) destructures none of `libraryFile`, `libraryStatus` + or `librarySync`. So the three callbacks cli.mjs passes do nothing until a row adds the Console library + routes. That row has to decide whether the tester listener gets `library.sync`. My view is that it + shouldn't, because sync is a human write. +- N6. N1 and N3 are documented in BUILD.md:122, and N4 is the TOOLS.md paragraph. The TOOLS.md + paragraph is accurate. + +## My runs + +None. I read everything with `git show`, `git diff`, `git grep`, `git merge-file` (in a scratch dir under +/mnt/storage/scratch/tmp, removed) and `sha256sum` on blobs, plus `python3 -B` to read `killers/results.json`. I ran no node, and no tests or queue reads. diff --git a/docs/SESSIONS.md b/docs/SESSIONS.md index 45773e879..afad6a47b 100644 --- a/docs/SESSIONS.md +++ b/docs/SESSIONS.md @@ -1447,3 +1447,4 @@ are never rewritten or removed; corrections are new entries. 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 | +2026-10-21T20:52Z | Duckworth (Opus 5.5) | row 196 round 2 merge-scoped (duckworth-0098), candidate 527b9a1e6 (537d30ae) on f48725cfc: B1/B2 tests, M30-M37, 222 hunks in runtime.mjs + webui cli.mjs | approve, C1 N2 to DEFERRED.md in post-landing doc append; N5 library callbacks inert in serve; record waits on Sage queue-write gap |