Uh oh!
There was an error while loading. Please reload this page.
feat(qa-checklist): flag an area-level recipe no item references (#11506) - #11847
Conversation
`check:platform-checklist` resolved `fixtures.provisioning.use` forwards only. The reverse direction — a recipe nobody opts into — was deliberately deferred while cross-area reuse had no spelling: flagging it then would have settled that open convention by accident, in the direction of "recipes are area-local". The maintainer's option-A ruling of 2026-08-22 (#10593 gap 2, landed in #11508) discharged that reason. Every legitimate consumer can now express itself as a `use` from any area, so a recipe with no `use` pointing at it is unambiguously dead text rather than possibly-referenced-from-prose. Three definitional edges are decided rather than left to the reader: - a reference from a `retired` item still counts. The forward direction runs on every item regardless of status, so a retired item's `use` must resolve and its recipe must exist; an active-only reading would make the two directions mutually unsatisfiable the moment a recipe's last consumer retired; - the direction is suppressed while any `use` dangles, so one typo cannot also accuse the correct recipe it was aiming at; - no waiver spelling is invented — the population needing one is zero, and the failure names the two remedies that exist (give it a consumer, or delete it). All four recipes on the ledger are referenced by seven items, so this direction's subject population is zero and stays that way on a healthy ledger. That makes its green uninformative on its own, so the 19-assertion positive control runs inline on every invocation, like the trap-vocabulary and resolve controls beside it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
os-steve
commented
Aug 24, 2026
ACCEPT. Verified by content on ⭐ You verified the premise first, and it holdsI made the expired-deferral check the lead requirement because "a deferral whose stated cause is gone is a different thing from a deferral someone forgot about." You established the cause, and its discharge is in the history: Plus the three in-tree confirmations, all of which I reproduced: ⭐ And you found a second expired warning the card carried: it said the gate was red on The zero is the interesting part, and you drew the right conclusion from itPopulation: 4 recipes / 7 references / 0 unreferenced. A lesser answer would have shipped the check and called the green a pass. Yours:
So you shipped the direction and a 19-assertion inline positive control, matching the file's existing 22 trap-vocabulary and 34 provisioning-resolve controls. That is the correct response to an empty population, and it is the difference between a gate and a decoration. The three definitional edges⭐ The retired-item one is the best reasoning in the report. You did not decide it by preference; you showed an active-only reading makes the two directions mutually unsatisfiable the moment a recipe's last consumer retires — escapable only by editing a retired item, which the append-only lifecycle forbids. A definition that can be forced by an impossibility is settled, not chosen. And the suppression edge is the Zone 1 guard I set, met by construction: while any AblationsB is the one I asked for: misspelling a real C reproduced the card's motivating scenario end to end: repointing consumers one at a time, green while one remained, then red reported against the area that owns the recipe though the triggering edits were in different files. That is cross-area accounting demonstrated, not asserted. Existing direction proven unchanged by the gate's own verdict line field-by-field, with both pre-existing self-check counts intact and only additions. ⭐ Two self-corrections, both worth more than they cost
That is #11824's defect biting inside a verification harness — a broken run misparsed into a false finding — caught by reading the real output rather than your own summary table. Two hours after that fix landed, here is the exact failure it exists to prevent, in the wild. And the ablation harness's shell-quoting bug in an ⛔ Zone 1 held
(My raw count of 3 did not distinguish prose from wiring — a reminder that a grep count is not a reading until you look at what it counted.) Ruling on your open question: A, ship with no waiver.Your reasoning is complete and I am confirming rather than merely accepting it. The decisive point is the one about reviewability: a waiver with no real case has no standard to judge it against, so the first use of it would set the standard by accident — which is exactly how this card's original deferral happened. And a waiver field is the surface an author reaches for to silence a gate, so adding one before a case exists makes the gate weaker for no measured benefit. B is a guess at what the first real case will want, written before that case exists to constrain it. C leaves the gate's own comment documenting an expired deferral indefinitely — the shape that created this card. Nothing here forecloses #10885 relocating the concept later. Flipping to ready. Arming once every check is green. Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#11506
check:platform-checklistresolvedfixtures.provisioning.usein one direction only. This adds the other: every area-level recipe must be named by some item'suse. A recipe nobody opts into is dead text a runner may still replay.The card's load-bearing claim, verified first
The card rests on "the reason it was deferred has expired". It has — established before any code was written, not assumed.
The reason. While cross-area reuse had no spelling, a recipe whose only consumer lived in another area could be referenced only from that item's
knownGapsprose, invisible to the gate. Flagging an unreferenced recipe then could not distinguish dead text from a recipe whose consumer could not say so, and reddening it would have settled the open cross-area convention by accident, toward "recipes are area-local".What discharged it. The maintainer ruled option A on 2026-08-22 (#10593 gap 2), landed by #11508. Three independent confirmations in this tree, not recollection:
parseUse, two-level lookup)scripts/check-platform-checklist.mjs"use": "search:qa-contributor-bound-member"areas/records-forms.json:23areas/search.json:52So every legitimate consumer can now express itself as a
use, from any area. A recipe with nousepointing at it is no longer possibly-referenced-from-prose. The gate's own comment and the README already said the reason had expired and pointed here; both are now rewritten to describe a direction that exists.The population, and what that dictated
Four recipes, seven referencing items, zero unreferenced — and all seven referencing items are
active:The subject population is zero and stays zero on a healthy ledger. That did not argue for a self-test instead of a check — the invariant is real going forward, and the card names the trigger precisely: a recipe goes unreferenced when its last consumer is retired, which is an edit in a different area file. What zero dictated is that a check alone would be worthless: its output is permanently empty, so its green cannot distinguish "working" from "deleted". So this ships both — the direction, and a 19-assertion positive control that runs inline on every invocation (the pattern the trap-vocabulary and resolve controls beside it already use). Here the control is not a safeguard on top of the real subject; it is the only subject.
Is "unreferenced" crisply definable? Yes — with zero exceptions needed
Measured rather than assumed. Grepping all four recipe names across the whole repo returns only the gate, the README and four area files — no run records, no skills, no other surface. Three edges, each decided in the gate's comment rather than left to the reader:
What counts as a reference — a resolved
fixtures.provisioning.use, nothing else. Recipe names do appear in prose (automation.jsonandsearch.jsonboth open with "Shape copied from qa-scratch-authz"), and that is provenance, not consumption. Counting prose would restore the invisible pointer the qualified spelling exists to retire.Retired items count. "Referenced" means referenced by any item. This is mechanical, not a convention call, and the forward direction forces it:
provisioningProblemsruns on every item regardless ofstatus, so a retired item'susemust still resolve, so its recipe must still exist. Under an active-only reading, a recipe whose last consumer retired would be flagged here, and deleting it to clear that flag would dangle the retired item'suseand red the forward direction — two checks in one gate made mutually unsatisfiable, escapable only by editing a retired item, which the append-only lifecycle forbids. Pinned as R11.Suppressed while any
usedangles. An unresolved reference means the consumer graph is incomplete, so "nobody references this recipe" is not yet worth saying — the intended consumer may be the broken reference. Without this, one typo prints twice: once against the item that has it, and once against the perfectly correct recipe it meant to name. Pinned as R12/R13, and proven on real data in ablation B below.Exception-list size: zero. No waiver spelling is invented, deliberately — the population needing one is zero, and a waiver field guessed ahead of its first real case guesses what that case wants. The failure names the two remedies that exist today: give the recipe a consumer, or delete it. A genuine keep-it-anyway case is the moment to decide the spelling, on that case's evidence; #10885 (recipes carry no
revision/history) may well answer it as a retired recipe rather than a waived one, and nothing here forecloses that.Non-vacuity — both readings on the record
All ablations restore under
trap … EXIT INT TERM, assert the anchor match count in-process (a zero-hit edit aborts loudly instead of reading as a clean ablation), prove the mutation landed by sha change, and prove the restore byte-identical.A — does it fire? Injected one genuinely unreferenced recipe into
areas/automation.json.B — Zone 1 guard: a typo must not accuse the correct recipe. Misspelt one real
use(qa-media-constraintstoqa-media-constraint).Only the dangling reference is reported.
qa-media-constraints— a correct recipe — was not accused. Suppression held on real data. Restore byte-identical.C — cross-area accounting on real data, two stages.
qa-contributor-bound-memberhas two consumers, one in another area.records-forms.crud-roundtrip) elsewhere: GATE_EXIT=0, still green, becausesearch.rls-both-personasstill references it.search.json, the area that owns the recipe, though the triggering edits were in other files. That is exactly the card's motivating scenario — a recipe going unreferenced by an edit in a different area file — reproduced end to end.All three files restored byte-identical; gate green again.
The existing direction is unchanged — by its verdict line, not by inspection
Baseline captured on this same tree at
e47d5ef61before any edit, compared field by field against the branch:Every pre-existing figure is untouched; the new figures are additions only. The
4/4is counted from the reference map rather than restated fromrecipeTotal— a line that restates a constant reports nothing, and this direction's whole risk is a green that looks the same whether it ran or not.Verification
Union re-run at final head
ee7ade76d, all throughscripts/pm/os-verify-lock.sh. Exit codes captured by redirect-then-capture, never through a pipe.Gate family re-derived from the committed diff with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(it reads its own change set; no hand-built path list) — same 8 families, no additions.check:doc-formula-expressionsfirst reportedPREREQUISITE NOT METfor@objectstack/formula, then for@objectstack/lint; both built as instructed and re-run green. Per #11824 that message is a prerequisite, not a finding.Repo-wide
pnpm lintnarrowed, declared as a narrowing with its three measurements: (1) population read from eslint's own config —isPathIgnoredreportsREADME.mdignored ("File ignored because no matching configuration was supplied") and the.mjsin scope, so one of the two files is eslint's whole population here; (2) file count from--format json— 2 entries, 0 errors; (3) config invariance —eslint.config.mjsstates in its own words that this repo "never enables type-aware linting (noparserOptions.project, no typed@typescript-eslintrules) for ANY file", measured there with a positive control, so this diff cannot move the verdict on any untouched file. CI runs the full farm regardless.Scope notes
check:platform-checklistis still not CI-wired, deliberately untouched — that is the maintainer's decision and [Decision]check:platform-checklisthas no reporting channel — it is deliberately not CI-wired, so its red is visible to nobody until a runner reads it #11730's open question..github/workflows/lint.ymlis not in this diff. So this direction, like every other one in this gate, reds at the next manual run rather than on the PR that breaks it.scripts/) plus the internal QA ledger's README (docs/qa/). Nothing published or user-visible changes, so the PR carriesskip-changeset.fixturesrecipes carry norevision/history, so a semantic recipe edit silently re-validates every run record that depends on it #10885 is cited and deliberately not pre-empted.Generated by Claude Code