docs: prioritised backlog, and the replay proof it found broken - #108
Conversation
Triage of the 70 unticked lines across specs/1NN against the tree rather than the record: 37 are feature 123, complete on PR #107; 20 are declined with the reason in the line; 4 describe work already on disk. Nine are genuinely owed, and feature 124 is specified and unstarted. Records one correction the triage found: 113 T006 promises "new §5.11 with FR-52 to FR-60", but those requirements landed under §5.12 — §5.11 is feature 112. The line is to be corrected before it is ticked, not ticked as written. No gate reads docs/v2/, so this file is inert to pnpm gates (check-vocabulary scans app/src, app/config, contracts and scripts; check-site-links scans site/docs; check-blog-length scans site/docs/blog/posts). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KCv1sDnpZZXoXJ2w8osB3s
Two independent adversarial passes and five verifiers ran against the first draft. Four findings were refuted and dropped; the rest are corrected here. Counts. The declined table held 19 rows against a claimed 20, and the record-lag column held 5 lines against a claimed 4. The errors cancelled, so the headline survived wrong terms. The partition is now stated so it can be audited: 70 = 37 + 20 + 2 + 11. pnpm replay-proof selects tests with `vitest run -t replay`, and the generator's AT-04 byte-identity test matches neither its own name nor its describe block: 7 tests run, 623 skip, and that one is skipped. The draft asserted the script "runs every byte-identity test" and told a reader to tick 101 T037 and 107 T607 on it. Those lines are now real work — fix the selector, watch the hole fail, then tick. check-snapshot-drift gives the forecast kernel no cover. All four artefacts hold only archive and nowcast eras, both authored by env-generator, so a planted kernel fault shifting every forecast temperature by 5 C and tripling its spread leaves the gate exiting 0 while the kernel runs 125 times and has its output discarded. The draft sent a reviewer to read a snapshot diff that cannot exist. 111 T070 was listed as an unwritten gate. The tree declines it, with the reason rewritten once the explainers existed and two narrower checks built instead, one watched failing. Read from the checkbox, not the paragraph under it. It moves to the declined table. Also: 123 has 11 ticked lines on main, not none, and 83 on the PR's current tip, not 79; only P2 waits on #107, since it touches no specs outside 123 and 124; SS5.20 spans FR-106 to FR-140; the deferral chain is two independent deferrals, not one; FR-35 is named as T006's residue, the one of its four amendments with no marker; and the claude.ai artifact link is removed, being unresolvable for any other reader and checked by no gate. Refuted and dropped: that issue #61 was claimed discharged (the wording was already hedged), that FR-35's gap was a defect rather than the anticipated residue, that the NetCDF decision already exists (neither disposition is on disk), and that 104 T307 and 103 T208 are stale declines (T307's consumer still sends no Accept header; 107 delivered no rate control). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KCv1sDnpZZXoXJ2w8osB3s
A second pair of adversarial passes ran against the corrected file. Two of their findings did not survive verification and were dropped; the rest are fixed here. The worst was a miscitation the first correction introduced. The 113 T006 row named docs/v2/plan.md:61 as the second stale section reference. That line is a table row about the gh-pages estate, and the only mention of 5.11 in that file, at line 178, is correct: feature 112 did take 5.11. Following the instruction would have corrupted a right line and left the real one, specs/113-operator-flowchart/plan.md:61, untouched. The row now names the right path and warns off the wrong one. The proposed replay-proof remedy was itself unsound. Vitest exits 0 when the selector matches nothing -- checked, 630 skipped, exit 0 -- so the suggested tag reproduces the fault it fixes. The row now asks for explicit file names plus an assertion on the number of tests actually run. The density row quoted 240x168 from the task line. That figure is in no source file: app/src/panels/operator/layout.ts:73-86 sets 208x116 and derives the expanded face as 450x464, so the decision would have been made against roughly a quarter of the real area. The row now says to take the numbers off disk, and to correct the line. Also: the parallelism claim carved out P5's forecast eras, which is discharged in scheduler.ts and collides with the 251 lines #107 adds there; the SS5.20 quotation replaced with what srd.md:1171-1172 actually says; 106 T506 and T507 given back the revisit triggers their glosses dropped; the self-check grep recipe made to print a total rather than per-file counts; the run.json and advisories.test.ts line numbers corrected; the third leakage fact noted as sitting inside a guard; and the forecast view described as arriving with #107 rather than present now. Refuted and dropped: that ClockStrip.tsx arrived with feature 105 -- the clone is shallow and 4dd407c is a graft boundary, so --diff-filter=A misattributes; specs/101-foundations-shell/plan.md:19 places it in 101 -- and that the ComposerPane fetch is at line 73. Both are recorded in the closing section, because a finding is a claim about the tree too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KCv1sDnpZZXoXJ2w8osB3s
IanMayo
left a comment
There was a problem hiding this comment.
Adversarial review
Two rounds, each with two independent pr-adversary passes (correctness; simplicity and the record) given the diff and base only, plus five pr-finding-verifier agents on round one's findings. Round one reviewed c010a77, round two reviewed f5a3484. Every confirmed finding was fixed before this pull request opened, so there are no inline comments — the value left here is the assumptions map and the record of what was refuted.
Assumptions and design decisions, ranked
- That a backlog belongs in a committed file at all. Both rounds, both passes, led with this. The file is a fourth record layer on top of the task lines, their in-line reasons, and six issues, with no gate to keep it true — and it had already drifted from the tree at the moment it was written. Mitigated, not solved: it now opens by saying the lines are the authority, carries a command to regenerate its own counts, and names what dies when #107 merges. Generating it from the lines would be the real fix. A reader who trusts it over the lines is still the failure mode.
- That a figure quoted from a task line is a fact about the tree. The draft applied the tree-is-authority test to checkboxes and not to the prose around them.
240×168was the instance: it appears in no source file, againstlayout.ts's 208×116 and a derived 450×464. - That every cited path and line is right, because every row ends in an imperative a developer will act on. Round two found three wrong, one of which would have corrupted a correct line.
- That two experiments hold without an artefact. The planted 5 °C kernel shift and the 125 kernel invocations were reproduced by a verifier, not by anything on disk. The structural claim behind them is checkable from
run.jsonandbuild-snapshots.ts; the numbers are not. - That "declined" and "deferred with a trigger" are one category. They are not; two rows had their triggers restored.
Confirmed and fixed before opening
docs/v2/plan.md:61named as the second stale §5.11. It is a gh-pages table row, and that file's only §5.11 (line 178) is correct. Real site:specs/113-operator-flowchart/plan.md:61. Following the instruction would have corrupted a right line and left the real one.- The proposed replay-proof remedy reproduced the fault: vitest exits 0 on an empty selection (checked — 630 skipped, exit 0), so a tag degrades the proof to zero tests and it still prints held.
240×168matches nothing on disk (app/src/panels/operator/layout.ts:73-86).- "Everything from P3 down in parallel" false for P5's forecast eras, discharged in
scheduler.ts, where #107 adds 251 lines. grep -cover several files never prints the total the self-check recipe was offered to produce.- The
grep 'feature 113'evidence undercounts: FR-36's marker wraps acrosssrd.md:417-418. - Citation drift:
run.jsonauthors block is 2305–2310;advisories.test.tsit()is at :305. - Over-claims: the §5.20 quotation was a paraphrase; the third leakage fact sits inside
if (noisyFirst && noisySecond); feature 124 stays unstarted after the merge.
Refuted, and dropped
Six findings did not survive verification — two of them errors the passes themselves made:
- That
ClockStrip.tsxarrived with feature 105. The clone is shallow and4dd407cis a graft boundary, sogit log --diff-filter=Amisattributes every file it touches;specs/101-foundations-shell/plan.md:19places it in 101. - That the
ComposerPanefetch is at line 73. It is at :74, as cited. - That the document claimed issue #61 discharged — its wording was already hedged, and the spec records the failure-kinds half as open.
- That FR-35's missing marker is a defect rather than the residue the row anticipates.
- That the NetCDF decision already exists on disk. Neither disposition does, and #62 scopes transfer rather than format.
- That
104 T307and103 T208are stale declines. T307's named consumer landed and sends noAcceptheader; 107 delivered no rate control.
Checked and sound
All arithmetic (70 = 37 + 20 + 2 + 11, and the per-feature allocation); 11 unticked V1 lines; 83/0 on the #107 branch against 11 ticked on main; 35 lines in 124; every srd.md citation (FR-52@583, FR-60@641, V2-C21@173, §5.11=112, §5.12=113, §5.13=110, FR-61@653, FR-62@664, FR-35@407 unmarked, FR-39@444-451, §5.20@1158); the six open issues and their scopes; start-condition.test.ts:228. The replay-proof hole was reproduced independently by both passes (7 passed, 623 skipped). No constitutional trap applies — no code, no generated output, no snapshots, no seam crossing, no wall-clock, no new gate. pnpm gates is 21 clean, which is a non-result here: no gate reads docs/v2/.
The three things worth opening the diff for
- P1 — the replay proof reports "held" over a test it never runs, and would report it over zero tests.
- P0 —
check-snapshot-driftgives the forecast kernel no cover, so #107's empty snapshot diff is correct rather than a missed regeneration. - P6 — the density figure in the task line disagrees with
layout.tsby roughly 4× in area.
Opus 5, two passes per round, findings verified against head before posting.
Generated by Claude Code
The AT-04 one-command proof named the generator's byte-identity test in
its header and excluded it by its selector. `vitest run -t replay` matches
neither `AT-04 seed: two runs from one root seed are byte-identical ...`
nor its `describe('the synthetic ocean (feature 102)')`, so the proof ran
7 tests, skipped 623, and printed "held" over the one property the
generator is most likely to lose. A name filter also has no floor: an
unmatched selector skips everything and exits 0.
Each byte-identity test now carries an `AT-04: byte-identity` marker on
the line above it. The script reads the markers off disk, runs the files
carrying them, and requires every marked test to have run and passed, so
a rename cannot shrink the proof silently. Seven tests are marked; the
eighth the old selector picked up was a boundary test whose describe
matched, not a byte-identity test.
Watched failing three ways, then reverted: no marker anywhere, where the
script refuses rather than proving nothing; a marker not above an `it(`,
named by file and line rather than guessed; and a planted per-run drift in
the generator's draw path, which took six of the seven down with the
generator's named among them. Clean tree: all seven ran and held.
With the proof sound, 101 T037 and 107 T607 are ticked. Also discharged:
113 T006, whose promised section was SS5.11 and is SS5.12 -- feature 112
took that number while 113 was being written -- corrected in tasks.md and
in the feature's own plan.md, with docs/v2/plan.md:178 left alone because
its SS5.11 is right; and FR-35, the one of that line's four amendments
carrying no marker, which now points to SS5.12 for presentation as FR-36
does. 110 T010 and 113 T007 were work already on disk and are ticked.
docs/v2/backlog.md follows the tree: 70 unticked lines at 3ad5212, less
these five, is 65.
pnpm check green: typecheck, lint, 630 tests, build, 21 gates.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KCv1sDnpZZXoXJ2w8osB3s
This branch, running: https://deepbluecltd.github.io/drogna/instances/claude-outstanding-tasks-specs-qvtgnn/Intro · Background · Data · Operator · Map · Messages · Sampling · Courses · Feasibility Built from |
Review of the marker change found that it closed one silent-shrink mode and opened another. A rename can no longer drop a test out of the proof, but a byte-identity test written without a marker sits outside it exactly as the generator's did, and the proof still prints "held". The marker set was a judgement no artefact recorded. So there are two markers and a sweep. `AT-04: not byte-identity` records a considered exclusion with its reason, and the script refuses any test whose name reads as a determinism or replay claim and carries neither marker. The sweep earned itself immediately: it found two byte-identity tests outside the proof under both the old selector and the first marking -- runtime.test.ts's "is deterministic: the same seed provisions the same manifest", which is the half 101 T037's own deferral text named as already tested, and planner.test.ts's "recommends deterministically: one seed, one plan, twice". Nine tests are now marked; two are excluded by name, being single-run claims rather than two-run ones. Two further faults, both reproduced before fixing: Passes were matched by title against a set pooled across every file, then widened by endsWith. A marked test that was skipped in one file could be satisfied by a passing test in another -- the same shape of hole this script exists to close. Matching is now by (file, title) from the report's own file path, with no suffix relaxation. `--reporter=json` replaced the default reporter rather than joining it, so a divergence printed a test name and no diff, and the report was deleted before the verdict was computed. Both reporters now run, cleanup happens after the verdict, and the report is written under the system temp directory rather than into app/, where an interrupted run left an untracked file for `git add -A` to sweep in. Watched failing five ways, each reverted: no marker anywhere; a marker not above an `it(`; a marked test skipped; a marker forgotten on a determinism-shaped test; and a planted per-run drift in the generator's draw path. Clean tree: all nine ran and held, and no stray file remains. Record corrections the same review found. T006 claimed grepping for "feature 113" finds three of four markers; it finds two, because the FR-35 marker this branch added wraps across a line break as FR-36's does -- the tick was falsified by its own edit. Every line-number citation in the ticks and in the backlog is replaced by an anchor, because the same FR-35 amendment moved every srd.md line below it by three. And specs/109-map tasks.md T807 and spec.md both still said the proof "runs every replay test", the claim this work exists to retract; both now say what it does. pnpm check green: typecheck, lint, 630 tests, build, 21 gates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KCv1sDnpZZXoXJ2w8osB3s
IanMayo
left a comment
There was a problem hiding this comment.
Adversarial review — the replay-proof change (c8c3b65)
Two independent passes over the code change, given the diff and base only. This round was the most productive of the three: it found that the fix closed one hole and opened another, and that two of the ticks were falsified by their own commit. Everything confirmed is fixed in 0e19e8a.
Assumptions and design decisions, ranked
- That the marker set is the complete set of byte-identity tests. Nothing established it. Both passes led here, and pass B proved it false by finding two unmarked ones —
runtime.test.ts's is deterministic: the same seed provisions the same manifest (the half101 T037's own deferral text named as already tested) andplanner.test.ts's recommends deterministically: one seed, one plan, twice. Swapping a mechanical selector for a curatorial one and not auditing the curation is the same fault one level up. Fixed: a second marker for considered exclusions, and a sweep that refuses any determinism-shaped test carrying neither. - That "ran and passed" could be decided by title alone. The verification pooled titles across all files and then widened the match with
endsWith. Pass A reproduced both variants in a rig: a skipped marked test reported as held, masked by a passing test elsewhere. Fixed: matched by(file, title)from the report's own path, no suffix relaxation. - That
--reporter=jsonwas a free change of reporter. It replaced the default rather than joining it, so a divergence printed a name and no diff — and the report was deleted in afinallybefore the verdict was computed. Fixed: both reporters, cleanup after the verdict. - That line numbers are a durable way to cite the tree. Falsified by the same commit: the FR-35 amendment inserted three lines into
srd.mdabove almost every citation the ticks and the backlog make. Fixed: anchors throughout. - That the proof runs on somebody's schedule. It is in no workflow, no
pnpm check, and no gate;scripts/is neither typechecked nor linted by CI. Not fixed here — flagged below.
Confirmed and fixed in 0e19e8a
- The marker set was incomplete by two, and nothing detected an unmarked byte-identity test.
- File-blind title matching could report a skipped marked test as held.
- A divergence printed no diff, and the evidence was deleted unread.
app/replay-proof-report.jsonwas written into the repo, ungitignored, and the cleanup did not run on thecatchpath (process.exitskips the enclosingfinally). Now written under the system temp directory.113 T006claimed grepping for "feature 113" finds three of four markers. It finds two — the FR-35 marker this branch added wraps across a line break exactly as FR-36's does. The tick was falsified by its own edit.- Eleven line citations stale at head by the size of this branch's own
srd.mdinsertion. specs/109-map/tasks.mdT807 andspecs/109-map/spec.mdboth still said the proof "runs every replay test" — the claim this work exists to retract, left standing one file over.- Dead machinery:
fullNamein the passes set and theendsWithscan, whose only reachable effect was to loosen the match.
Watched failing, five ways
Each reverted after being seen to fail: no marker anywhere (the floor a name filter never had — vitest run -t <unmatched> skips all 630 and exits 0); a marker not above an it(, named by file and line rather than guessed; a marked test skipped; a byte-identity test whose marker was forgotten, which the sweep names; and a planted per-run drift in the generator's draw path, which took six down with the generator's named among them. Clean tree: nine marked tests ran and held, no stray file.
Not fixed, and why
pnpm replay-proof runs on nobody's schedule. It is absent from .github/workflows/ci.yml, from pnpm check and from scripts/gates.registry, and scripts/ is outside app/tsconfig.json's include and outside eslint .'s cwd — so this script's ~180 lines are typechecked by tsx's type-stripping alone. Unlike every gate in scripts/gates/, it has no entry in scripts/tests/. That is exactly how it stayed silently wrong from the 101–109 close-out until now, and it is beyond what this pull request was for: wiring it into CI is a change to the check surface that deserves its own argument. Worth an issue.
Checked and sound
pnpm check green — typecheck, lint, 630 tests, build, 21 gates. The diagnosis behind the change verified independently by both passes: vitest list reports 630 tests and -t replay matches exactly 7, the generator's among the skipped; an unmatched selector exits 0. The backlog's arithmetic re-derived line by line (65 = 37 + 20 + 8, with the declined table at exactly 20 rows). No constitutional trap applies — the app/src changes are comment lines only, no import moved, nothing generated or snapshotted touched. Both passes confirmed the plan.md §5.11→§5.12 fix lands on the correct file, the one an earlier round nearly got wrong.
Opus 5, two passes, findings verified against head before fixing.
Generated by Claude Code
The AT-04 proof had reported "held" over a test it never ran since the 101-109 close-out, and nothing noticed, because nothing ran it: it was in no workflow, not in scripts/gates.registry, and not reachable from pnpm check. Fixing the selector without fixing that would leave the next such fault to be found the same way. Split in two, by cost. The static half is a gate: every test whose name reads as a determinism or replay claim must carry either `AT-04: byte-identity` or `AT-04: not byte-identity` with its reason, and a marker must sit directly above an it(...) the proof can name. That runs in pnpm gates at no test time, so the case that produced the original fault -- a byte-identity test outside the proof -- is caught on every change rather than when somebody remembers the command. The dynamic half stays pnpm replay-proof and is now its own CI step, about two minutes, because it re-runs the marked tests' files. Placed after the gates and before the build. The scan is shared rather than written twice: replay-proof.ts imports it from the gate. A gate and a proof disagreeing about which tests are in the proof would be the same fault as the old selector disagreeing with the script's own header. The empty-set floor stays in the proof, not the gate: a fixture tree with no byte-identity tests is legitimate, a proof asserting nothing is not. Watched failing on two planted fixtures, as the other 21 gates are: a byte-identity test written with no marker, and a marker drifted off the test below it. The second reports both facts, deliberately -- the marker is adrift and the test is unclassified, which are two things to fix. The companion test also asserts the marked set is non-empty and contains the generator's AT-04 test by name, the one the old selector skipped, with the count deliberately not asserted: a number in a test is a number to edit rather than a property to hold. pnpm check green: typecheck, lint, 630 tests, 41 gate tests, 22 gates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KCv1sDnpZZXoXJ2w8osB3s
Two raw NUL bytes went into the file in 0e19e8a, as the separator between a test's file path and its title in the passes key. The separator is right -- a path or a title can contain a space, so a space would be ambiguous -- but writing it as a literal byte made git treat the source as binary. The PR diff for this file rendered as "Bin 9382 -> 7671 bytes" for two commits, which is a change nobody could review, on the script the whole change is about. Same separator, spelled `\0` in source. The file is UTF-8 text again and the diff renders. Behaviour is unchanged: the proof still reports all nine marked tests ran and held. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KCv1sDnpZZXoXJ2w8osB3s
Review of the gate found that it repeated, in its own implementation, the
class of fault it exists to catch. Each is fixed and each is now held by a
planted fixture.
A marker written as a trailing comment marked the wrong test. Reading only
the line below a marker meant `it('replays byte-identically', ...) //
AT-04: byte-identity` recorded the *next* test as the proof's, and let its
own drop out of the sweep unreported -- the original fault's exact shape,
reintroduced by the fix for it. A marker now marks the test on its own line
when it trails one.
The sweep saw only `it(` with the name on the same line. vitest exports
`test` as well, and a multi-line call is ordinary formatting; a
byte-identity test written either way was invisible. The asymmetry is what
made it dangerous: a *marker* above an unrecognised form was reported, but
a missing marker on one was not, and the missing marker is the case the
sweep is for.
Prose quoting a test name was treated as a test. This repository's
docblocks quote test names as a habit -- check-replay-markers' own header
does -- and both escapes were wrong: rewording the prose, or marking it and
adding a test that does not exist to the proof's expected set. Comment
lines are now skipped, using the isCommentLine that scripts/gates/lib.ts
already exported for this.
Two marked tests sharing a leaf title in one file are now refused. The
proof keys a pass by (file, title) because vitest's title carries no
ancestor describe, so one passing could not be told from the other being
skipped.
The proof's own verification had no test at all, which review named as the
deepest finding: deleting the missing-test check left pnpm check, pnpm test
and pnpm gates green while the proof printed "held" over a file vitest had
failed to collect. It moves to scripts/replay-verify.ts and is exercised by
scripts/tests/replay-proof.test.ts -- a skipped marked test, a failed one,
an uncollected file, and a same-named pass in another file that must not
stand in for it.
Record corrections from the same review. CLAUDE.md said pnpm check is "what
CI runs", which this branch made false by adding a CI step; it now says
most of what CI runs, and names the proof beside it. The backlog said the
new gate's test sits "beside the other 21" -- 18, since check-schema-masters
and check-snapshot-drift have none, which is worth saying in a document
about not trusting an unwatched check. And #107 adds 244 lines to
scheduler.ts, not 251, which was the changed-line total.
The CI comment now names the duplication it accepts rather than only its
duration: the App tests step already runs these files once.
Verified in pieces after two OOM kills of the combined run: typecheck,
lint, 630 app tests, 50 gate tests, 22 gates, build, and the proof holding
over 9 marked tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KCv1sDnpZZXoXJ2w8osB3s
IanMayo
left a comment
There was a problem hiding this comment.
Adversarial review — the gate and CI wiring (d398362)
Two independent passes over the code change. This round found that the gate repeated, in its own implementation, the class of fault it exists to catch — three times — and that the proof's verification was held by nothing at all. Everything confirmed is fixed in 734cb5d.
Assumptions and design decisions, ranked
- That the marker set is complete, and that a marker sits on the line above a test. Both passes led here. The second half was the fault: reading only the line below meant a marker written as a trailing comment recorded the next test as the proof's and let its own drop out of the sweep, unreported. Both halves failed at once, silently, and it is the original fault's exact shape — the record names one test, the run proves another.
- That candidacy is a name-vocabulary judgement over
it(with the name on the same line. Three legal spellings were invisible:test(, a multi-line call, andit.each. The asymmetry is what made it dangerous — a marker above an unrecognised form is reported, a missing marker on one is not, and the missing marker is the case the sweep is for. - That the verification logic is exercised by something. It was not. Delete the missing-test check and
pnpm check,pnpm testandpnpm gatesall stay green while the proof prints held over a file vitest failed to collect. The gate half was permanently held by planted fixtures; this half was held by nothing. - That prose is not code.
scan()did not skip comment lines, thoughscripts/gates/lib.tsexportsisCommentLinefor exactly this. A docblock quoting a test name — a habit this repository has, including in the new gate's own header — was reported as a violation, and both escapes were wrong: reword the prose, or mark it and add a test that does not exist to the proof's expected set. - That vitest's
titleidentifies a test within a file. It is the leaf only, with no ancestordescribe, so two marked tests sharing a title in one file would make a pass for either indistinguishable from a skip of the other. Not reachable today; now refused.
Confirmed and fixed in 734cb5d
- Trailing marker marks its own test; the sweep no longer loses it.
test(and multi-line names are swept; the lookahead reads three lines.- Comment lines are skipped, so prose about the rule is not a use of it.
- Duplicate marked titles within a file are refused with the reason.
- The verification moves to
scripts/replay-verify.tswithscripts/tests/replay-proof.test.tsholding it: a skipped marked test, a failed one, an uncollected file, and a same-named pass in another file that must not stand in for it. Four new fixtures hold the gate half. CLAUDE.mdsaidpnpm checkis "what CI runs" — false the moment this branch added a CI step. Now "most of what CI runs", with the proof named beside it and the reason it is not inpnpm check.- The backlog claimed the new gate's test sits "beside the other 21". It is 18:
check-schema-mastersandcheck-snapshot-drifthave no planted-violation test. Worth stating in a document whose closing section is about not trusting an unwatched check. - #107 adds 244 lines to
scheduler.ts, not 251 — that was the changed-line total. - The CI comment now names the duplication it accepts (the App tests step already runs these files) rather than only its duration, and says what would make selecting by name safe now that the report check supplies a floor.
Deliberately not fixed
scripts/ is typechecked and linted by nothing. No root tsconfig.json exists and @types/node sits only in app/node_modules; eslint . is rooted at app/. A trial typecheck gives 11 errors, 9 of them inside gate fixture trees that any real config must exclude. The two real ones are scripts/build-snapshots.ts:93 (a 'departure' era passed where four are accepted — in the script that produces the committed artefacts) and a duplicate join import in scripts/gates/check-vocabulary.ts. Fixing that means a root tsconfig, a lockfile change, and touching the snapshot builder: a separate argument from this one.
Checked and sound
pnpm check green in pieces after two OOM kills of the combined run — typecheck, lint, 630 app tests, 50 gate tests, 22 gates, build — and the proof holds over 9 marked tests. Both passes independently reproduced the diagnosis behind the change (vitest list → 630, -t replay → 7, unmatched selector → exit 0) and confirmed the 9/2 split is honest, both exclusions being genuine single-run claims. The gate and the proof cannot disagree about the marked set, because the proof imports scan rather than re-deriving it — both passes named that as the change's best decision. Constitution clear throughout: the app/src changes are comment lines only, nothing generated or snapshotted, no seam crossing, scripts/gates.registry appended rather than rewritten.
One process note, since it bears on how much these reviews are worth: neither pass caught that scripts/replay-proof.ts carried two raw NUL bytes for two commits, making its diff render as Bin and therefore unreviewable. The passes read files directly, so a binary diff is invisible to them. That is a gap in how the review is run, not in the passes.
Opus 5, two passes, findings verified against head before fixing.
Generated by Claude Code
What this changes, and why
Adds
docs/v2/backlog.md— the 70 unticked task lines acrossspecs/1NN-*/tasks.md, triaged against the tree rather than against the record — discharges the five lines that triage identified as P1, and fixes the proof one of those five turned out to depend on.The count was misleading in three directions at once, which is why a plain list of unticked lines was not the deliverable. 37 of the 70 are feature 123, complete on #107 — they only look outstanding from
main. 20 are declined or deliberately not done and carry their argument in the line; they are listed separately as not backlog, so they stop being re-read as work every time somebody counts checkboxes. That left eleven, and five are done here: 65 unticked lines remain, of which 8 are work.The replay proof was broken.
scripts/replay-proof.tsclaimed in its header to run "every byte-identity test in the suite — the generator's, the whole loop's, and the advisories-and-bundles one", and selected them withvitest run -t replay. The generator's test isAT-04 seed: two runs from one root seed are byte-identical…insidedescribe('the synthetic ocean (feature 102)')— neither string contains "replay". Seven ran, 623 were skipped, and it printed replay proof held over the one property the generator is most likely to lose. A name filter has no floor either: an unmatched selector skips everything and exits 0.Fixing it took three rounds, because each fix reproduced the fault at one remove.
AT-04: byte-identitymarker read off disk. That closed the rename hole and opened another: a byte-identity test written without a marker sits outside the proof exactly as the generator's did, and the marker set was a judgement no artefact recorded.AT-04: not byte-identityfor considered exclusions and a sweep refusing any determinism-named test carrying neither. The sweep earned itself immediately, finding two byte-identity tests outside the proof under both the old selector and the first marking:runtime.test.ts's is deterministic: the same seed provisions the same manifest — precisely the half101 T037's own deferral text named as "already tested" — andplanner.test.ts's recommends deterministically: one seed, one plan, twice.test(and multi-lineit(were unseen; and prose quoting a test name was treated as a test. It also found that the proof's verification had no test at all — delete the missing-test check andpnpm check,pnpm testandpnpm gatesall stay green while it prints held over a file vitest never collected.Nine tests are marked, two excluded by name as single-run claims.
And it now runs. The proof had been wrong since the 101–109 close-out and nothing noticed, because nothing ran it. It is split by cost:
check-replay-markersis gate 22, so the sweep fires on every change at no test time, with its own planted-violation fixtures; the half that runs the tests is a CI step of its own — measured at 91 seconds in CI.Rejected: widening the name pattern to
replay|byte-identical. It closes today's gap and not the class — expected set and observed set would both come from the names, so a rename moves them together and the proof shrinks in silence.Also rejected: filing the remaining eight backlog items as GitHub issues. They are already distributed across the feature records that own them, and a second copy would be a second thing to keep true.
The instance, opened at the change
Nothing visible — no shell or panel code changes. The only executable changes are build-time scripts and a gate; the
app/srcedits are comment lines.The blog entry
No entry. A working document plus a fix to a proof script is neither a new face in the shell nor a piece of backend simulation worth watching work — D17's test, and this fails it in both halves.
What was watched failing
The proof, five ways, each reverted after being seen to fail: no marker anywhere (the floor a name filter never had —
vitest run -t zzz-no-such-test-nameskips all 630 and exits 0); a marker not above anit(, named by file and line rather than guessed; a marked test skipped, caught because matching is by(file, title)so a pass elsewhere cannot mask it; a byte-identity test whose marker was forgotten, named by the sweep; and a planted per-run drift in the generator's draw path (+ plantedDrift++ * 1e-9) → six of nine failed, the generator's named among them.The gate, on planted fixtures beside the other gates': an unmarked byte-identity test; a marker adrift from its test; a trailing marker, which must mark its own test and not the next;
test(and a multi-line name, which must be swept; prose quoting a test name, which must not be; and two marked tests sharing a title in one file, which must be refused.The proof's verification, now that it has tests at all (
scripts/tests/replay-proof.test.ts): a skipped marked test, a failed one, an uncollected file, and a same-named pass in another file that must not stand in for it.Clean tree:
replay proof held: all 9 marked byte-identity tests ran and reproduced their runs byte-identically, exit 0, no stray file — the report goes to the system temp directory, not intoapp/.pnpm checkgreen: typecheck, lint, 630 app tests, 50 gate tests, build, 22 gates. CI green on734cb5d, all 24 steps.Backend changes: what was brought up, and what was seen
Not a backend change. Eleven test files gain a comment line each; no test body, assertion or production path is touched.
Left undone, and why
scripts/is typechecked and linted by nothing. No roottsconfig.json,@types/nodeonly inapp/node_modules,eslint .rooted atapp/. A trial typecheck gives 11 errors, 9 inside gate fixture trees that any real config must exclude; the two real ones arescripts/build-snapshots.ts:93(a'departure'era where four are accepted — in the script that produces the committed artefacts) and a duplicatejoinimport inscripts/gates/check-vocabulary.ts. That needs a root tsconfig, a lockfile change and a careful look at the snapshot builder: a separate argument from this one.specs/107-operator-view/spec.mdrecords.Review
Four rounds of
/adversarial-review— two on the document, two on the code — each with two independent passes, plus verifier agents on round one's findings. Each round found the previous fix unsound at one remove, which is the useful part of the record: round two caught a miscitation round one's correction had introduced, which would have sent a developer to edit a correct line while leaving the real stale one untouched; round four caught the gate reproducing, in its own implementation, the fault it exists to catch.Across all rounds nine findings did not survive verification and were dropped — two of them errors the passes themselves made:
ClockStrip.tsxattributed to feature 105 (a shallow-clone artefact —4dd407cis a graft boundary, sogit log --diff-filter=Amisattributes) and a line number that was already correct.One thing no pass caught:
scripts/replay-proof.tscarried two raw NUL bytes for two commits, so its diff rendered asBinand was unreviewable. The passes read files directly, so a binary diff is invisible to them.🤖 Generated with Claude Code
https://claude.ai/code/session_01KCv1sDnpZZXoXJ2w8osB3s