chore(docs,scripts,ci): track the register's live view and cross-check it - #2080
Merged
Conversation
…k it The on-box acceptance register is two files: the markdown, and a hand-authored styled HTML page published to a fixed artifact URL. Only the markdown was in the repo — the live view lived in whichever session scratchpad last built it, so it could only be updated by an agent that still had it, and was one cleanup away from unrecoverable. Track it as docs/testing/onbox-acceptance-register-live-view.html. The name states what the file is rather than differing from the markdown by one character, which is the slip that let four consecutive publishes replace the styled page with raw markdown rendering (#2075). check:onbox-register now compares the two: owed total, per-group glance counts, each group header's own count, and the row IDs per group — so 2026-07-28's drift (a row missing on one side, a branch-only row on the other, cancelling in the total) fails instead of reading green. The three markers the parser depends on each error when they stop matching, so a restyle cannot turn the comparison into a silent no-op. Wired into the paths that actually carry the change: the workflow's path filter, verify.yml's hooks-scope regex, and verify-cache's test:hooks extraFiles — the last because the test reads both files at runtime, so without it a register-only diff prints [cached] and sits stale-green. The live view is prettier-ignored: it is authored to be published verbatim, and reflowing markup the checker matches line-wise would break both. Closes #2079 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Independent review of PR #2080 found two confirmed ways the new check could report green on the exact surface it exists to protect, plus a doc claim wider than the code. The section-split marker ended at the closing quote, so it missed the real file's `class="group is-blocked"` and `class="group is-soft"` sections — 7 blocks parsed for 9 elements. Their content folded into the preceding group's block, which meant the gtag filter never actually saw them (the comment saying it did was false), a lettered row added to either would have been attributed to the wrong group, and a purely cosmetic modifier class on any other section made its whole row list read as extra rows. Match the whole class attribute instead. Both parsers used Map.set on a letter with no duplicate check, so a repeated group kept whichever row came last: a glance table listing B twice, wrong count first, passed. The markdown side already guards this; mirror it on both. Also: checkLiveView now honours the unterminated-fence bail-out that checkRegister deliberately has — without it a stray fence made the check demand the deletion of five sections that were fine — and the row-level messages name the section they compared, so a row filed under the wrong group no longer reads as two contradicting errors. Five branches had no test and survived deletion; all 18 are now mutation-verified. The Blocked-section test asserted a shape the real document does not have (its fixture used an unmodified class), so it passed for an incidental reason; it now pins the modifier-class handling itself. CLAUDE.md claimed the checker cross-checks "oldest debt" — it reads only the owed total, the per-group counts and the row IDs. Both doc surfaces now say what is and is not covered. Refs #2079 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ng them Round 2 of review found one more silent pass of the same shape as the first two, plus two claims wider than the code. The row-ID shape filter discarded any `num` that was not `<Letter><N>` BEFORE comparing, so a live-view row numbered `A31b`, `a32` or `A 32` vanished from the check entirely: the page carried a row the register did not and the run stayed green. The markdown side rejects the identical convention violation loudly (`### A19b`). Collect and report them instead, mirroring invalidRowHeadings. Three parser edges hardened while here, each verified against the real committed page: the section marker no longer depends on attribute ORDER (`<section id="…" class="group is-blocked">` silently re-opened the fold the last commit fixed) and no longer matches sibling names like `grouping`/`group-nav`; glance rows carrying an attribute are no longer invisible, which had made an ADDED group row pass; and HTML comments are blanked before parsing, so commenting out a section reads as missing rather than passing, and a commented-out row is no longer counted. Two branches survived the last commit's mutation drill, so "all 18 are now mutation-verified" was wrong: nothing fed checkLiveView a markdown without the glance heading, and the zero-sections test asserted `.some` rather than the exact list, leaving its early return unpinned. Both now covered — 21 branches mutated, no survivors. The register also claimed "an edit here that misses the live view fails CI". A wording-only edit does not, and that is the most common edit this file gets. Its edges are now spelled out rather than implied. Refs #2079 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The on-box acceptance register is two files:
docs/testing/onbox-acceptance-register.md, and a hand-authored styled HTML page published to a fixed artifact URL. Only the markdown was in the repo — the live view lived in whichever session scratchpad last built it, so it could only be updated by an agent that still happened to have it, and was one cleanup away from unrecoverable.The live view is now tracked at
docs/testing/onbox-acceptance-register-live-view.html(920 lines). The name states what the file is rather than differing from the markdown by one character — that near-identity is the slip that let four consecutive publishes replace the styled page with raw markdown rendering (#2075). Its head comment says it is the publish source, that the markdown must never be published in its place, and why it deliberately isn't a complete HTML document (the publisher supplies the<!doctype>/<head>/<body>skeleton, so one here would nest).npm run check:onbox-registernow compares the two files. Owed total, per-group glance counts, each group header's own row count, and the row IDs group by group. That last one matters: the 2026-07-28 drift was a row missing on one side and a branch-only row on the other, and they cancelled in the total — a count-only check reads green on it. All three markup markers the parser depends on error when they stop matching, so a future restyle fails loudly instead of turning the comparison into a silent no-op.Both docs surfaces that instruct an agent —
CLAUDE.mdstep 3 and the register's own "Live view" section — now name the file to publish rather than only the URL to pass, and state what the check does not cover.Wiring
Three places, because a check that doesn't run on the diff that breaks it is decoration:
onbox-register-check.ymlpath filter — a live-view-only edit must fire it;verify.yml'shooksscope regex — same, for thetest:hooksleg;verify-cache.mjs'stest:hooksextraFiles— the test reads both register files as text at runtime, so without them a register-only diff prints[cached]and the cross-check sits stale-green locally. Same trap already documented there forRELEASE_NOTES.mdandbump-version.mjs.The live view is
.prettierignored: it is authored to be published verbatim, and Prettier splitting an element's text across lines breaks the checker's element-level patterns.Review round 2 —
93763526An independent adversarial pass found two confirmed silent-pass holes in the first commit and one doc overstatement. All folded, plus the cheap findings:
class="group is-blocked"andclass="group is-soft"— the real file's last two sections — never matched: 7 blocks parsed for 9 elements. Their content folded into the preceding group's block, so the gtag filter never actually saw them (the comment claiming it did was false), a lettered row added to either would have been attributed to the wrong group, and a cosmetic modifier class on any other section made its whole row list read as extra rows. Now matches the whole class attribute.Map.setis last-writer-wins with no duplicate check, so a glance table listing B twice — wrong count first — passed, and a duplicated section rendered the group twice with nothing reporting it. The markdown side already guards this; both live-view parsers now mirror it.checkLiveViewdropped the unterminated-fence bail-outcheckRegisterdeliberately has. A stray fence truncates the markdown, so the comparison demanded the deletion of five sections that were fine.CLAUDE.mdclaimed the checker cross-checks "oldest debt" — it reads the owed total, per-group counts and row IDs only. Both doc surfaces now say what is and isn't covered.Review round 3 —
932fa85dA second adversarial pass verified all seven round-1 fixes are real, and could not construct an edit that drifts the checked surfaces past the gate. It found one more silent pass of the same shape, plus two claims wider than the code:
numthat wasn't<Letter><N>before comparing, so a live-view row numberedA31b,a32orA 32vanished from the check entirely — the page carried a row the register did not, and the run stayed green. The markdown side rejects the identical violation loudly. Now mirrored.<section id="…" class="group is-blocked">silently re-opened the fold the previous commit closed) and no longer matches siblings likegrouping/group-nav; glance rows carrying an attribute are no longer invisible, which had let an added group row pass; and HTML comments are blanked before parsing, so commenting out a section reads as missing rather than passing, and a commented-out row is no longer counted as real.checkLiveViewa markdown without the glance heading, and the zero-sections test asserted.somerather than the exact list, leaving its early return unpinned. Both covered now.Test plan
32 new cases in
scripts/tests/check-onbox-register.test.mjs(26 → 58 in that file;npm run test:hooks824 pass, 0 fail), each mutating one aspect of a coherent fixture pair:gcountn owed/table.glance/section.groupmust errorgroupingdecoy section is not mistaken for a group; a commented-out section reads as missing while a commented-out row is not counted; an added glance row carrying an attribute is still seenMutation-verified: all 21
errors.push/early-return branches incheckLiveViewneutralised in turn, suite re-run each time. No survivors.Checklist notes: no regression plan (tooling only, no behaviour change); release notes skipped — process/CI change with no user- or operator-visible delta; on-box acceptance unaffected — this changes how the register is checked, not what it owes, and its counts are unchanged.
Closes #2079. Follows #2076 (wording fix) and #2075 (the incident).