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]>
214 lines
11 KiB
Markdown
214 lines
11 KiB
Markdown
# 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.mjs` and webui `bus.test.mjs`.
|
||
- Row 60 (1771bf98): the bus README, `broker.mjs` and webui `bus.test.mjs`.
|
||
- Row 59 (a8512007): the bus README and `broker.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.mjs` 837 to 861, webui
|
||
`bus.test.mjs` 163 to 191. The `decisions.test.mjs` hunks 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 boundary` heading. Row 60 has since put its
|
||
`credentials` paragraph 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 above
|
||
`case 'decisions'` (line 868). Row 59's `agents` block ends above it
|
||
(line 852 on), and row 60's `case '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/agents` not-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-*.sh` exit 0: auth 15, conductor 17, config 24,
|
||
discord 66, extension-package 18, foundation 44, queue 27, release 4,
|
||
task 26. `test-queue` skipped `verify` and `render --check`, because the
|
||
worktree isn't the queue's canonical root. `test-release` skipped its
|
||
Docker cases without a daemon.
|
||
- Afterwards `git status` showed only the four staged paths and the
|
||
`node_modules` link, 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 after `recent`). The
|
||
human marks the human-routed decision seen and resolves it; `cto` does the
|
||
same for its decision; `pm` marks its decision seen and the raiser
|
||
withdraws it. For each, the store holds exactly `seen` then the closing
|
||
op, and the `recent` row's `closed` has that op, the closing event's `seq`
|
||
and its `by`. The test also pins the `recent` order by close. Mutant
|
||
`recent-closed-first-event` fails 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=bad` answers 400 `invalid-request`, and
|
||
`?list=roles` answers 503 `not-configured`, so a good list still reaches
|
||
the bus check. Trail checks `subject` the same way, so the same loop pins
|
||
`/api/bus/trail?subject=%3C` at 400. Mutant `web-503-first` fails it.
|
||
- **The whole row** (test 4, "every row is what trail already gives…"). For
|
||
every `roles` and `recent` row, for human and reader sessions, the row
|
||
minus `authorization` and `closed` deep-equals trail's `decisions` row for
|
||
that id minus `table`, with `blocking` as a boolean. `authorization`
|
||
deep-equals `{action, target, approvalChoice}` from trail's one
|
||
`action.allowed` `decision.raise` event for the id. `closed` is present on
|
||
`recent` rows only, and equals trail's `decision_events` row as before.
|
||
The seed left `project`, `requirement_ref` and `supersedes` null and every
|
||
approval choice at the default `yes`, 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 choice `no`. The first closes as `withdrawn` by the
|
||
supersede, so it lands in `recent`; the second is in `roles`.
|
||
- **The comment** above the `decisions` case in `broker.mjs` now says which
|
||
part each row equals: inbox's view of the decision, which is trail's
|
||
`decisions` row with `blocking` as a boolean plus `authorization` from the
|
||
raise's `action.allowed` event (trail gives that too); a `recent` row adds
|
||
`closed`, the closing `decision_events` row.
|
||
- **README** (`packages/bus/README.md`): the sentence "Every row is the
|
||
decision as `trail` gives…" 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
|
||
|
||
1. The webui check also pins `trail?subject=` before the 503. The brief
|
||
names only `list`; 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?
|