Skip to content

fix(scripts): bind the dual-build floors' census to the ref it was measured on, and reconcile it in the open - #13234

Queued
os-elon wants to merge 1 commit into
mainfrom
claude/issue-13128-dual-build-provenance
Queued

fix(scripts): bind the dual-build floors' census to the ref it was measured on, and reconcile it in the open#13234
os-elon wants to merge 1 commit into
mainfrom
claude/issue-13128-dual-build-provenance

Conversation

@os-elon

@os-elon os-elon commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

Fixes #13128

1. The direction of the drift, measured before anything was touched

The card asked which way the gap ran and explicitly did not check. It was measured here, from the gate's own printed line (--list, which returns before the prerequisite check and so needs no build) run inside a detached worktree at each ref — never from a second enumerator.

tree the gate's own printed line recorded MEASURED
8cb96ec41b — the ref the record names 103 entries / 67 packages (the record did not exist yet)
55a53ee17b — where MEASURED landed 103 / 67 103 / 67 ✅
1a540e82b1 — the parent of PR #13123 103 / 67 103 / 67 ✅
a81aa9dd5e — today's main, fully built 102 / 66 / 610 / 1 103 / 67 / 613 / 1 ❌

⇒ The record was exact at the ref it names, and stayed exact for the whole of its life. Neither of the card's two hypotheses is right: nothing drifted away under it, and it was not written from a different tree state. The tree moved away from the record 2 hours and 21 minutes after the record landed.

The named cause, from the gate's own row sets differenced across those refs:

  • removed: @objectstack/cli#(main), @objectstack/plugin-hono-server#(main)
  • added: @objectstack/plugin-hono-server#.

PR #13123 gave @objectstack/cli an accurate ESM-only exports map. A package with no exports map publishes its main as a require entry point, so declaring one that has no require condition removes the package from this population: minus one entry, minus one package. The same PR converted plugin-hono-server from (main) to ., which is net zero — it was already counted.

Two corrections to the card, both measured

  1. The card's arithmetic on the pre-change tree is wrong. It reasoned that PR feat(cli,plugin-hono-server): declare exports maps, and make "a published package declares one" a gate #13123 adds an entry, so "the pre-change tree read 101 / 65 against a recorded 103 / 67". The pre-change tree read 103 / 67 — exactly the record. That PR replaced one of its own entries rather than adding one, and the whole minus-one came from the cli conversion. The gap was never 2 / 2; it is 1 / 1 and it did not exist when the card was filed.
  2. A2.1 is falsified on today's tree. The card's "not a build-state artifact" argument rests on "cjsFiles (613) and probes (1) match the record exactly". On today's built tree the gate reads 610 cjsFiles against a recorded 613. The conclusion survives, but on different and stronger evidence — the entry/package half was re-derived at the recorded ref itself and matched exactly. cjsFiles is the counter that moves with the build rather than with the source, and it is the one no reader can cheaply reproduce; both facts are now written down in the file.

2. What this changes

MEASURED was a bare tuple, and the ref it belonged to lived in four other places as a loose literal — the exact shape recorded in this family's older instances, where one figure restated across N prose sites drifted from the thing it described. So:

  • The ref moves INSIDE the frozen record. A count and the tree it came from can no longer be edited apart. The refusal message and the self-test regex now interpolate MEASURED.ref instead of restating it; the literal survives in exactly one other place, a dated past observation about the orphan-ledger fix that describes nothing current and cannot rot.

  • provenanceLine prints the census THIS run read beside the census the floors were derived from, and its ref, on every pass. This is the whole repair. The floors are inequalities on purpose, so no run can ever contradict the record — and without this line every green log looked identical whether the record still described the tree or not. Today it reads:

    ✓ check:dual-build-cjs-loads — 102 published require entry point(s) across 66 package(s) load; 610 emitted CommonJS file(s) parse; 1 cross-format behaviour probe(s) agree.
      provenance — entries/packages/cjsFiles/probes: this run 102/66/610/1 · floors 90/58/520/1 · derived from 103/67/613/1 measured on 8cb96ec41b (-1/-1/-3/= vs the record).
      ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the floors decide. Reproduce the record: see this file's header.
    
  • The header now carries what a reader auditing the floor actually needs: the reproduction recipe, verbatim; the asymmetry that entries/packages are source-only and reproducible at the ref with no install and no build while cjsFiles/probes need a full build AT that ref and are recorded on the author's word; and the mechanism above — this population shrinks for good reasons as well as growing, so a drop is not evidence of a broken walk.

  • Six self-test cases, none of which can red on a tree that legitimately moved. They red when the record stops being a self-contained reproducible claim.

3. No equality, and no band either — the width was derived, not guessed

An equality is ruled out and the card proved why. A band was the next thing to reach for, and it does not survive a derivation:

  1. The band already exists and is called the floor. MIN_ENTRIES is MEASURED.entries minus the headroom this gate declared (13). A second, narrower band is a second tolerance for one fact, and its width would be invented.
  2. No width measures anything. Measured today, the three gates in scripts/ that carry a record of this shape had drifted -1, -5 and +15 from theirs, in both directions, within days of landing. A band narrow enough to notice this file's -1 reds on the +15 next door; one wide enough to survive the +15 cannot see a -1.

A2.4 confirmed, and sharpened. "Record the ref" is a real improvement and not one more thing to rot — I reproduced the record from its ref in about three minutes, which is exactly the claim. But the ref was already recorded here and the file was still the card's example, because the ref was detached from the counts and a green run never showed the reader the comparison. A ref rots differently from a count only while it stays bound to it.

4. The sweep — 3 gates, this one included, and all 3 already drifted

⛔ Measurement only; nothing outside this file is touched.

Criteria, so the count is reproducible: the script (a) enforces vacuity floors as inequalities over a live census, (b) records in source the census those floors were derived from, and (c) has nothing that reconciles the record against the tree. 15 scripts under scripts/ declare a MIN_/FLOOR_/MAX_/CEIL constant; 3 meet all three criteria.

gate recorded ref live, from its own verdict line drift
scripts/check-dual-build-cjs-loads.mjs 103 / 67 / 613 / 1 8cb96ec41b 102 / 66 / 610 / 1 -1 / -1 / -3 / =
scripts/check-keyed-text-bounds.mjs 113 / 118 / 255 / 594 / 151 fa5d137ab0 112 / 117 / 251 / 589 / 148 -1 / -1 / -4 / -5 / -3
scripts/check-undeclared-dep-imports.mjs 78 / 2057 / 1805 aef1b7e6 78 / 2072 / 1819 = / +15 / +14

Two near neighbours are not in the class and are worth knowing, because each already implements one of the honest routes: check-docs-section-name.mjs records no number at all and delegates provenance to a re-derivable command ("Measured on the tree this landed against: see --list"), and check-label-desc-cap.mjs declines to record the count and says why in the constant's own docblock.

5. Ablations — prediction named before each run, mutation and restore both proven on disk

No dist/ is involved: the gate is executed from the very source file mutated (node scripts/check-dual-build-cjs-loads.mjs), so there is nothing to rebuild and nothing stale to read. Each leg proved the mutation with decomposed grep counts on the text that was meant to move plus git hash-object against the HEAD blob, and refused to take a reading when either failed to move. Restore is git checkout HEAD -- against an absolute path (never the bare form, which restores from the polluted index), proven by hash equality with the HEAD blob and a zero-byte git diff HEAD; an absolute-path trap ... EXIT INT TERM covers the crash path.

ablation predicted observed
A1 — blank the recorded ref 1 case reds; the two includes(MEASURED.ref) cases pass vacuously because includes('') is always true, which is why the well-formedness case is their precondition exit 1, exactly 1 of 59: the record carries the ref it was measured on
A2 — restate the ref as a literal in the refusal, then move the record's ref 2 cases red exit 1, exactly 2 of 59: the refusal-quotation pin and the floor-refusal regex, both built from the record
A3 — delete the pass path's call 1 case reds — this is the pin that closes the function-tested-but-never-called gap exit 1, exactly 1 of 59: the PASS path actually prints it

The A3 pin had to be written twice. Its first form spelled the needle as a string literal, so the assertion found itself in the source and stayed green with the call site deleted — the phantom check this whole card is about, reproduced inside its own repair. The needle is assembled at runtime now, and A3 is what proves it.

6. Verification

All at 7871ff3f49, the final commit. Every exit code captured before any pipe.

  • pnpm check:dual-build-cjs-loadsexit 0; 59 self-test cases pass (53 before this change), gate green with the provenance line above.
  • Derived family, node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (exit 0, derived at 7871ff3f49 vs merge base a81aa9dd5, 1 path): all 13 green — check:agent-test-spelling, check:bash32-floor, check:cli-command-ids, check:cross-package-test-inputs, check:dual-build-cjs-loads, check:entry-guard, check:parse-guard, check:pnpm-filter-targets, check:watch-hint-literal, check-ci-filter-parity.mjs, check-cross-package-test-inputs.mjs, check-shard-attestation.mjs.
  • Convention pair for editing a gate script, both green: scripts/pm/bare-root-worklist.mjs --self-test (exit 0 — 43 recorded verdicts, none stale, none missing, none contradicted) and pnpm check:pm-dispatch-gates (exit 0, 879 cases).
  • node scripts/check-test-completeness.mjsexit 3, PREREQUISITE NOT MET, no test log named. A refusal to measure, recorded as NOT MEASURED; the gate's own text notes CI always passes it a log, so that branch is unreachable there.
  • pnpm build — exit 0, 71 tasks, under the shared verify lock.
  • pnpm lint (repo-wide eslint . --no-inline-config) — exit 0. Run whole; no narrowing claimed.

No changeset: this is a CI-internal gate script and releases nothing, which lint.yml names as the textbook skip-changeset case. The label is applied on this PR.

Generated by Claude Code


Generated by Claude Code

…asured on, and reconcile it in the open

The recorded `MEASURED` census (103/67/613/1) was exact at `8cb96ec41b` and
stayed exact until an ESM-only `exports` map removed `@objectstack/cli` from
the population. The floors are inequalities on purpose, so nothing could ever
contradict the record: the provenance of a floor quietly stopped matching the
tree it was measured from, and no run said so.

- the ref moves INSIDE the frozen record, so a count and the tree it came from
  cannot be edited apart; the refusal and the self-test read it from there
  instead of restating it (it was spelled in four places)
- `provenanceLine` prints the census THIS run read beside the census the floors
  were derived from, and its ref, on every pass — the reconciliation the floors
  cannot do without going red on legitimate movement
- the header records how to reproduce the record, which two of the four
  counters are cheap to reproduce and which two are not, and why the population
  shrinks for good reasons as well as growing
- six self-test cases: the ref is well-formed, the refusal and the line quote it
  from the record, drift renders in both directions, the delta is marked as
  information rather than a verdict, and the pass path actually calls the line

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants