Skip to content

test(devx): sweep the shrink-only ratchet self-tests for baseline-presence pins — 33 derived, 1 repaired - #12050

Merged
yinlianghui merged 2 commits into
mainfrom
claude/issue-11694-selftest-present-ratchet-class
Aug 25, 2026
Merged

yinlianghui merged 2 commits into
mainfrom
claude/issue-11694-selftest-present-ratchet-class

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #11694

A shrink-only ratchet exists to be driven to zero. If its self-test pins the current
baseline entries as present — rather than a property of the gate that survives their
removal — the last commit of the burn-down turns the gate's own self-test red, and the
ledger becomes un-burnable by construction. #11692 repaired one instance; this is the
class sweep.

Result: population of 33 derived, 1 defect found and repaired, 32 unchanged.
Files changed: scripts/check-driver-memory-census.mjs — and nothing else.

1. The population, derived (not taken from the card's candidate list)

Both the card and its first comment say the candidate list is a lead. It was: none of
the six named candidates carries the shape
, and the one gate that does is not on the
list.

Definition used — a scripts/** gate that (a) enforces a baseline the project may only
shrink (a roster of entries, or per-file numeric ceilings), and (b) ships a selfTest().

Reproducible, in three passes:

# (a) every scripts/ gate shipping a self-test — the outer bound
grep -rlE '^(export )?(async )?function selfTest ?\(' scripts --include=*.mjs --include=*.mts | wc -l
#   → 133

# (b) of those, the ones whose SELF-TEST reads the real ledger/baseline (the only ones
#     that CAN carry the shape — a fixture-driven self-test cannot; discriminator 1)
#     grep the selfTest() body of each for: loadLedger|readLedger|loadBaseline|
#     readBaseline|loadCensus|readCensus|loadDebt|readDebt|readRoster|loadRoster
#   → 4:  check-cli-test-child-env.mjs        (readBaseline, l.865)
#         check-driver-memory-census.mjs      (loadLedger,   l.660)
#         check-optional-error-sink-contract.mjs (loadBaseline, l.1053)
#         check-test-typecheck.mts            (loadLedger,   l.516 — in main(), NOT in selfTest)

# (c) the in-file arm: gates whose ledger is a module constant rather than a JSON file,
#     scanned for assertions over that constant inside selfTest()
#   → the 29 roster/JSON candidates below, plus 4 numeric/registry ratchets the roster
#     regex missed (skills-token, pm skill-line, error-code-casing, i18n-coverage)

Derivation of the gate families at the final commit, as required:

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
  dispatch-gates: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 650639399
  --repo 'objectstack-ai/objectstack' checked against this checkout's 'origin' remote — it holds.
  change set derived from git — 1 path(s) vs merge base 1e79aa4f8

2. Per-item verdict — all 33

SURVIVES = the self-test's assertions still hold with the baseline emptied. Before ==
after for every row except row 6.

# gate baseline verdict
1 check-adr-0087-registration.mjs ADR-0087 registries SURVIVES — registry-vocabulary pins; LEDGER_SOURCES are fixture paths
2 check-adr-anchors.mjs KNOWN_NUMBER_COLLISIONS SURVIVES — ablation asserts red.length === numbers.length; reduces to 0 === 0
3 check-adr-links.mjs KNOWN_DEAD_TARGETS SURVIVES — staleEntries.length === 0; [].filter() is 0
4 check-auth-mount-ledger.mjs PENDING_DISPOSITION SURVIVES — every use is a fixture array
5 check-cli-test-child-env.mjs cli-test-child-env.baseline.json SURVIVES — the instance, repaired in #11692; re-verified here
6 check-driver-memory-census.mjs driver-memory-census.ledger.json DEFECT → REPAIRED (§3)
7 check-driver-conformance.mjs LEDGER SURVIVES — fixture-driven
8 check-durability-degradation-log-level.mjs 2 baselines SURVIVES — fixture-only self-test
9 check-engine-double-contract.mjs engine-double-contract.baseline.json SURVIVES — fixture-only self-test
10 check-entry-guard.mjs KNOWN_IMPORT_UNSAFE SURVIVES — asserts NON-membership
11 check-error-code-casing.mjs shrink-only code registry SURVIVES — self-test drives a fixture, by its own header
12 check-error-status-conformance.mjs error-status-unpinned-baseline.json SURVIVES — PRE_8963_* are frozen historical snapshots, not the ledger
13 check-i18n-coverage.mjs i18n-coverage-baseline.json SURVIVES — fixture/probe; ships an explicit emptyPopulationVerdict
14 check-optional-error-sink-contract.mjs optional-error-sink-contract.baseline.json SURVIVES — entry-shape loop; zero iterations at empty
15 check-plugin-teardown-shape.mjs KNOWN_TEARDOWN_UNREACHED SURVIVES — fresh/stale === 0; positive control is a git-pinned revision
16 check-published-readme-exports.mjs published-readme-exports.baseline.json SURVIVES — fixture-only
17 check-query-options-erasure-ratchet.mjs query-options-erasure-baseline.json SURVIVES — fixture-only
18 check-ratchet-remedy-authority.mjs CONTROL registry SURVIVES — a positive-control roster, not burnable debt. Rule untouched (#11935)
19 check-release-section-coverage.mjs PRE_10232_* SURVIVES — frozen snapshots used as positive controls
20 check-role-word.mjs role-word-baseline.json SURVIVES — its own summary already states "the same result once the ledger is empty"
21 check-settings-bind-window.mjs KNOWN_PRE_BIND_READS ([]) SURVIVES — auditSource(code, ledger = []). Spot-checked, §5
22 check-skill-compatibility-version.mjs EXEMPT SURVIVES — passed into fixtures
23 check-skills-token-ratchet.mjs CEILINGS SURVIVES — a ceiling map burns by lowering numbers, not by losing keys
24 check-startup-registry-verdict.mjs startup-registry-verdict.baseline.json SURVIVES — fixture-only
25 check-test-source-alias.mjs KNOWN_UNALIASED_TEST_IMPORTS SURVIVES — self-test asserts nothing about entry presence
26 check-test-typecheck.mts test-typecheck-debt.json SURVIVES — the loadLedger() call is in main(), not in selfTest()
27 check-type-check-coverage.mjs 5 ledgers SURVIVES — every self-test use is a fixture ledger literal
28 check-type-source-resolution.mjs KNOWN_DIST_RESOLVED_TYPE_IMPORTS SURVIVES — as row 25
29 check-where-matcher-conformance.mjs where-matcher-conformance.baseline.json SURVIVES — fixture-only
30 pm/dispatch-gates.mjs ESCAPABLE_LITERAL_LEDGER (empty) SURVIVES — the model of the repaired form: a witness pair keeps the positive half exercised at zero rows
31 pm/check-skill-line-ratchet.mjs CEILINGS SURVIVES — as row 23
32 check-slot-lookup-ratchet.mjs slot-lookup-baseline.json NO self-test — outside the repair surface. Recorded as an observation (§6). Rule untouched (#11681)
33 regen-artifacts.mjs 3 baselines NO self-test — a generator, not a gate

Zone-1 constraint honoured: rows 18, 13 and 32 are the gates whose rules #11935 / #11671 /
#11681 claim. None of them carries this shape, so not one byte of any of those three
gates changed
.

3. The one repair — scripts/check-driver-memory-census.mjs

Before:

expect('the ledger rules exactly the consumers #5704/#6664 named',
  (realLedger.ruledConsumers ?? []).length === 2);

The gate's own LIVE invariant says "a migrated consumer must lose its entry in the same
PR, or the ledger becomes the next stale census"
. That deletion is exactly what #5499's
frozen-driver retirement programme exists to produce — and this equality makes it
impossible: obey LIVE, and the self-test reds with a message reading "the ledger lost
an entry it must have"
when the entry was correctly deleted.

After: the claim the pin was actually protecting — no third consumer may be waved into
ruled-permanent under a marker of this programme's invention — which survives the set
shrinking to empty. validateLedgerShape already requires some rulingMarker; it did
not require the ruling to be one this programme issued.

const RULING_ISSUES = ['#5704', '#6664'];
const admittedByARuling = (e) => (e.rulingMarkers ?? []).some((m) => RULING_ISSUES.includes(m));
const ruled = realLedger.ruledConsumers ?? [];
// witness pair — `[].every(...)` is a pass that proves nothing, so the synthetic row keeps
// the positive half exercised at zero rows and the negative witness gives it teeth there.
expect('every ruled consumer is admitted by one of this programme\'s two rulings',
  [...ruled, WITNESS_RULED].every(admittedByARuling));
expect('…and that rule can FAIL: a marker neither ruling issued does not admit a consumer',
  !admittedByARuling(WITNESS_UNRULED));
expect('the ruled set never grows past what those rulings named (2 arm(s); it may shrink to empty)',
  ruled.length <= RULED_CEILING);

The anti-growth half the equality was also doing is kept as RULED_CEILING, a
shrink-only ceiling: lowered by any PR that migrates an arm away, raised only by a new
maintainer ruling. The witness-pair idiom is taken from ESCAPABLE_LITERAL_LEDGER in
scripts/pm/dispatch-gates.mjs, which already solved this at zero rows.

No rule changed. No assertion deleted. No exclusion added. The gate's production verdict is
byte-identical.

4. Reverse verification — three legs, each mutation confirmed on disk, restore on a trap

leg mutation (confirmed on disk) before repair after repair
A ruledConsumers 2 → 1 — the shrink the programme exists to perform EXIT 1 — x self-test: the ledger rules exactly the consumers #5704/#6664 named EXIT 0, 0 failure lines
B ruledConsumers 2 → 3 (third arm pointing at a file the scan really binds, so only the ceiling can fire) — EXIT 1 — x self-test: the ruled set never grows past what those rulings named (2 arm(s); it may shrink to empty)
C entry's rulingMarkers → ['#0'] — EXIT 1 — x self-test: every ruled consumer is admitted by one of this programme's two rulings

Leg A is the defect, measured on the unrepaired file. Legs B and C are the pin still
discriminating: it is not a pin that only ever answers "pass". Every leg printed
MUTATION CONFIRMED ON DISK from a ruledConsumers length / marker read before its
reading was taken, and the ledger was restored and re-read after each (git status clean).

5. Premise checks against origin/main

  • The card's "most urgent candidate" is falsified — as its own first comment already
    said, and it has not drifted since.
    check-settings-bind-window.mjs line 162 is
    const KNOWN_PRE_BIND_READS = [];, auditSource = (code, ledger = []), and both runs are
    green: ✓ settings bind-window guard self-test: all cases pass. /
    ✓ settings bind-window: 4 declared / 0 self / 1 structurally upstream / 0 ledgered (68 plugin unit(s) scanned…).
  • The other five named candidates are clean too — rows 18, 27, 17, 32, 13 above.
  • Discriminator 1 is the whole sweep. Of 133 self-tests in scripts/, only 3 read the
    real baseline from inside selfTest(). Everything else defaults its fixture ledger, so
    the empty path was never cold and the shape cannot be acquired.
  • Zone-3 secondary check (an empty ledger must not read as an empty population): no
    instance found.
    Every ratchet whose ledger is already empty still reports its scanned
    population — check-settings-bind-window 68 plugin unit(s),
    check-where-matcher-conformance 298 matcher(s) discovered … 0 grandfathered file(s),
    check-entry-guard 157 scripts/ file(s) … 2 known-unsafe.

6. Out of scope — recorded, deliberately not touched

  • The shape's habitat is not confined to scripts/ — reported, deliberately not swept.
    packages/spec/scripts/ and packages/lint/scripts/ carry shrink-only ratchets of their
    own (check-dual-source-exports.ts, check-exported-any.ts, check-liveness.mts,
    check-doc-formula-expressions.mjs), and two carry test twins under packages/**
    rather than a --self-test (check-generated-ledger.test.ts,
    check-react-blocks-declaration-parity.test.ts). One of those twins pins bucket counts as
    exactly 1 (expect(output).toContain('1 explicit manual-only generators')) — the same
    species. Widening the sweep is the PM's call, so it is measured here and not touched.
  • scripts/check-slot-lookup-ratchet.mjs is a shrink-only ratchet with no self-test at
    all
    , and its check:slot-lookup wiring invokes none. check-self-test-wired.mjs
    enforces only the superset ("a script that ships one must run it"), so this is invisible
    to it.

Verification

All at 650639399. Exit codes captured before any pipe; each line below is the gate's own
verdict.

gate exit verdict
check:driver-memory-census 0 check-driver-memory-census: OK — every declaration is ledgered, every ledger entry is live, and every ruled file states "#6664 census: 2 ruled consumers".
check:agent-test-spelling 0 green
check:cross-package-test-inputs 0 All 109 self-test cases passed. OK: 16 package(s) read outside themselves, all declared…
check:entry-guard 0 ✓ check:entry-guard: 157 scripts/ file(s) … 2 known-unsafe, ⛔ SHRINK-ONLY
check:parse-guard 0 green
check:pnpm-filter-targets 0 ✓ check:pnpm-filter-targets: 135/168 --filter occurrence(s) across 26 file(s)…
scripts/check-ci-filter-parity.mjs 0 OK: all 96 declared cross-package glob(s) (81 unique) are covered…
scripts/check-cross-package-test-inputs.mjs 0 OK: 16 package(s) read outside themselves, all declared…
check:nul-bytes 0 check-nul-bytes: OK (scanned 6672 text file(s) … no raw ASCII control bytes)
check:pm-dispatch-gates 0 ✓ dispatch-gates self-test: 579 cases pass.
check:where-matcher 0 ✓ where-matcher conformance holds: 298 matcher(s) discovered…

The last two are not in the derived list: dispatch-gates --residue flags them as the two
artifact-roster gates whose roster sits under scripts/, where a silent verdict "is not
evidence in EITHER direction". Run rather than assumed.

Repo-wide lint, not narrowed: eslint . --no-inline-config --format json — EXIT 0,
5066 files linted, 0 errors, 0 warnings
— run under the shared verify lock, not narrowed
and not skipped.

No changeset: this is root scripts/ gate tooling and publishes nothing. skip-changeset
applied.


Generated by Claude Code

claude added 2 commits August 25, 2026 06:45
… on its ledger's size

The self-test asserted `ruledConsumers.length === 2`. That holds only while both
ruled arms are still ledgered, so the gate's own LIVE invariant — "a migrated
consumer must lose its entry in the same PR" — could not be obeyed without
turning the self-test red, and red with a message reading "the ledger lost an
entry it must have" when the entry was correctly deleted. #5499/#5704 exist to
shrink that set; an equality on its size made it un-burnable by construction.

Re-spelled as the claim the pin was actually protecting, which survives the set
shrinking to empty: every ruled consumer is admitted by one of the two rulings
this programme issued (#5704 Q2 / #6664 A), carried on a witness pair so the
positive half stays exercised at zero rows; plus a shrink-only CEILING for the
anti-growth half that the equality was also doing.

Measured: shrinking the ledger by one entry was EXIT 1 before, EXIT 0 after;
growth past the ceiling and a marker neither ruling issued both still red.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
`#9999` reads as a real issue reference to the next author, and quoting it in a
PR body cross-references an unrelated card. `#0` is what this repo's other gate
fixtures already use for a marker that names nothing on purpose.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
@yinlianghui yinlianghui added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 25, 2026 — with Claude
@yinlianghui
yinlianghui marked this pull request as ready for review August 25, 2026 07:09
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 25, 2026
Merged via the queue into main with commit 7f6dc4c Aug 25, 2026
32 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-11694-selftest-present-ratchet-class branch August 25, 2026 07:29
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
…objectstack-ai#12973)

`key-mention.baseline.json` declares itself a SHRINK-ONLY RATCHET (objectstack-ai#11457) —
a finite debt ledger that exists to be driven to zero — and its twin pinned
the debt as PRESENT: `expect(doc.exemptions.length).toBeGreaterThan(0)` goes
red on the commit that deletes the last exemption, i.e. exactly when the
burn-down succeeds. Deleting the floor alone would not have been the repair
either: the `for (const row of doc.exemptions)` one line later asserts nothing
over an empty array, so it would trade a red for a silent no-op.

Split into the two claims the case conflated, per the PR objectstack-ai#12050 witness-pair
idiom:

  * the parser + row-quality rule, carried on a SYNTHETIC witness pair (a
    conforming row the parser accepts and the rule holds for; a structurally
    valid row that only reassures, which the rule refuses) — so both halves
    keep discriminating once the ledger reaches zero;
  * the shipped ledger, held to the same rule CONDITIONALLY on rows existing,
    with the read asserted independently of the row count so an empty ledger
    cannot read as a file that was never read.


Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants