Manifest pins 4 files on base 970ef729; candidate source is not committed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
6.3 KiB
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?