Manifest 4387c8d5. Rebase only: same added and removed lines, three files re-digested after rows 58, 60 and 59. Gate green, round 1 mutants all killed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
11 KiB
Row 67 (#1560): decisions read follow-up, round 2
Filbert, 2026-10-12. Brief: docs/plans/2026-10-11_decisions-read-follow-up.md,
"Decisions read follow-up: seen-then-resolve, list-before-503 and full-row
tests" (blob 7674ff40, unchanged). Darkwing and Dewey review. Sage commits
after the gate. Nothing in the candidate is committed, staged or pushed.
Base f38b7f46, origin/refactor's tip when I rebased. build.patch applies
there with git apply --index, and candidate-manifest.sha256 pins all 4
files after the patch. Round 1 was manifest 8959e615…7369 on 970ef729,
approved by Dewey (27452) and Darkwing (27627).
Round 2: rebase only
Sage set the scope: a rebase onto the current tip, the merged hunks named here, a fresh manifest, and a digest re-approval. Nothing new goes in, so the round 1 description, mutants and questions below still apply. Dewey's N1 (an assertion that test 4's new decisions are listed) is not folded in; it stays a follow-up.
What landed on the manifest paths
Since 970ef729, three rows changed 3 of the 4 files.
packages/bus/tests/decisions.test.mjs is untouched.
- Row 58 (
aaf87213): the bus README,broker.mjsand webuibus.test.mjs. - Row 60 (
1771bf98): the bus README,broker.mjsand webuibus.test.mjs. - Row 59 (
a8512007): the bus README andbroker.mjs.
Digest check
Round 1's patch no longer applies plain (README line 205), as Darkwing found.
It applies to f38b7f46 with git apply --index --3way with no conflicts.
I compared the round 1 and round 2 patches:
- The added and removed lines are byte-identical (106 lines each).
- The hunk headers moved: README 205 to 235,
broker.mjs837 to 861, webuibus.test.mjs163 to 191. Thedecisions.test.mjshunks are unchanged. - One context line differs, below.
decisions.test.mjs keeps its digest (9477cfa7…). The other three have new
digests because their bases changed.
Merged hunks
Line numbers are in the round 2 tree.
packages/bus/README.md, lines 235–243: the "Every row is…" sentence is rewritten as in round 1. In round 1 the hunk's last context line was the## Shared reads and adapter boundaryheading. Row 60 has since put itscredentialsparagraph between the two, so that paragraph's first line is the context now. The sentence and the paragraph share no lines.packages/bus/src/broker.mjs, lines 864–867: the comment abovecase 'decisions'(line 868). Row 59'sagentsblock ends above it (line 852 on), and row 60'scase 'credentials'follows it (line 889). Neither touches the comment or the case.packages/webui/tests/bus.test.mjs, lines 194–198: the three no-bus requests still follow the/api/bus/agentsnot-configured check, inside "bus refusals map to statuses…". Row 58 and row 60's additions sit above.
Gate (round 2)
Run in a worktree of f38b7f46 with the round 2 patch staged and
node_modules linked, in sequence, with TMPDIR in my scratch and
DOCKER_HOST pointed at a missing socket, 2026-10-12T01:47:57Z to 01:52:03Z.
- Packages: webui 62/0, bus 95/0, cli 114/0, control-board 124/0.
- All nine
scripts/test-*.shexit 0: auth 15, conductor 17, config 24, discord 66, extension-package 18, foundation 44, queue 27, release 4, task 26.test-queueskippedverifyandrender --check, because the worktree isn't the queue's canonical root.test-releaseskipped its Docker cases without a daemon. - Afterwards
git statusshowed only the four staged paths and thenode_moduleslink, and the tree checked 4/4 against the manifest.
Mutants (round 2)
I reran round 1's nine mutants (mut/run.mjs) on the round 2 tree,
2026-10-12T01:52Z to 01:53Z. All nine are killed, each by the same test as in
round 1. The bus suite now has 95 tests and the webui file 9, so the counts
read 94/1, 92/3 for view-blocking-raw and 8/1 for web-503-first, where
round 1 read 80/1, 78/3 and 7/1. One edit changed: row 59 added a second
blocking: !!d.blocking, in the agents read (broker.mjs line 930), and
the runner refuses a target that isn't unique. So view-blocking-raw ran
from a copy of the runner that anchors on its whole line (line 297, inside
the decisions view), and the edit is the same. After each run git diff was
unchanged, and the tree checked 4/4 against the manifest afterwards.
The round 1 packet follows unchanged: the description, files, round 1 mutants, round 1 gate and questions. Its base and line numbers are round 1's.
Row 67 (#1560): decisions read follow-up, round 1
Filbert, 2026-10-11. Brief: docs/plans/2026-10-11_decisions-read-follow-up.md,
"Decisions read follow-up: seen-then-resolve, list-before-503 and full-row
tests" (blob 7674ff40). Darkwing and Dewey review. Sage commits after the
gate; nothing is committed, staged or pushed yet.
Base 970ef729. build.patch applies there with git apply --index;
candidate-manifest.sha256 pins all 4 files after the patch.
No behaviour changes. No new test found a defect; the only non-test edits are the broker comment and the README sentence that repeats it.
What ships
- Seen, then closed (
decisions.test.mjs, new test afterrecent). The human marks the human-routed decision seen and resolves it;ctodoes the same for its decision;pmmarks its decision seen and the raiser withdraws it. For each, the store holds exactlyseenthen the closing op, and therecentrow'sclosedhas that op, the closing event'sseqand itsby. The test also pins therecentorder by close. Mutantrecent-closed-first-eventfails it. - List before 503 (
webui/tests/bus.test.mjs, the no-bus server in "bus refusals map to statuses…"). With no bus configured,/api/bus/decisions?list=badanswers 400invalid-request, and?list=rolesanswers 503not-configured, so a good list still reaches the bus check. Trail checkssubjectthe same way, so the same loop pins/api/bus/trail?subject=%3Cat 400. Mutantweb-503-firstfails it. - The whole row (test 4, "every row is what trail already gives…"). For
every
rolesandrecentrow, for human and reader sessions, the row minusauthorizationandcloseddeep-equals trail'sdecisionsrow for that id minustable, withblockingas a boolean.authorizationdeep-equals{action, target, approvalChoice}from trail's oneaction.alloweddecision.raiseevent for the id.closedis present onrecentrows only, and equals trail'sdecision_eventsrow as before. The seed leftproject,requirement_refandsupersedesnull and every approval choice at the defaultyes, so a test of them would pass on a mutant that nulled them. Test 4 now raises two more decisions: one with a project and a requirement ref, then one that supersedes it, blocking, on a task, with approval choiceno. The first closes aswithdrawnby the supersede, so it lands inrecent; the second is inroles. - The comment above the
decisionscase inbroker.mjsnow says which part each row equals: inbox's view of the decision, which is trail'sdecisionsrow withblockingas a boolean plusauthorizationfrom the raise'saction.allowedevent (trail gives that too); arecentrow addsclosed, the closingdecision_eventsrow. - README (
packages/bus/README.md): the sentence "Every row is the decision astrailgives…" said the same imprecise thing, so it now says what the comment says. The brief allows this edit only if the comment fix changes what the README says; it does.
Out of scope, unchanged: no LIMIT on roles; roles order stays seq.
Files
packages/bus/src/broker.mjs: comment only.packages/bus/README.md: one sentence.packages/bus/tests/decisions.test.mjs: one new test; test 4 rewritten.packages/webui/tests/bus.test.mjs: three requests in the no-bus case.
Mutants
mut/run.mjs (not shipped): one edit per mutant in the worktree, then the
owning suite (packages/bus/tests/*.test.mjs, or
packages/webui/tests/bus.test.mjs for web-503-first), then a restore.
git diff was unchanged after each run.
| Mutant | Edit | Base 970ef729 |
Candidate |
|---|---|---|---|
recent-closed-first-event |
closed is the first event for the id |
survives | killed: seen-then-closed |
recent-join-any-event |
recent joins any event, not only closing ops |
survives | killed: seen-then-closed |
web-503-first |
busRead answers 503 before checking the request |
survives | killed: refusals test |
view-requirement-null |
row drops requirement_ref |
survives | killed: test 4 |
view-supersedes-null |
row drops supersedes |
survives | killed: test 4 |
view-project-null |
row drops project |
survives | killed: test 4 |
view-task-ref-null |
row drops task_ref |
survives | killed: test 4 |
view-approval-fixed |
approvalChoice always yes |
survives | killed: test 4 |
view-blocking-raw |
blocking left as 0/1 |
killed: tests 1, 2 | killed: tests 1, 2, 4 |
Candidate: 9 of 9 killed, each by the test this row adds or rewrites. Base: 8 of 9 survive. Each mutant fails only the tests named above; no other test moved.
Gate
gate.sh (not shipped) on the candidate worktree, sequentially, with
TMPDIR in scratch and DOCKER_HOST pointed at a missing socket:
| Suite | Result |
|---|---|
node --test packages/bus/tests/*.test.mjs |
81 pass, 0 fail |
node --test packages/control-board/tests/*.test.mjs |
124 pass, 0 fail |
node --test packages/webui/tests/*.test.mjs |
40 pass, 0 fail |
node --test packages/cli/tests/*.test.mjs |
112 pass, 0 fail |
scripts/test-auth.sh |
15 passed, 0 failed |
scripts/test-conductor.sh |
17 passed, 0 failed |
scripts/test-config.sh |
24 passed, 0 failed |
scripts/test-discord.sh |
66 passed, 0 failed |
scripts/test-extension-package.sh |
18 passed, 0 failed |
scripts/test-foundation.sh |
44 passed, 0 failed |
scripts/test-queue.sh |
27 passed, 0 failed |
scripts/test-release.sh |
4 passed, 0 failed |
scripts/test-task.sh |
26 passed, 0 failed |
Every suite exited 0. sha256sum -c candidate-manifest.sha256 passes in the
worktree, and git diff 970ef729 there hashes the same as build.patch.
No commit since 970ef729 touches packages/.
Rows 59 and 60, both in review, also edit packages/bus. On a fresh
970ef729 worktree, build.patch applies after row 59's patch with plain
git apply, and after row 60's (alone, or after 59 then 60) with --3way
and no conflicts. Whichever lands first, this one re-bases cleanly; the
gate above is for this patch alone.
Questions for reviewers
- The webui check also pins
trail?subject=before the 503. The brief names onlylist; the order is the same code path one branch up, and the line is in a file this row owns. Keep it or drop it?