From 11bf4cb00a256472c46839ca22633caa9150f9a5 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Tue, 13 Oct 2026 10:26:05 -0500 Subject: [PATCH] researcher: row 80 BUILD.md, Console follow-up and fixes (#1574) Filbert's R1 (comment 28188): the brief requires the packet to name what the Console would need to show the stranded list. Added as a Console follow-up section by the subagent, read-only, with unchecked items named. Also N1 (M12 note) and N2 (round 1 title). Candidate unchanged, manifest 52c41526. Sage corrected one count: M12 failed 6 of 11 tests, not 13 (the 13 was the count of x-marker lines). Co-Authored-By: Claude Opus 5.5 --- agents/researcher/work/queue-80/r1/BUILD.md | 178 ++++++++++---------- 1 file changed, 89 insertions(+), 89 deletions(-) diff --git a/agents/researcher/work/queue-80/r1/BUILD.md b/agents/researcher/work/queue-80/r1/BUILD.md index a0f324a7e..2862e5c65 100644 --- a/agents/researcher/work/queue-80/r1/BUILD.md +++ b/agents/researcher/work/queue-80/r1/BUILD.md @@ -1,12 +1,12 @@ -# Row 80 (#1574) build, round 2 +# Row 80 (#1574) build, round 1 -Status: Phase B complete on the full gate. Every suite, the nine scripts and the 15 mutants exited 0 (see "Sage's narrowed ruling and the full rerun" below). Earlier round 2 attempts stopped at cli for the missing `node_modules` and are recorded in the sections that follow. Not committed, not pushed, queue row not moved. +Status: Phase B ran three times on this packet. Run 1 failed in `bus` (119/120). Run 2 passed `bus` (120/120) and `business` (60/60) after the fix, then stopped at `cli` (exit 1) because the worktree had no `node_modules`. Run 3 was the full gate with a `node_modules` symlink in place: all 14 node suites and the nine `scripts/test-*.sh` exited 0, and `mutants.py` (15 mutants) exited 0 with all 15 killed. Not committed, not pushed, queue row not moved. ## Identity - Row 80: bus, "stranded delivery" display. Brief: `docs/plans/2026-10-11_bus-stranded-delivery.md` (not edited). -- Owner: researcher (decisions 122, 129). Reviewer: Filbert. Dispatch: Sage (lead decision 129). -- BASE: `57a2ddc5978b05b780dc0e69fb4e6c2a60ab5877` (see `base.txt`). Round 1 used `2b2a9e14eec4ff81cdada5dc42bbf1b8063b29b4`; the rebase to 57a2ddc59 changed none of the three paths. +- Owner: researcher (decisions 122, 129). Reviewer: Filbert (approved, comment 28188). Dispatch: Sage (lead decision 129). +- BASE: `57a2ddc5978b05b780dc0e69fb4e6c2a60ab5877` (see `base.txt`). Phase A used `2b2a9e14eec4ff81cdada5dc42bbf1b8063b29b4`; the rebase to 57a2ddc59 changed none of the three paths. - Worktree: `.claude/worktrees/agent-abe72e00ca2bcdf7e`, detached at BASE, changes uncommitted. No commit, no push, no queue move. - Candidate manifest: `candidate-manifest.sha256`, checked with `sha256sum -c` from the worktree root: all three paths OK. @@ -16,105 +16,105 @@ Status: Phase B complete on the full gate. Every suite, the nine scripts and the | `packages/bus/src/broker.mjs` | `f685af1c5c496f0f1907995919017b9b098f7d84d9aa1047d126f3f4cc4991ef` | | `packages/bus/tests/agents.test.mjs` | `076137bca90b6594cc0888fb0daa2c57903fff5f1035122c00dd5540de65f6eb` | -`build.patch` is `git diff` against BASE, limited to these three paths (regenerated this round). +`build.patch` is `git diff` against BASE, limited to these three paths. -## Round 2 changes +## Changes -- Round 1 Phase B failure: `bus` exit 1, 119/120. `agents.test.mjs:164` failed at L174. Cause: `node:sqlite` returns rows with a null prototype, so `assert.deepStrictEqual` against plain objects fails although the values match. -- Fix (Sage's direction, in `broker.mjs`, not the test): the stranded query at L1048 to L1052 ends with `).map((m) => ({ id: m.id, from: m.from, run: m.run, at: m.at }));` (the `.map` is at L1052). The test expectation is unchanged. -- `agents.test.mjs` file: `node --test tests/agents.test.mjs` passes 11/11 after the fix. -- Mutant M15 added (`.map` removed, expected killer `agents.test.mjs:174 prototype check`). The table now has 15 entries. -- `phase-b.sh` now stops on any nonzero step exit. A stop writes `STOP: exited `, skips the rest, runs the cwd scan and ends `PHASE-B-STOPPED`. A full pass ends `PHASE-B-DONE`. -- Stale round 1 logs in `out/` were deleted before the rerun. +- Fix (Sage's direction, in `broker.mjs`, not the test): the stranded query at L1047 to L1052 ends with `).map((m) => ({ id: m.id, from: m.from, run: m.run, at: m.at }));`. The `.map` is on L1052. Cause: `node:sqlite` returns rows with a null prototype, so `assert.deepStrictEqual` against plain objects fails although the values match. The test expectation is unchanged. +- `node --test tests/agents.test.mjs` passes 11/11 after the fix. +- Mutant M15 added (`.map` removed, expected killer `agents.test.mjs:174 prototype check`). The table has 15 entries. +- `phase-b.sh` stops on any nonzero step exit. A stop writes `STOP: exited `, skips the rest, runs the cwd scan and ends `PHASE-B-STOPPED`. A full pass ends `PHASE-B-DONE`. -## Phase B round 2 results +## Phase B runs -| Step | Exit | Tests | Notes | -| --- | --- | --- | --- | -| `bus` | 0 | 120 pass, 0 fail | Round 1 was 119/120. The L174 failure is gone. | -| `business` | 0 | 60 pass, 0 fail | | -| `cli` | 1 | 131 total: 121 pass, 8 fail, 2 cancelled | Stop. See below. | -| remaining 11 suites | not run | | Stopped | -| nine `scripts/test-*.sh` | not run | | Stopped | -| `mutants.py` (15) | not run | | Stopped | +### Run 1 -### The cli stop +- `bus` exit 1, 119/120. `agents.test.mjs:164` failed at L174 (null prototype, as above). +- Stopped on TERM (exit 143) on purpose, before the fix was applied. A `/proc` check found no leftover test process. -Cause: the worktree has no `node_modules`. The seat sandbox (`packages/seat/src/sandbox.mjs:95`, `binds`) calls `realpathSync` on `/node_modules`, which fails with ENOENT. The failing trace runs through `packages/seat/src/session.mjs:122` and `packages/cli/src/launcher.mjs:237`. Two launcher tests then time out at 30 s (`tests/launcher.test.mjs:471`, the host that died hard). The rest of the failures are the same ENOENT. +### Run 2 -Scope: the candidate touches only `packages/bus`, and no package imports another workspace package (this was checked in Phase A). The cli failure is therefore a property of the worktree layout, not of row 80. I have not proven it on BASE: I did not run cli on `57a2ddc59` in a clean tree, and I did not link `node_modules` into the worktree, because that would change the candidate tree and is a decision for Sage. +- Run after the `.map` fix. +- `bus` exit 0, 120/120. `business` exit 0, 60/60. +- `cli` exit 1: 131 total, 121 pass, 8 fail, 2 cancelled. The stop rule fired. The remaining 11 suites, the nine scripts and the mutants did not run. +- Cause: no `node_modules` in the worktree. The seat sandbox (`packages/seat/src/sandbox.mjs:95`, `binds`) calls `realpathSync` on `/node_modules`, which fails with ENOENT. The failing trace runs through `packages/seat/src/session.mjs:122` and `packages/cli/src/launcher.mjs:237`. Two launcher tests then time out at 30 s (`tests/launcher.test.mjs:471`). The rest of the failures are the same ENOENT. +- Scope: the candidate touches only `packages/bus`, and no package imports another workspace package (checked in Phase A). The cli failure is a property of the worktree layout, not of row 80. I did not run cli on `57a2ddc59` in a clean tree to prove this on BASE. +- The cwd scan reported three processes with a cwd in the worktree at the stop (pids 1353827, 1359345, 1362919). A re-check a few seconds later found none. -The cwd scan reported three processes with a cwd in the worktree when the stop fired (pids 1353827, 1359345, 1362919). A re-check a few seconds later found none. I did not identify them. They were most likely children of the failed cli run that exited on their own. +### Run 3 -## Mutants (15), not run this round +- `node_modules` symlinked from the canonical root into the worktree. `git status --porcelain` showed the three candidate paths, `?? node_modules` (expected: `.gitignore` line 2 is `node_modules/`, which matches directories only, so a symlink shows as untracked), and `?? agents/researcher/work/queue-80/`. Nothing else. Sage's ruling on this status is recorded in the coordinator message, not decided here. +- Full phase B from the top. Summary in `out/phase-b-summary.txt`, end marker `PHASE-B-DONE`. +- Node suites, all exit 0: bus (120/120), business (60/60), cli (passes with the link; count in `out/`), control-board, conversation, discord, harness, ledger, mosaic, queue, runs, seat, tasks, webui. +- Scripts, all exit 0: `test-auth`, `test-conductor`, `test-config`, `test-discord`, `test-extension-package`, `test-foundation`, `test-queue`, `test-release`, `test-task`. +- The cli pass confirms the run 2 cli failure was the missing `node_modules`. +- Two attempts did not run phase B. A symlink attempt in the earlier round was stopped by the untracked-link rule. A later chained command was refused by the worktree guard. Neither changed the candidate. -Each mutant is one exact-once replacement in `broker.mjs`. Line numbers are in the candidate. +### Cleanup after run 3 -| ID | File:line | Expected killer | -| --- | --- | --- | -| M01 | broker.mjs:47 | `agents.test.mjs:203` length 50 | -| M02 | broker.mjs:1048 | `agents.test.mjs:204` oldest-first ids | -| M03 | broker.mjs:1048 | `agents.test.mjs:203` length 50 | -| M04 | broker.mjs:1048 | `agents.test.mjs:189` current holder keeps unread | -| M05 | broker.mjs:1048 | `agents.test.mjs:174` released-run entry | -| M06 | broker.mjs:1048 | `agents.test.mjs:192` read message not listed | -| M07 | broker.mjs:1048 | `agents.test.mjs:230` per business | -| M08 | broker.mjs:1048 | `agents.test.mjs:221` per role | -| M09 | broker.mjs:1048 | `agents.test.mjs:174` entry shape (`run` renamed) | -| M10 | broker.mjs:1048 | `agents.test.mjs:174` entry shape (`from` renamed) | -| M11 | broker.mjs:1048 | `agents.test.mjs:174` entry shape (`at` dropped) | -| M12 | broker.mjs:1053 | `agents.test.mjs:58` key list | -| M13 | broker.mjs:932 | `agents.test.mjs:62` role session keys | -| M14 | broker.mjs:1048 | `agents.test.mjs:239` routed, not yet received | -| M15 | broker.mjs:1052 | `agents.test.mjs:174` prototype check | - -These expectations are reasoned, not observed. M09, M11 and M15 are the ones Sage asked about. They have not run against the fixed broker yet. - -## Gate rerun after Sage's environment ruling (2026-10-13T15:13Z) - -Sage ruled the cli stop an environment gap and asked for a symlink of the canonical root `node_modules` into the worktree, then a full rerun. Steps taken: - -1. Cwd scan run from `/`: zero processes with a cwd in the worktree. An earlier count of one was my own scan subshell, which had inherited the worktree cwd. That process was gone on the re-check. -2. `ln -s /mnt/storage/src/mosaic-stack/node_modules /node_modules`. `git status --porcelain` listed `?? node_modules`. Sage's rule for that case is stop and report, so the rerun did not start. The symlink was removed with `rm` (link only). Status is back to the three candidate paths and the packet; the manifest still verifies. `build.patch` covers the three paths only, so the link never entered it. -3. No rerun. Round 2 phase B results above stand as the last recorded run (bus 120/120, business 60/60, cli stopped at 121/131 with 8 fail and 2 cancelled, the rest not run). - -Open for Sage: whether an untracked `node_modules` link is acceptable for the gate, or whether the gate should link it outside the worktree or exclude it from `git status`. The cli failures are not yet re-run with the link in place. - -## Sage's narrowed ruling and the full rerun (2026-10-13) - -Sage ruled that `?? node_modules` is expected: `.gitignore` line 2 is `node_modules/`, and a trailing slash matches only directories, so a symlink shows as untracked. Nothing is committed from the worktree, and `build.patch` and the manifest cover only the three paths. Stopping at the first symlink check was right under the earlier rule. The rerun was ordered. - -Steps taken: -1. Symlink linked again. `git status --porcelain` showed the three candidate paths, `?? node_modules`, and `?? agents/researcher/work/queue-80/`. Nothing else. -2. First attempt did not start. A chained command was refused by the worktree guard, so `phase-b.sh` never ran. The earlier monitor read the previous round's summary. That monitor was stopped, the old out/ logs were removed, and the run was restarted with a plain command. -3. Full phase B from the top, with the symlink in place. - -Results (`out/phase-b-summary.txt`, end marker `PHASE-B-DONE`): - -| Step | Exit | -| --- | --- | -| bus, business, cli, control-board, conversation, discord, harness, ledger, mosaic, queue, runs, seat, tasks, webui | 0 each | -| `script-test-auth`, `-conductor`, `-config`, `-discord`, `-extension-package`, `-foundation`, `-queue`, `-release`, `-task` | 0 each | -| `mutants.py` (15) | 0 | - -Per-suite counts for bus, business and cli are in their logs under `out/`. The cli suite passes with the link in place, which confirms the round 2 cli failure was the missing `node_modules`. The bus suite (120/120) includes the `.map` fix. - -Mutants: baseline green, manifest clean after restore, all 15 KILLED. Two notes on the verdicts: -- M09, M10, M11 and M15 were all killed at `agents.test.mjs:174`, as expected. -- M12 is killed, but not by the expected killer. Removing the `stranded` key makes the test read `undefined`, which throws a TypeError at `agents.test.mjs:203` and `:221` before the L58 key-list assertion runs. The verdict line says "error marker in log". The kill is real. The expected-killer note in the table is wrong about the mechanism. - -Cleanup: -- `rm /node_modules` (plain rm, the link only) was run after the summary. +- `rm /node_modules` (plain rm, the link only). - `git status --porcelain` shows only the three candidate paths and the packet. - `build.patch` covers the three paths only. -- `sha256sum -c` verifies all three hashes. -- A scan from `/` finds zero processes with a cwd in the worktree. Three transient processes flagged at the end of the run belonged to my own monitor's shell and its `sleep`, and they exited when the monitor stopped. +- `sha256sum -c` verifies all three candidate hashes. +- A scan from `/` finds zero processes with a cwd in the worktree. + +## Mutants (15), phase B run 3 + +Each mutant is one exact-once replacement in `broker.mjs`. The "Observed" column lists the `agents.test.mjs` line numbers that appear in the stack frames of `out/M.log`. The expected killer line appears in every log. + +| ID | Site | Expected killer | Observed assertion frames | +| --- | --- | --- | --- | +| M01 | broker.mjs:47 (`STRANDED` cap) | `:203` length 50 | 203 | +| M02 | broker.mjs:1048 (sort order) | `:204` oldest-first ids | 204 | +| M03 | broker.mjs:1048 (cap) | `:203` length 50 | 203 | +| M04 | broker.mjs:1048 (current holder) | `:189` current holder keeps unread | 189 | +| M05 | broker.mjs:1048 (released run) | `:174` released-run entry | 174, 189, 203, 221 | +| M06 | broker.mjs:1048 (read filter) | `:192` read message not listed | 192 | +| M07 | broker.mjs:1048 (business scope) | `:230` per business | 230 | +| M08 | broker.mjs:1048 (role scope) | `:221` per role | 221 | +| M09 | broker.mjs:1048 (`run` renamed) | `:174` entry shape | 174, 205 | +| M10 | broker.mjs:1048 (`from` renamed) | `:174` entry shape | 174, 205 | +| M11 | broker.mjs:1048 (`at` dropped) | `:174` entry shape | 174 | +| M12 | broker.mjs:1053 (`stranded` key removed) | `:58` key list | 58, 174, 189, 203, 221, 239 | +| M13 | broker.mjs:932 (role session rows) | `:62` role session keys | 62 | +| M14 | broker.mjs:1048 (routed, not received) | `:239` routed, not yet received | 239 | +| M15 | broker.mjs:1052 (`.map` removed) | `:174` prototype check | 174 | + +Verdicts: baseline green, manifest clean after restore, all 15 KILLED. + +M12 note: the L58 key-list assertion does catch M12, at `agents.test.mjs:58:32`. The run also shows TypeErrors at `:203` and `:221`, because the test reads `stranded` as `undefined` in those assertions. The first failing test header in that log is `:49`, with 6 of 11 tests failing (`ℹ fail 6`; the 13 `✖` lines are each failure twice plus the "failing tests:" header). The kill is real and the expected killer is correct. The mechanism is partly TypeErrors, not only the L58 check. + +## Console follow-up + +Scope: read-only research of `packages/webui` and `packages/cli`. No code changed. Line numbers are from the current worktree. + +Not checked, listed once here: a live read of `/api/bus/agents` (no run was made); the `bus-browser` test run; the contents of the "Not shown here yet" gap list at the end of `agentsView`; whether `packages/webui` references `stranded` anywhere (I did not grep for it, and the render functions below do not use it); whether `#/messages/` exists as a detail route (grep found only list pages); the text of decision 123 in the repo; which session type (`human` or `reader`) the Console's `humanCall` opens; and whether the human-cli `agents` verb passes broker rows through unchanged. + +### (a) Which read carries `stranded` to the Console + +- Route: `packages/webui/src/serve.mjs:52` (`BUS_VERBS` includes `agents`); `busRead` at L54; the GET-only `/api/bus/` route at L223-227 returns 404 for unknown verbs. +- Transport: `packages/webui/src/bus.mjs:27` `humanCall` runs the human transport `packages/bus/src/human-cli.mjs` (header L1-2; lead decision 70). +- Broker: `packages/bus/src/broker.mjs:932` returns `#agentState(c)` rows when `s.human || s.reader`, and claim rows otherwise. `#agentState` at L1053 returns `{ ...c, state, working, inputNeeded, stranded }`. The stranded query is at L1047-1052. +- Result: for a human or reader session, the broker already puts `stranded` on the `agents` read. No read-path change is needed for the Console to receive it, subject to the unchecked items above. `bus.test.mjs:77` already asserts that `inputNeeded` comes through the reader view. `stranded` sits on the same object, so the same path applies, but no test covers it yet. +- CLI: `packages/cli/src/cli.mjs:188` prints `agents` through `formatAgents`. `packages/cli/src/format.mjs:52-55` prints role, holder run, harness and `since`. `packages/cli/src/app.mjs:433` prints the detail lines (held by, harness, since). None of these read `inputNeeded` or `stranded` (grep of `packages/cli/src` for both names: no matches). A cli change is optional and not needed for the Console. + +### (b) Where a list or count would render, and what `inputNeeded` does + +- Render path: `packages/webui/src/public/bus.js`. `fetchRead(verb, subject)` at L34, and the `get` helper at L59-62. +- `attention(c)` at L421-428 is the existing `inputNeeded` renderer. It links the first ask to `#/inbox/`, shows a blocking chip, and shows a "+N" link to `#/inbox`. +- `heldTable(claims)` at L435 builds the table. `paintHeld(err)` at L440-460 draws the Board held count, the refusal banner and the table. `agentsView(get)` at L462-470 renders the Held roles table with columns Role, State, Attention, Holder, Harness and Held since. +- The Board refresh at L706-717 reads inbox, agents and messages, then calls `paintHeld`. +- Natural fit for a follow-up: a stranded count in the Attention cell, built the same way as `attention(c)`, with a link per message. The link target would be `#/messages/`, which is not confirmed to exist (see unchecked list). A separate "Stranded" column is the alternative. + +### (c) What the follow-up row would test, and `public/` + +- Existing test patterns to extend: `packages/webui/tests/bus.test.mjs:77` (reader-view `inputNeeded` assertion) and `packages/webui/tests/bus-browser.test.mjs:248-259` (browser check of the Agents table: headers, state mark, "Input needed:" text, `#/inbox/` link). +- A follow-up would need, at minimum: stranded entries present in the reader rows; rendered count or links; the link target; no change to the read when nothing is stranded; and a check that listing writes nothing (no delivery row changes). +- Public path: any render change touches `packages/webui/src/public/bus.js`, and any stylesheet under `public/` would too. Per decision 123 that needs Mos's go. The read-path files (`serve.mjs`, `bus.mjs`) and the cli files are not under `public/`. ## Open items 1. Not committed, not pushed, queue row not moved. -2. Sage to review M12's killer note (the mechanism differs from the table). The kill itself is sound. - -## Round 1 (superseded) - -Phase A packet, base `2b2a9e14`, 14 mutants. Its manifest sha256 was `4c91e42d71ff9a95e336db57f6c5821f3ef94f6e3dbf07ef860d71145d664c8a`, and its broker hash `cc949cea…` is superseded. Phase B round 1 failed at bus (119/120) as described above. +2. Sage to review the corrected M12 note above. +3. Console follow-up is research only. It needs a separate queue row, and Mos's go for any `public/` change. +4. Phase A (superseded): base `2b2a9e14`, 14 mutants, manifest sha256 `4c91e42d71ff9a95e336db57f6c5821f3ef94f6e3dbf07ef860d71145d664c8a`, broker hash `cc949cea…`. Superseded by the broker hash above.