Skip to content

fix(hygiene): bind each banner figure to the surface its own checks read - #1348

Merged
os-trump merged 1 commit into
mainfrom
claude/issue-1339-banner-binds-allts
Aug 27, 2026
Merged

fix(hygiene): bind each banner figure to the surface its own checks read#1348
os-trump merged 1 commit into
mainfrom
claude/issue-1339-banner-binds-allts

Conversation

@os-trump

Copy link
Copy Markdown
Collaborator

Fixes #1339

Description

The source-hygiene gate opened every run with one sentence:

Source hygiene — 340 files under src, test, e2e, scripts, plus 3 root .ts file(s)
                 in the marker and header checks; the control-byte scan adds 350 …

That leading 340 is codeFiles.lengthevery file type the walk returns
while the marker and copyright-header checks named in the same clause read
allTs, which is .ts only. Re-measured on origin/main @ 41232dc before
touching anything:

walk returns under src, test, e2e, scripts : 340   ← what the banner printed
.ts under those trees (excluding .d.ts)    : 326
root .ts                                   :   3
                                   allTs    = 329   ← what those checks read
gap                                         =  14

A reader taking the sentence at face value got 343 where the truth is
329, and the 14 files in the gap are exactly the set the gate's own
"Why .ts only" paragraph explains is excluded (the four src/docs/*.md pages,
seven scripts/*.mjs, scripts/lib/main-module.d.mts,
scripts/lib/source-hygiene-surface.mjs, scripts/wow1-live-schema.sh).

The gap is not a stale constant that could be corrected once. It widens on its
own
: every non-.ts file added anywhere under the scanned trees moves
codeFiles.length and leaves allTs alone. #1343 added one and took the gap
from 13 to 14 without going near this line. So the remedy has to be a binding,
not a fresh number.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • CI/CD update

Related Issues

Fixes #1339. Context, none of it touched here: #1343 (landed the shared surface
module), #1236 (put the root .ts files on the two .ts checks), #1338 and
#1346 (the no-producer members of the class, whose ruling is the opposite of
this one's).

Changes Made

Route 2 of the card — one banner line per surface — and why, decided on the
real code.
Route 1 (print allTs in that clause) cannot be done without
either dropping codeFiles.length, which the ruling forbids because it is a
live reading, or re-running the two figures together in one sentence, which is
route 2 with worse formatting. codeFiles.length is exactly what the size cap
and its advisory measure, so it keeps its own line, beside the checks where it
is true:

Source hygiene — the surface each check reads:
  console.log, id-in-prose  : 166 .ts file(s) under src
  markers, copyright header : 329 .ts file(s) — 326 under src, test, e2e, scripts plus 3 root .ts file(s)
  size cap, size advisory   : 340 file(s) under src, test, e2e, scripts
  control bytes             : those 340, plus 351 under content, .changeset, docs, .github, .claude and 16 root file(s)
  • scripts/check-source-hygiene.mjs — the banner statement only, plus the
    comment above it recording why the figure is bound rather than corrected and
    why it is not deletable. Every figure is interpolated from the same array its
    checks are handed a few lines below; the marker/header line prints its own
    total and the two parts (allTs.length - rootTs.length and
    rootTs.length) so the arithmetic is visible and cannot be mis-added the way
    the old sentence was.
  • test/source-hygiene-scan-surface.test.ts — one new case that asserts the
    binding. Outside the card's stated file surface, and deliberately: the
    acceptance criterion is "demonstrated by asserting the printed figure against
    allTs in the gate's own suite", which cannot be met inside the gate alone.
    test/source-hygiene-header-position.test.ts (source-hygiene-header-position.test.ts prose says "3 of 5 .mjs under scripts/" — it is 2 of 7, both integers were already wrong, and PR #1343 nudges it to 2 of 8 #1346, in flight) and
    .github/workflows/code-quality.yml were left alone.
  • .changeset/hygiene-banner-binds-what-the-checks-read.md — empty
    frontmatter; tooling and tests only, nothing ships to users.

Readers checked before choosing route 2, since it reshapes a line other
things read:

Nothing else moved: no threshold, no constant, no check, and not one file in
any check's input set. pnpm hygiene reads exactly what it read before; only
what it prints changed.

Testing

pnpm verify green at 6fc5222, the branch head this PR is opened on. The
whole chain ran serialised through the shared verify lock:

✓ source hygiene clean
✓ source token ratchet clean
Test Files  142 passed (142)
Tests  3003 passed | 1 skipped (3004)
os-verify-lock: VERDICT command-exit 0

Reverse-verified, because a green run proves nothing here — the gate was green
before this PR too.
The new case is built on a sandbox where the two figures
disagree: three .ts files under the code trees plus the three root .ts
(marker/header surface = 6) against nine files in the walk (size surface = 9),
six of the nine carrying a marker that the marker check never sees. The fixture
asserts walkSurface > markerHeaderSurface first, so it cannot pass on a
mis-bound banner by the two happening to be equal.

Then the mis-binding was put back on disk and the suite re-run. Mutation proven
before any verdict was read — git hash-object moved from
6f90a9e2f890a7010bb2c2b3d587e71a544d968b, injected text 1 hit, removed text 0
hits — and the run went red, on that case alone:

× binds each banner figure to the surface its own checks read (#1339)
AssertionError: expected '  markers, copyright header : 9 .ts f…' to contain '6 .ts file(s)'
Tests  1 failed | 31 passed (32)

That is the exact defect this PR closes, reproduced and caught: the walk figure
printed in the clause that names the marker and header checks. Restored with
git checkout HEAD -- PATH and proven back at HEAD — blob hash identical,
git diff HEAD empty — before pnpm verify was run.

  • Unit tests pass (pnpm test)
  • Linting passes (pnpm lint, pnpm lint:i18n-gate)
  • Build succeeds (pnpm build)
  • New tests added
  • Changeset added (empty frontmatter — releases nothing)

Additional Notes

The control-byte figure was left computed and was not pinned anywhere. This
PR is its own evidence for why: it reads 350 on main and 351 on this
branch, moved by one by the changeset this PR is required to add.

The banner grew from one line to five. That is the point of route 2 — the
workflow comment sends readers here to learn what the gate reads, and the old
single sentence could not answer that question for four different surfaces
without binding a figure to the wrong one.


Generated by Claude Code

The gate opened every run with one sentence whose leading figure was
`codeFiles.length` -- every file type the walk returns -- while the clause
that figure sat in named the marker and copyright-header checks, which read
`allTs`: `.ts` only. Measured on main, that printed 340 beside two checks
reading 329, so a reader taking the sentence at face value got 343.

The gap widens on its own: every non-`.ts` file added under the scanned
trees moves `codeFiles.length` and leaves `allTs` alone. So the remedy is a
binding, not a corrected number, and the figure is not deletable either --
`codeFiles.length` is exactly what the size cap and its advisory measure.

The banner is now one line per surface, each count printed beside the checks
that consume it, every figure interpolated from the same array those checks
are handed below. No threshold, constant, check or scanned file set moved.

`test/source-hygiene-scan-surface.test.ts` asserts the binding on a sandbox
built so the `.ts` count and the walk count disagree, so printing the walk
figure in the marker clause turns it red.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ED4cusQ7pxZ7d7TCLKrZb
@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hotcrm Ignored Ignored Aug 27, 2026 4:25am

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd CI plumbing and the verification pipeline

Projects

None yet

1 participant