Uh oh!
There was an error while loading. Please reload this page.
Give check:slot-lookup a --self-test, and wire it like every sibling ratchet - #12154
Conversation
…ry sibling ratchet The #4251 sweep ratchet was one of two shrink-only ratchets under scripts/ with no selfTest() at all, and the only gate among them. Its verdict is the emptiness of a finding set, so weakening its rule can only shrink what it reports and the production run cannot tell a swept tree from a broken rule. The self-test drives the REAL rule (eslint.config.mjs, through the same measuring config the production run builds) over synthetic sources in both directions, plus the ratchet comparison and both refusals, which no clean tree reaches. The silent half pins the rule that EXISTS: TYPED, never COMPLETE — #11681 was closed not_planned, so a narrow per-consumer interface must stay silent here on purpose. scripts/slot-lookup-baseline.json is untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
Both files state that `pnpm check:slot-lookup` has no --self-test hook — a premise for why the guard-adoption walk in check-query-options-erasure-ratchet's self-test is the only coverage of the other gate's call site. The wiring change in this PR makes the first clause false while the conclusion stays true, so the sentences are re-spelled rather than deleted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
Uh oh!
There was an error while loading. Please reload this page.
yinlianghui
commented
Aug 25, 2026
Dequeued — base-branch failure, not this PR's. Nothing pushed here.
This diff is ⛔ Filed already by two other seats — #12180 ( Waits for Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#12052
scripts/check-slot-lookup-ratchet.mjs— the #4251 sweep ratchet — was one of twoshrink-only ratchets under
scripts/with noselfTest()at all, and the only gateamong the two. Its wiring carried no self-test invocation either. This adds the self-test
and wires it the way every sibling ratchet spells it.
scripts/slot-lookup-baseline.jsonis untouched — not one entry. Verified in the diff.Why the wiring gate could not see the gap
scripts/check-self-test-wired.mjsenforces the mechanically decidable superset — everyscript CI runs that ships a
--self-testmust have that self-test run by CI too — so agate shipping none is outside it by construction. Both verdicts, quoted from the gate's
own output:
The gate's population moved by exactly one (143 → 144 carrying, 131 → 132 run, 127 → 128
through the flag): this gate is now inside its superset. And it is now enforced — with
the script's
--self-testin place but thepackage.jsoninvocation reverted, the wiringgate goes RED naming this script (
[self-test-not-run] scripts/check-slot-lookup-ratchet.mjs,exit 1). Before this PR the same tree was green.
The rule the self-test pins — TYPED, never COMPLETE
check:slot-lookupbans erasing a service lookup toany. It does not check that thetype you name describes the object you call. #11681 recorded exactly that gap and was
closed
not_planned(2026-08-25): a completeness check has no ground truth for thedeliberately narrow per-consumer interfaces the #4251 B4 decision asks for on an OPTIONAL
slot. So the boundary is pinned as a case that must stay silent, deliberately.
The fixture (written out in words, because GitHub's body sanitizer eats short
angle-bracket fragments): an
interface SettingsReadSurfacedeclaring exactly one member,get(key: string); actx.getService(...)lookup on slot'settings'carryingSettingsReadSurfaceas its type argument; then a call tos.getMany([...])— amember the named type does not declare. The gate must stay SILENT on it. The literal
source is in
scripts/check-slot-lookup-ratchet.mjs, in theSILENTtable, under"a narrow per-consumer interface stays silent even where the call exceeds it".
A case asserting completeness would pin a rule this gate does not have, and would go red
the day someone read it as a bug.
What the self-test drives — the real rule, never a re-implementation
The fixtures are linted through the live
eslint.config.mjsrule, via the samemeasuringConfig()the production run builds, and counted through the samecountRuleHits()predicatemeasure()uses. There is no second copy of the rule and nosecond copy of the counter.
slot-lookup/no-any-assignmentplugin rule for the split declaration, and the thirdlookup name
getRequestKernelService).boundary above; both
UNCONTRACTED_SLOTSexemption channels;getObjectQL; an unrelatedas any; the typed split declaration the sweep produces).source, same config, one
ignoresentry apart — so the pin survives the baselineshrinking to zero entries (test(devx): sweep the shrink-only ratchet self-tests for baseline-presence pins — 33 derived, 1 repaired #12050's discipline). The real baseline is checked too, when
it still has an entry to check.
diffRatchet().ruleBlockProblem,populationScopeProblem), each in bothdirections — neither is reachable from a healthy tree at all.
Ablations — both directions, each confirmed on disk, each restored by a trap
Every leg grepped the injected and the deleted text before and after the mutation
(counts below), and restored through
trap … EXIT INT TERM.--self-testpackage.json: drop the--self-test &&invocationcheck-self-test-wiredRED (exit 1)SLOT_LOOKUPS = ['resolveService']onlyTSAnyKeywordtype-argument test relaxed to any type argumentdiffRatchet(): delete thecount > allowedbranchA1 reddens the report cases (
getService/getRequestKernelServiceshapes plus thegrandfathering pair); A2 reddens a disjoint set — the two typed-silent cases, including the
#11681 boundary case — so neither set is green under both mutations.
A3 is the load-bearing reading. Deleting a comparison branch left
node scripts/check-slot-lookup-ratchet.mjsprinting its usual verdict —✓ slot-lookup ratchet holds: 107 unswept site(s) in 25 file(s) …, exit 0 — while--self-testwent red naming the case. That is the class of regression this gate had noinstrument for, and it is now measured rather than argued.
The header states the one place the blindness is not uniform, rather than overclaiming:
because a fall below the baseline is itself an error here, a weakening broad enough to erase
live sites reddens production too (A1). What that does not cover is a weakening over a shape
with no live sites, an over-fire,
diffRatchet(), or either refusal.Restructure, and why it is the shape it is
The script was one top-level program. It is now
measuringConfig()/countRuleHits()/measure()/ruleBlockProblem()/populationScopeProblem()/diffRatchet()/baselineKeysAddedSinceMergeBase()/main(), matching its structural twincheck-query-options-erasure-ratchet.mjs. Behaviour, output text, exit codes and--updateare unchanged. Nothing is exported (socheck:entry-guard's import-safety ruleis not newly reached),
POPULATION_GLOB's literal spelling is untouched (sodispatch-gates.mjs's hint extraction and its pins still read it), and the counted textlints go through
lintTextStrict()(so the #10599 call bans stay satisfied — verified bycheck:query-options-erasure's adoption walk, which runs over both gates).One bounded in-place repair, named here with its evidence
Two prose statements elsewhere assert "
pnpm check:slot-lookuphas no self-test hook" —scripts/eslint-fatal-guard.mjsandscripts/check-query-options-erasure-ratchet.mjs.This PR falsifies the first clause of each while their conclusion (the guard-adoption walk
in the sibling's self-test is the only coverage of this gate's call site) stays true, so
both sentences are re-spelled rather than deleted. Comment-only; both files' gates run
green below. Scan for the class:
grep -rn 'check:slot-lookup'over the tree — those twowere the only occurrences making a self-test claim.
Verification
All at the final commit
9fe3f66d5. Exit codes captured before any pipe; the verdict linesare the gates' own.
The 9 families
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackderives for this diff, all exit 0:
check:agent-test-spelling,check:cross-package-test-inputs,check:entry-guard,check:parse-guard,check:pnpm-filter-targets,check:query-options-erasure,check:slot-lookup,check-ci-filter-parity.mjs,check-cross-package-test-inputs.mjs— pluscheck:self-test-wiredandcheck:nul-bytes.Repo-wide
pnpm lintwas narrowed, and the narrowing is declared rather than implied.Run:
eslint --no-inline-config --format jsonover the three changed.mjsfiles →3 files linted, 0 errors, 0 warnings (count read from ESLint's own JSON, and all three
came back as linted results rather than ignored). The narrowing is sound because this repo
enables no type-aware linting —
project:appears nowhere ineslint.config.mjs— so achange confined to
scripts/*.mjscannot move the verdict on any file it does not contain.CI runs the full farm regardless.
No changeset: root
scripts/+package.jsononly, no published-package source change ⇒skip-changeset.Generated by Claude Code