test(hygiene): declare the source-hygiene gate's scan surface once - #1343
Merged
Conversation
The three sandbox suites that run scripts/check-source-hygiene.mjs each declared their own hand-copied copy of the gate's SCANNED, TEXT_SCANNED and ROOT_TEXT_FILES, and nothing checked any copy against the gate. #1236 made those copies load-bearing: each suite's rootFixture() branches on its own ROOT_TEXT_FILES to decide which fixtures carry a copyright header. Move the three lists, with their reasoning, into scripts/lib/source-hygiene-surface.mjs and import them from both the gate and the suites. No constant value moves; the moved text is byte-identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ED4cusQ7pxZ7d7TCLKrZb
…nothing Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ED4cusQ7pxZ7d7TCLKrZb
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
This was referenced Aug 27, 2026
os-trump
marked this pull request as ready for review
August 27, 2026 02:06
This was referenced Aug 27, 2026
os-trump
pushed a commit
that referenced
this pull request
Aug 27, 2026
…s prose (#1346) The comment above the "does not judge .d.ts" pin explains why the header check reads `.ts` only, and propped that explanation on two hand-maintained integers: "3 of 5 `.mjs` under scripts/, the `.sh`, the four `src/docs/*.md` pages". Sixth instance of the defect the #1338 family collects — a counted fact in prose with no producer and nothing checking it — handled the same way: the numerals go, the claim they propped up stays, and no counting guard is added (ADR-0049 axis 3). This instance came with its own demonstration that renumbering is not a repair. The card was written against "2 of 7"; PR #1343 moved it to "2 of 8" two hours later by adding `scripts/lib/source-hygiene-surface.mjs`; and measuring here with the gate's own rule gives 3 of 8. The third reading differs from the second because `scripts/publish-marketplace.mjs` opens // Copyright (c) 2026 ObjectStack contributors. … while `COPYRIGHT_HEADER` is anchored `/^\/\/ Copyright \(c\) \d{4} ObjectStack\./` — it wants the period directly after `ObjectStack`, so that file is headerless to the check and headered to a reader. Three measurements, three answers. Deleting the numerals alone would have left a false sentence, so the `.mjs` clause gains a quantifier. "3 of 5" was not only arithmetic: it said *some, not all*, and five of the eight `.mjs` under `scripts/` do carry the header. "Some of the `.mjs` under scripts/" says that without a figure and holds under either reading of the `publish-marketplace.mjs` spelling. The other two kinds stay unqualified because they are complete — the one `.sh` and all four `src/docs/*.md` pages have no header at all. Prose only: no fixture, assertion or `describe` block is touched, and the gate itself is untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ED4cusQ7pxZ7d7TCLKrZb
4 tasks
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Fixes #1314
The three sandbox suites that run
scripts/check-source-hygiene.mjseachdeclared their own hand-copied copy of the gate's three surface constants, and
nothing checked any copy against the gate. They now import all three from one
producer,
scripts/lib/source-hygiene-surface.mjs.Measured on
49bd297.Route taken, decided against the current tree
Route 1, in its sibling-module form — the card's own "export the constants
from the gate, or a tiny sibling module the gate imports".
The cost that used to rule route 1 out is gone, as the dispatch note says:
#1321 landed
allowJsrepo-wide andtest/source-token-ratchet.test.tsalreadyimports constants from a gate
.mjsunderstrictwithtsc --noEmitgreen.So the choice was never TS7016 — it is top-level side effects, and that is
what decided it:
check-source-hygiene.mjsruns its five checks at module top level and callsprocess.exit(1). A suite that imported it would run the whole gate overthe real repo at import time and could take a vitest worker down with it.
main()behindisMainModule()— which re-indents the banner line. Thatline is The hygiene banner binds the wrong count to the marker and header checks — it says 339 + 3 where those checks read
allTs= 329, and two cards this round were told to trust that line as the producer #1339's, serialised behind this card on this same file, so this PRmust not move it. Confirmed untouched:
git diffcontains zero occurrences ofcodeFiles.length.construction: no guard to get wrong (
scripts/lib/main-module.mjsexistsbecause that comparison was hand-rolled wrong twice, The source-token-ratchet gate silently no-ops (and exits 0) when invoked through a symlinked path — 10 of its own tests fail on every macOS checkout #1252), no output, no
exit, and the gate's executable body is untouched.
Routes 2 and 3 were rejected against this same tree: a source parser (2) is a
maintenance surface the card itself flags, and a
--print-surfaceflag (3)would add a test-only seam to production tooling and still need a parser on the
reading side. Neither buys anything over an import that already typechecks.
⛔ The gate is still runnable as bare
node scripts/check-source-hygiene.mjswith no build step, which is how both workflows invoke it. Verified directly.
No surface moved
The three lists were moved byte-for-byte, doc comments included; the only
textual change is the
exportkeyword on threeconstlines. Proved bydiffing the extracted region against
HEAD:The gate's banner reports the same surface before and after: 3 root
.tsfiles,16 root files, the same five text trees.
Acceptance: the load-bearing proof, not a green suite
All three suites are green today with hand-copied lists, so today's green proves
nothing. Both directions were mutated on
origin/main(control, 08613ad) and onthis branch (treatment). Every mutation was proved on disk by blob hash before
any verdict was read, restored with
git checkout HEAD -- PATH, and the restoreproved by an empty
git diff HEAD; every anchor was match-counted first, so azero-match no-op could not be read as green.
origin/main— hand-copied.tsscanned root file(s) missing: eslint.config.ts.tsDOES extend the marker and header checks to the root .ts files (#1236), which namesvitest.config.tsliterally — red naming the drift, which is the acceptance's second branchSCANNED${CODE_TREES.length} violation(s)assertionThe card says an added root
.ts"leaves all three suites still green whilesilently not exercising the new file", and that the gate's missing-file guard
"cannot fire on this". Measured, that is not what happens in the ADD
direction. In a sandbox, a stale mirror means the file is genuinely absent
from disk, which is exactly what that guard fires on — so all three suites went
red, loudly. What was wrong with it was the message: it names a missing
fixture, sending the reader to create a file, rather than saying a mirror is
stale.
The genuinely silent direction is removal, and it is two suites of three,
not three: dropping a root
.tsfrom the gate's list leftsize-advisoryandheader-positiongreen while both kept materialising a file the gate no longerreads.
scan-surfacecaught it only by accident of asserting the banner's rootcount.
The card is still worth doing, and the fix is unchanged — this only narrows the
claim it rests on.
Notes
REQUIRED_TREES/REQUIRED_ROOT_FILESnaming is kept —those names say what that file needs the lists for. Only the values derive
now.
REQUIRED_TREESis[...SCANNED, ...TEXT_SCANNED], which is what italways was by hand.
GATE_DEPENDENCIES, matchingtest/source-token-ratchet.test.ts:77one file away. It is hand-maintainedand safe to be: an import the sandbox does not carry makes the spawned gate
die with
ERR_MODULE_NOT_FOUNDand takes every case in the file with it, soit cannot rot quietly.
ROOT_EXCLUDED_FILES(the two lock files) stays hand-written on purpose — itis an anti-list, pinning files deliberately absent from the gate's
constants, so there is nothing to derive it from.
Verification
pnpm verifygreen end to end on this tree, quoting each gate's own verdict line:Re-run on the final commit
49bd297:pnpm hygieneexit 0,pnpm hygiene:tokensexit 0,
pnpm typecheckexit 0, and the six directly adjacent suites(three hygiene suites,
script-main-guard,source-token-ratchet,verify-log-decoy-pin) 86 passed.tsc --noEmit --listFilesconfirms the new.mjsis genuinely in the typecheck program rather than silently excluded.Generated by Claude Code
Generated by Claude Code