Files
stack/agents/researcher/work/queue-88/r1/BUILD.md
T
jason.woltjeandClaude Opus 5.5 f0bab46e9a researcher: row 88 round 1 packet, Haiku subagent build on 8e0fbca4 (#1582)
Five paths: tasks fields.mjs and fields.test.mjs, webui README, public/bus.js
(aria-label only) and bus-browser.test.mjs. Phase B: 14 node suites and 9
scripts green, 8 of 8 mutants killed. The literal U+202E is now an escape.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 11:16:12 -05:00

13 KiB
Raw Blame History

Row 88 (#1582) build, r1

Phase B complete on the rebased candidate. Base: 8e0fbca420 (refactor, #1588 plus row 80 and later). Result: every package suite and every test script passed with zero failures, and all eight mutants were KILLED. Nothing committed, pushed, merged or moved in the queue.

Correction (2026-10-13, after the first phase B report): the first version of this file said "zero skips" for every suite. That was wrong for three scripts. The phase B run had DOCKER_HOST=unix:///nonexistent.sock exported, so docker-dependent cases skipped: test-release (4 passed, state-machine cases skipped), test-task (docker cases skipped), and test-queue (queue verify and render --check skipped, because this worktree is not the queue's canonical root). The package suites had zero skips. See "Script skips" below.

Revision (the fix Sage ruled on, same base): line 99 of bus-browser.test.mjs now uses the \u202e escape instead of a literal U+202E. The runtime string is unchanged. The webui suite was rerun (108/108, 0 skipped), and mutants.py was rerun (all eight KILLED). The manifest was regenerated; see "Revision" below for the new sha256.

Owner: researcher. Reviewer: Filbert. Brief: docs/plans/2026-10-12_row61-followup.md (read only, not edited). Ruling: the packet is at agents/researcher/work/queue-88/r1/, not agents/darkwing/work/queue-/ as the brief names it.

Base and rebase

BASE was 2b2a9e14ee (origin/refactor, #1588). Sage's slot-clear message moved the candidate to 8e0fbca420 (decision 127). Since 2b2a9e14, row 97 changed packages/webui/README.md (the Board-test paragraph, near line 567). Rebase steps, in order:

  1. The five tracked paths were stashed by tag ("row88-r1-rebase-20261013"), not with a bare stash. The entry's SHA was captured (bf6831953e6e04cbd8b5ef4cd1236a3575aa90e3) before the detach. Other sessions' stash entries were left in place.
  2. git checkout --detach 8e0fbca420a3161ce98c5e0f3910f500daabebba.
  3. git stash apply <sha>, not pop. README auto-merged with no conflict. The entry was then dropped by its SHA.
  4. Checked: git diff --stat against the new base shows only the five paths (93 insertions, 18 deletions). The README diff against the new base is my one-phrase hunk, so row 97's paragraph is intact in HEAD. No conflict markers.

base.txt holds 8e0fbca420. build.patch was regenerated with git diff --binary 8e0fbca... -- packages and covers the five paths only.

node_modules: linked to /mnt/storage/src/mosaic-stack/node_modules for the run, then removed with a plain rm (the link only). The canonical node_modules is untouched.

Files changed

  • packages/webui/src/public/bus.js: one attribute value, the writers table aria-label (line 400).
  • packages/webui/README.md: the task fields paragraph (line 522), one phrase.
  • packages/webui/tests/bus-browser.test.mjs: a readFileSync import, the hostile fields copy, the label-excerpt class assertions (lines 94-99), three S5 assertions (lines 236-240), and one README pin test (line 76).
  • packages/tasks/src/fields.mjs: header comment, lines 3-5. No code change.
  • packages/tasks/tests/fields.test.mjs: header comment; schema case (line 29); prose test with the unmapped-name check (line 91); cto walk actor (lines 143 onward).

Not changed: fields.json, roles/*.json, verbs.mjs, broker.mjs, bus.js beyond the aria-label, the slice 1 plan, the brief.

Packet files: base.txt, build.patch, files.txt, candidate-manifest.sha256, BUILD.md, mutants.py, phase-b.sh (the runner, see below), out/ (logs of this run).

Runner note: the worktree guard refused a command that ran env -C <dir> with a glob computed at runtime. The phase B sequence therefore lives in r1/phase-b.sh, a file with no glob in the command text. It runs each suite with set -e, so the first nonzero exit stops the run. It was run once, from the background, against this worktree.

Phase B results

Run: DOCKER_HOST=unix:///nonexistent.sock, sequential, each step's output in r1/out/. The counts are from the "ℹ tests / pass / fail / skipped" lines of each log.

suite tests pass fail skipped log
packages/bus (glob) 127 127 0 0 out/pkg-bus.log
packages/business 67 67 0 0 out/pkg-business.log
packages/cli (glob) 137 137 0 0 out/pkg-cli.log
packages/control-board 124 124 0 0 out/pkg-control-board.log
packages/conversation 182 182 0 0 out/pkg-conversation.log
packages/discord 178 178 0 0 out/pkg-discord.log
packages/harness (glob) 69 69 0 0 out/pkg-harness.log
packages/ledger (no package.json; tests/ run directly) 78 78 0 0 out/pkg-ledger.log
packages/mosaic 69 69 0 0 out/pkg-mosaic.log
packages/queue 150 150 0 0 out/pkg-queue.log
packages/runs 43 43 0 0 out/pkg-runs.log
packages/seat 105 105 0 0 out/pkg-seat.log
packages/tasks (glob) 92 92 0 0 out/pkg-tasks.log
packages/webui 108 108 0 0 out/pkg-webui.log

"(glob)" marks the packages whose package.json script runs tests/*.test.mjs. The others run tests/ as given, as their scripts do.

Scripts (each exit 0; the last line of each log is its own selftest or suite summary):

script own summary log
scripts/test-auth.sh selftest: 15 passed, 0 failed out/script-auth.log
scripts/test-conductor.sh selftest: 17 passed, 0 failed out/script-conductor.log
scripts/test-config.sh selftest: 24 passed, 0 failed out/script-config.log
scripts/test-discord.sh discord suite: 66 passed, 0 failed out/script-discord.log
scripts/test-extension-package.sh extension package selftest: 18 passed, 0 failed out/script-extension-package.log
scripts/test-foundation.sh selftest: 44 passed, 0 failed out/script-foundation.log
scripts/test-queue.sh queue suite: 27 passed, 0 failed out/script-queue.log
scripts/test-release.sh selftest: 4 passed, 0 failed out/script-release.log
scripts/test-task.sh selftest: 26 passed, 0 failed out/script-task.log

The row 88 tests themselves are in the logs as passing: the README pin (pkg-webui.log), the S5 browser test with its inertCheck (pkg-webui.log), the inherited-verb test and the prose test (pkg-tasks.log).

Pass-count note: packages/tasks/tests/fields.test.mjs is run by the glob; its new tests are the inherited-verb case, the prose test and the walk. The walk ran with the cto as an actor.

Ruling D: inertCheck

Outcome: passed. The S5 browser test ran inertCheck('task') (bus-browser.test.mjs line 240) against the page built from the hostile copy. The test did not fail, so inert() strips or neutralizes the control and bidi characters before txt() escapes them, as assumed. Nothing in bus.js was changed beyond the aria-label. Note the assumption was checked by this run, not by reading inert() in phase A.

Mutants: run after the suites passed

Runner: r1/mutants.py, run as python3 -I .../r1/mutants.py <worktree-root>. Log: out/mutants.log. Both baselines passed first (packages/webui bus-browser, packages/tasks fields). Every mutant was restored with a byte check. No RESTORE or ANCHOR failure. Verdict per mutant: KILLED.

The runner records only the exit code of each killer run, not which assertion fired. The killer locations below are the test and the assertion lines that cover each item, read from the test source. A line-level failure trace would need a re-run that keeps the killer's output.

id mutation (file) verdict killer (file:line)
M1 aria-label suffix removed (packages/webui/src/public/bus.js:400) KILLED packages/webui/tests/bus-browser.test.mjs:236 (aria-label assertion, in the S5 test at :81)
M2 Object.hasOwn becomes in (packages/tasks/src/fields.mjs:48) KILLED packages/tasks/tests/fields.test.mjs:29 (inherited-verb test)
M3 label not escaped (bus.js:399) KILLED packages/webui/tests/bus-browser.test.mjs:238 (comments label shown), and :240 (inertCheck('task'))
M3b note not escaped (bus.js:399) KILLED packages/webui/tests/bus-browser.test.mjs:239 (note shown), and :240 (inertCheck('task'))
M4 due date row: task.schedule to task.assign (docs/plans/2026-10-04_slice-1.md:77) KILLED packages/tasks/tests/fields.test.mjs:91 (prose test), assertion at :115
M5 verbs.mjs:145 lets cto pass the pm writer check (packages/tasks/src/verbs.mjs:145) KILLED packages/tasks/tests/fields.test.mjs:143 (walk), expectation at :201
M6 README wording reverted (packages/webui/README.md:522) KILLED packages/webui/tests/bus-browser.test.mjs:76 (README pin)
M9 broker line 676 routing rule removed (packages/bus/src/broker.mjs:676) KILLED packages/tasks/tests/fields.test.mjs:143 (walk), route expectation at :223-229

Items 3 and 4 have no mutant (comment-only; Sage ruling B).

Resolved finding: literal bidi character in the test source

Found after the first run: packages/webui/tests/bus-browser.test.mjs line 99 held a literal U+202E (bytes E2 80 AE), an invisible right-to-left override in source. Sage ruled to fix it (Trojan Source pattern). Fixed: line 99 now reads assert.ok(comments.label.includes('\u202e'), ...), the escape used on the lines around it. The string's runtime value is unchanged. The fix is a single-character edit that does not move any line, so the killer locations in the table above still hold.

Checks after the fix (run with grep -cP '[\x{200E}\x{200F}\x{202A}-\x{202E}\x{2066}-\x{2069}]'):

  • packages/webui/tests/bus-browser.test.mjs: 0 (it was 1)
  • build.patch: 0

Script skips

Logs under out/ that show skips (none is a failure; all exit 0):

  • out/script-release.log: "skip state-machine cases (docker daemon unavailable)". The 4 passing tests are the non-docker cases. The state-machine cases were skipped only because DOCKER_HOST pointed at a dead socket; on the normal host they run, so the release count differs there.
  • out/script-task.log: adapter seam, workspace/capability, live task, and live recall pair cases skipped (docker daemon unavailable).
  • out/script-queue.log: "skip queue verify and render --check: this checkout is not the queue's canonical root". This check is not run in a worktree. It must run on the canonical checkout (or be run by the gate there).
  • The package suites (out/pkg-*.log) have "\u2139 skipped 0"; the skip lines matched there are test names, not skipped cases.

The scripts were not rerun in this revision; no script references packages/webui or the changed test (grep), so the phase B script results stand.

Revision: fix, reruns, manifest

  • base.txt: 8e0fbca420 (unchanged).
  • build.patch: regenerated with git diff --binary 8e0fbca420a3161ce98c5e0f3910f500daabebba -- packages; 237 lines; five paths; 0 bidi control characters.
  • candidate-manifest.sha256, file sha256: 59a164c6099b490c3dfbcb7b11dbae63ed94a25c16bf131115914c829e17f5a6. The changed hash is bus-browser.test.mjs (now a979fe69...). sha256sum -c passes on all five.
  • webui suite rerun (DOCKER_HOST=unix:///nonexistent.sock, node --test tests/, out/pkg-webui.log): exit 0; tests 108, pass 108, fail 0, skipped 0.
  • mutants.py rerun, gated on the webui pass (out/mutants.log, overwritten): both baselines ok; M1, M2, M3, M3b, M4, M5, M6, M9 all KILLED; exit 0. The runner restores each file with a byte check; the manifest verified again afterwards.
  • node_modules: linked for the run, removed with plain rm. Canonical node_modules intact.
  • No process has a cwd in the worktree after the run (the two /proc matches seen were my own shell and had exited).
  • Nothing committed, pushed, or moved in the queue.

Deviations from the plan, recorded

  • Item 8 label is HOSTILE.slice(34), not all of HOSTILE, because checkFields caps labels at 80 (fields.mjs line 42). Accepted by Sage (ruling A).
  • The phase B sequence is in r1/phase-b.sh because the guard refused env -C with a runtime glob. The commands are the same ones Sage listed. The ledger package has no package.json, so its tests/ directory was run directly.
  • The row 88 README hash in candidate-manifest.sha256 changed (76cd207... to 209434f...) because the README on the new base includes row 97's paragraph. The other four hashes are unchanged.

Open questions, with Sage's rulings

A. Ruled: the label excerpt is accepted. Deviation recorded under item 8; the excerpt-class assertions are at bus-browser.test.mjs lines 94-99. B. Ruled: items 3 and 4 are comment-only. A review read is enough; no pin. C. Ruled: the walk pins the current broker routing at broker.mjs line 676. D. Outcome: inertCheck passed in this run (see above). No stop was needed. E. Ruled, applied: strip at most one trailing s; the prose test fails, not skips, on an unmapped prose name (fields.test.mjs lines 103-111).