Uh oh!
There was an error while loading. Please reload this page.
feat(qa-checklist): resolve an area-qualified provisioning.use (#10593 gap 2) - #11508
Merged
Merged
Conversation
A checklist provisioning recipe is proved by one area and sometimes needed by a second. Until now `fixtures.provisioning.use` could only name a key in the item's OWN area, so a cross-area consumer had one option left: cite the recipe by name in `knownGaps` prose. Prose is the one form that cannot drift-check — rename the recipe and `search.json` moves while the sentence in `records-forms.json` does not, silently. `use` now has two spellings and the gate resolves both: "qa-scratch-authz" a recipe of the item's own area "search:qa-contributor-bound-member" "<area>:<recipe>", any area Option A on the second gap of #10593, ruled by the maintainer on 2026-08-22 over a shared `areas/_fixtures.json` and over "recipes stay area-local, and a cross-area consumer duplicates". Every reference already written keeps working unchanged — the qualifier is an addition, not a migration, and the self-test pins that. The lookup is two-level, so the file is now walked twice: the recipe universe has to be complete before any item is judged, or whether a cross-area reference resolves would depend on readdir order. Recipes are keyed by FILENAME stem rather than `doc.area`, so a qualified reference cannot resolve through the very area/filename mismatch the gate reports one line down. A two-level lookup can also fail by resolving too MUCH, which no "it resolves" assertion can see, so all three failure shapes are pinned firing and pinned distinguishable: a real area missing the key, a missing area, and a malformed reference (`a:b:c`, `search:`, a padded half). A bare key that some other area defines is now told the exact qualified spelling to write. Self-test: 36 -> 56 assertions. Converts the two cross-area prose pointers where they already sit: `records-forms.crud-roundtrip` clause 7 opts into the recipe `search.json` owns, and that recipe's own `knownGaps` stops saying no cross-area spelling exists. Both consuming items take a revision bump and a history entry, per the lifecycle rule and the precedent set when the recipe text last changed. The unreferenced-recipe direction stays unchecked. Its stated reason has expired — with a qualified spelling, an unreferenced recipe is unambiguously dead text — so the deferral is now tracked at #11506 rather than resting on a justification that no longer holds. Note: `check:platform-checklist` is deliberately not CI-wired, so the local run is the only signal this gate gets. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
This was referenced Aug 24, 2026
os-steve
marked this pull request as ready for review
August 24, 2026 02:13
Uh oh!
There was an error while loading. Please reload this page.
os-steve pushed a commit
that referenced
this pull request
Aug 24, 2026
`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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#10593
Gap 2: cross-area recipe reuse gets a spelling. Implements the maintainer ruling of 2026-08-22 — option A, area-qualified references — over option B (a shared
areas/_fixtures.json) and option C (recipes stay area-local, cross-area consumers duplicate). The ruling is not re-litigated here.The problem
A provisioning recipe is proved by one area and sometimes needed by a second. Until now
fixtures.provisioning.usecould only name a key in the item's own area, so a cross-area consumer had exactly one option left: cite the recipe by name inknownGapsprose. Prose is the one form that cannot drift-check — rename the recipe andsearch.jsonmoves while the sentence inrecords-forms.jsondoes not, silently and with nothing red.What changed
usenow has two spellings, and the gate resolves both:The area half is the filename stem. Every reference already written keeps working — the qualifier is an addition, not a migration, and the self-test pins that as a regression, not as an aspiration.
Two implementation notes worth review attention:
readdirorder.doc.area. The two must agree, and that agreement is itself checked one line down — keying by a mismatcheddoc.areawould let a qualified reference resolve through the very inconsistency the gate reports.Per the dispatch, the resolver stays inside
check-platform-checklist.mjs; no new module underscripts/(that would movedispatch-gates' family universe, which #11190 is measuring across this same round).Non-vacuity — this is the part to review
A two-level lookup has a failure mode the one-level one did not: resolving too much. No "it resolves" assertion can see that, so every limb is observed failing as well as passing. Self-test: 36 → 56 assertions.
Ablation A (the #10810 precedent):
provisioningProblems()neutered to return early. Predicted 27 of the 34 provisioning assertions red — U4–U13 and Q4–Q20, every case asserting the check fires; U1/U2/U3/Q1/Q2/Q3 assert zero problems and U14 exercisesareaRecipeKeysonly. Observed exactly 27, exactly that set, and the full gate refused withthe provisioning-resolve check's own positive control FAILEDrather than going quietly green. Mutation confirmed on disk by anchor count (marker 0 → 1 at line 307) and sha256, never by the editor's exit code; restored byte-identically via anEXIT INT TERMtrap (sha back to3fa7b602…, marker count 0, cleangit status).Ablation B — the sharper one, because it tests the code/data pairing rather than the code alone. The gate was swapped back to
origin/main(confirmed on disk: sha39d95f98…,recipesByAreaoccurrences 10 → 0,git diff --quiet origin/mainclean) while keeping this PR's data change. The base gate reds on exactly the converted item:So the green below is produced by the new two-level lookup and by nothing else, and the data change genuinely requires the code change. Restored byte-identically.
All three failure shapes reproduced on the live corpus (each injection confirmed present on disk before the reading was taken; all reverted by trap):
usesearch:qa-recipe-nobody-wrote…not a recipe in area "search"'s area-level "fixtures" block — \search` offers `qa-contributor-bound-member``serch:qa-contributor-bound-member…whose area half names "serch" — there is no such area file — did you mean \search`?`search:qa:member…not a usable reference: it carries 2 ":" separators…search:…not a usable reference: its recipe half is empty…qa-contributor-bound-member(unqualified)…\search` defines it: write `search:qa-contributor-bound-member` … and do NOT fork a second copy into this area.`That last row is the upgrade that makes the drift self-correcting: an author who copied an unqualified key across areas is handed the exact spelling that works.
The two prose pointers, converted where they sit
records-forms.crud-roundtripclause 7 now carriesuse: "search:qa-contributor-bound-member"; itsknownGapsand clause-7verifytext stop saying the reference cannot be spelled.search.json's recipeknownGapsstops saying no cross-area spelling exists, and now describes a recipe with consumers in two areas, both machine-resolved.Both consuming items take a
revisionbump and ahistoryentry. Forsearch.rls-both-personasthat follows the precedent its own revision 5 set: area-level recipes carry norevision/history, so a consuming item's revision is the only pin the recipe text has. What a run does is unchanged in every particular — same recipe, same file, same clause, same object and row; no step, clause, oracle or persona changed.1.
check:platform-checklistis not CI-wired, by maintainer decision recorded at.github/workflows/lint.yml. That decision is honoured, not changed. The local runs in this PR are the only signal this gate will ever get — a greenLint & Repo Gateson this PR says nothing whatsoever about it, and a typo'duseis caught at the next manual sweep rather than on the PR that introduces it.2. The gate is already red on
main, for a reason this PR does not touch. Measured on pristineorigin/main@63da5867before any edit:coverage.json · manifest: UNCLASSIFIED.packages/spec/liveness/manifest.jsonlanded 2026-08-23 (#11338) without a matchingcoverage.jsonentry — 31 liveness kinds against 30 coverage entries. Already filed as #11421; nothing here is a fix for it, and it is the second-order cost of (1) showing up in practice. This PR's contribution to the problem list is zero: base and branch print the identical single problem.With that one unrelated problem temporarily waived as a measurement (reverted;
coverage.jsonsha256 unchanged), the gate goes fully green and reports the census:Gates
All on the final commit
4638ae27, exit codes captured before any pipe, each verdict quoted from the line the gate printed itself. Derived withnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no paths passed — it takes the change set from the merge base), re-derived on the final commit, same 8 families both times.check:cross-package-test-inputsOK: 14 package(s) read outside themselves, all declared…check:doc-authoring✓ doc authoring guard: 389 files clean--filter @objectstack/lint check:doc-formula-expressions✓ … 22 record-scoped formula example(s) across 420 files / 1449 TS blocks judged cleancheck:entry-guard✓ … 139 scripts/ file(s) — every entry guard goes through invoked-as.mjscheck:parse-guardcheck:pnpm-filter-targets✓ … 120/148 --filter occurrence(s) … resolvescripts/check-ci-filter-parity.mjsOK: all 89 declared cross-package glob(s)…scripts/check-cross-package-test-inputs.mjsOK: 14 package(s) read outside themselves, all declaredcheck:nul-bytesOK (scanned 6455 text file(s) … no raw ASCII control bytes)check:platform-checklisteslint scripts/check-platform-checklist.mjsexit 0. The repo-widepnpm lintwas narrowed deliberately, and the narrowing is a measurement:eslint --print-configon the changed file reportsparserOptions.project,projectServiceandEXPERIMENTAL_useProjectServiceallundefined, so type-aware linting is not enabled and this diff cannot move the verdict of any file it does not touch;--format jsonjudged all 4 changed files, 0 problems. CI runs the full farm regardless.Dependency closure built under
scripts/pm/os-verify-lock.shbefore any gate was trusted;git status --porcelainempty afterwards, so nothing generated is in this diff.Deliberately not in this PR
check:platform-checklist— reversing a recorded ruling.fixturesrecipes carry norevision/history, so a semantic recipe edit silently re-validates every run record that depends on it #10885 (reciperevision/history) is not foreclosed, and is cited in the history entry that leans on the consuming-item-revision workaround.provisioning.use— it now does #10809 is closed (merged as docs(qa-checklist): correct two knownGaps that still deny the provisioning.use resolve #10883). Its own sequencing note assigned the second prose rewrite to whatever answered gap 2 — "Doing this correction after that decision costs one edit instead of two" — which is this PR, so the twoknownGapsare corrected here rather than left inverted a second time.No changeset: internal QA gate plus internal QA ledger, nothing published changes — same posture as #10810, which changed these same files.
skip-changesetapplied.Generated by Claude Code
Generated by Claude Code