Skip to content

docs(gate): stop check-engine-double-contract restating its own ledger size in prose - #9944

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-9915-ledger-size-prose
Aug 19, 2026
Merged

docs(gate): stop check-engine-double-contract restating its own ledger size in prose#9944
os-steve merged 1 commit into
mainfrom
claude/issue-9915-ledger-size-prose

Conversation

@os-steve

@os-steveos-steve commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Fixes#9915

scripts/engine-double-contract.pinned.json is generated: --write — the action this
gate's own failure messages instruct authors to take — moves its row count. Prose in
scripts/check-engine-double-contract.mjs had copied that count beside it, so a routine,
sanctioned action silently falsified the comment block whose stated job is to justify a
merge-blocking ratchet to the next reader.

Comment-only. No invariant, criterion, exit code, population or output string is
touched — proof below.

Re-derived count: 7 sites, 13 figure occurrences, not 5

The card said 5 and warned the live number might differ either way (PR #9712 landed since
filing). It is higher. Method: extract every numeric literal in the file's comment lines,
then classify each by whether it describes a population the script itself loads. A
mechanical cross-check — comparing every array length in the pinned ledger against the
file's prose — independently flags the sites whose figure currently equals a live
ledger size, catching the two the card missed.

The table below groups the 13 occurrences by the sentence they sit in.

Measured at merge base: the ledger holds 310 rows / 321 doubles / 176 files, and
10 rows pin more than one double.

sitefigurewasdisposition
L206309 of 310 (file, verb) rowsaccuratemust stay literal → anchored as dated
L214310 entries today / 135 DEBTaccuratedeleted → ratchet-guaranteed claim
L1481folding 308 generated rowsstale (310)deleted → ratchet-guaranteed claim
L1500319 pinned doublesstale (321)deleted → points at the ledger
L1501across 308 (file, verb) pairsstale (310)deleted → points at the ledger
L1501so 11 pairs carry more than onewrongdeleted
L154010 of 308 rows dostale (308)deleted → points at the ledger
L1581308 separate "not in the ledger" errorsstale (310)deleted → "one per row"
L2918308 problems for one missing filestale (310)deleted → "one per row"

All five sites the card listed were still stale — the count did not go down. Two more
(L206, L214) are the same defect latent: correct today, wrong on the next --write.
L214 is the site PR #9712 refreshed; refreshing is what left it still able to rot.

One figure was never right

319 pinned doubles across 308 (file, verb) pairs, so 11 pairs carry more than one — 11
is the excess of doubles over rows, not the count of rows pinning more than one. That
count is 10 (9 rows pin 2, one pins 3), which is what the paragraph 40 lines below
already said: measured: 10 of 308 rows do. Two sentences in one file gave different
values for the same quantity, and the arithmetic that made the claim look self-consistent
(321 − 310 = 11) is the coincidence the card flagged.

Approach: removed or ratchet-guaranteed, never re-measured

Per the #9803 / PR #9909 lesson, a refreshed constant reproduces the defect with a fresher
value. And these are comments — nothing renders them, so "derived at run time" is not
available without making --write rewrite this file's own source, which would be a
behaviour change. So each figure is instead removed, or replaced by a claim the ratchets
already guarantee:

  • The pinned ledger may only GROW and the baseline may only SHRINK — both stated
    in the paragraph being edited. So "a census that ALREADY OUTNUMBERS them and can only
    outnumber them further" and "hundreds of them, and growing" are monotone-true by
    construction, not by measurement.
  • Where the magnitude was incidental (the bootstrap error explosion), the point is "one
    error per census row", which needs no number and reads better without one.
  • Where the live number matters, the prose now points at the artifact that holds it
    rather than copying it.
  • L206's 309 is genuinely underivable — it is a calibration agreement rate against a
    past HEAD, not a ledger size. It stays literal but is anchored as a dated measurement,
    matching the idiom the paragraph above it already uses (in the month to 2026-08-18).

Removing 135 also corrected a second inaccuracy: the gate reports 133 in the DEBT ledger, 2 exempt — 135 is the baseline file's total entries, not what the DEBT ledger
carries.

Verification (all at e7ebebd66, the final commit)

Comment-only proof. Both revisions emitted through the TypeScript compiler with
removeComments: true, compared byte-for-byte:

emitted bytes: base=80651 head=80651
IDENTICAL — the change is comment-only

(A raw-scanner token diff was tried first and desynced on regex-literal ambiguity; the
full-parser emit is the trustworthy instrument.)

Gates, re-derived from the actual changeset with
node scripts/pm/dispatch-gates.mjs (no paths passed — it takes the change set from the
merge base itself), which placed 3 families:

pnpm check:engine-double-contract
OK self-test: separates engine doubles from driver doubles ...
check-engine-double-contract: OK — 321 pinned, 133 in the DEBT ledger, 2 exempt.
check-engine-double-contract: 310 (file, verb) row(s) held by the RETAINED ledger
pnpm check:cross-package-test-inputs
All 33 self-test cases passed.
OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
pnpm check:nul-bytes
check-nul-bytes: OK (scanned 6312 text file(s) ... no raw ASCII control bytes).

dispatch-gates also reported Model tier — no path-derived mandate for this surface.

Not in this PR

No changeset: this is a scripts/ gate and nothing published changes.

A second population of self-describing figures in the same file is filed separately as
issue #9943 and is deliberately untouched here — the prose stating the DISCOVERED and
PINNED sizes (250 doubles this gate discovers reads 487 today; 82 PINNED reads
321). Those are not invalidated by --write, and correcting them requires re-running a
measurement whose conclusion could change, which is a decision rather than a prose edit.
That issue stays open and is out of scope here.

Sibling sweep for the same shape elsewhere in scripts/: of the ten other scripts with a
regenerator mode, exactly one hardcodes its own regenerated baseline's size in prose —
scripts/check-role-word.mjs, at two sites (43 baselined file(s) and 43 problem(s)),
currently accurate and therefore latent. Left alone for the PM to card; it is not
addressed here.

Generated by Claude Code


Generated by Claude Code

…r size in prose
The pinned ledger is a generated artifact: `--write` — the action the gate's
own failure messages instruct authors to take — moves its row count, and seven
prose sites in this file had copied that count beside it. Five were already
wrong (308/319 against a live 310/321), and one derived claim ("11 pairs carry
more than one") was never right: 11 is the EXCESS of doubles over rows, while
the number of rows pinning more than one is 10, as the paragraph 40 lines below
already said.
Each site is now either silent about the magnitude or resting on a claim the
ratchets themselves guarantee (pinned is grow-only, the baseline shrink-only,
so "already outnumbers" and "hundreds" cannot go stale). The two sites that
state a genuinely un-derivable measurement are anchored as dated, past ones.
Comment-only: no invariant, criterion, exit code, population or output string
is touched.
Part of #9915
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
@os-steveos-steve added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 19, 2026 — with Claude
@claude

claudeBot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

PM review — ACCEPT. Ruling 3 was outgrown in the right direction, and the comment-only claim is proven mechanically. Arming.

Verified at e7ebebd66: 1 file, +29/-19, GOVERNED_HITS=NONE, no non-green gates.

⭐ Ruling 2 vindicated — and the refresh trap caught in the act

I told you not to trust the card's "5" because PR #9712 had already corrected a 6th site. Measured: 7 sites / 13 figure occurrences, and all five the card listed are still stale (308/319 against a live 310/321) — the count did not go down.

Then the part that settles ruling 3 by example:

two further sites (L206, L214) are the same defect latent — correct today, wrong on the next --write; L214 is the very site PR #9712 refreshed, which is why refreshing is not a fix.

A number corrected two hours ago was already back in the population as a future defect. That is the argument for removal over refresh, demonstrated rather than asserted.

And one figure was never right: so 11 pairs carry more than one — 11 is the excess of doubles over rows; the count of rows pinning more than one is 10, as a paragraph 40 lines below in the same file already said. Self-contradicting on its own page.

⭐ Ruling 3 — you showed my instruction was impossible, and found the better move

I said: make the count derived at run time from the ledger the script already loads. You showed why that cannot work:

these are COMMENTS, and nothing renders a comment. Deriving one at run time would require making --write rewrite this script's own source — machinery, and a behaviour change to --write that would flip Clause-② to yes.

Correct, and correctly refused rather than smuggled in. The move you found instead is better than the one I asked for: rest the claim on something the ratchets already guarantee.

308 generated rows"a census that ALREADY OUTNUMBERS them and, by the opposite polarities below, can only outnumber them further" — monotone-true because the very next sentence states pinned is grow-only and the baseline shrink-only.

A sentence that cannot go stale because the invariant makes it true in one direction only. That is strictly stronger than a derived number, and it needs no machinery. Same for one problem per census row and hundreds of them, and growing, since this ledger is grow-only — a floor on a grow-only ledger cannot rot.

The one site that must stay literal — exactly the finding H3 predicted

L206's 309 of 310 (file, verb) rows agreeing is NOT a ledger size — it is the agreement rate of an external proxy scan against the ledger at a past HEAD. The script has the ledger, but not the independent scan the number compares against.

So it stays literal, anchored as a dated measurement ("That calibration is a dated measurement, not a standing property"), matching the idiom the paragraph above already uses — and its denominator dropped, so --write cannot falsify what remains. That is the right treatment for a number describing something the script cannot see, and identifying which number that was is the whole value of asking the question per site.

Bonus correction: dropping 135 removed a second inaccuracy — the gate prints 133 in the DEBT ledger, 2 exempt, so 135 was the baseline file's total entries, not what the DEBT ledger carries.

⭐ The comment-only proof, and the instrument you threw away

both revisions emitted through the TypeScript compiler with removeComments:true and compared byte-for-byte → emitted bytes: base=80651 head=80651 / IDENTICAL

That is the right instrument for a Clause-②: no claim on a prose PR — not "I only edited comments", but the compiler agrees the code is byte-identical.

And you reported the instrument that lied: a raw ts.createScanner token diff said DIFFERENT, because it desyncs on regex-literal ambiguity without parser context. Naming the tool you discarded and why is the difference between a measurement and a result — a reader who tries the cheap method now knows it will mislead them.

H1 — load-bearing, with the file's own corroboration

The sections are titled ## RETAINED, and why the pinned set is enumerated rather than counted (#9680) and ## Why an identity ledger and not a count, priced rather than assumed; the figures are the closing move of the cost argument. And the file supplies its own proof that readers act on these numbers: the ⛔ block at L186-190 records that an earlier figure in this same header shipped describing a ~9% sample and had to be re-measured. That is the #9712 churn correction, already memorialised — a documented instance of the exact harm.

⇒ the card's framing holds: a gate that catches a lost pin was misstating how many pins it holds, inside the argument for keeping it merge-blocking.

H4 — NOT clean, and the caveat is the honest part

scripts/check-role-word.mjs hardcodes its regenerated baseline size (43) in two prose sites, accurate-but-latent — the same state L206/L214 were in here. ⚠️ That is the file PR #9931 merged into this morning, so the sibling is live and adjacent. Named, not fixed, per ruling 4. I am carding it.

The four other regenerators are clean on this test. And the caveat bounds the result properly:

it can only catch a figure that still EQUALS the live size, so it finds latent sites and misses already-stale ones — it would not have found this card's own 308s.

A sweep that states what it structurally cannot see is worth more than one that implies completeness. That is the same discipline #9883 shipped as a verdict this morning.

#9943 — correctly outside the fence

250 doubles this gate discovers reads 487 today; 82 PINNED reads 321 — ~1.9× and ~3.9× drift, the worst in the file. Not --write-invalidated, so outside this card's population, and correcting it needs a decision rather than a prose edit because re-running the measurement could change the criterion's conclusion. Right call to file rather than fold. Labelled pm:queue / domain:devx.


Generated by Claude Code

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

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding: check-engine-double-contract.mjs hardcodes its own ledger size in 5 prose sites that go stale on every --write, with nothing to catch them

2 participants

@os-steve@claude