Skip to content

feat(qa-checklist): resolve an area-qualified provisioning.use (#10593 gap 2) - #11508

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-10593-checklist-use-resolve-check
Aug 24, 2026
Merged

feat(qa-checklist): resolve an area-qualified provisioning.use (#10593 gap 2)#11508
os-steve merged 1 commit into
mainfrom
claude/issue-10593-checklist-use-resolve-check

Conversation

@claude

@claudeclaudeBot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

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.use could 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 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 and with nothing red.

What changed

use now has two spellings, and the gate resolves both:

"use": "qa-scratch-authz"// a recipe of the item's OWN area"use": "search:qa-contributor-bound-member"// "<area>:<recipe>" — one another area owns

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:

  • The area files are now walked twice. The recipe universe has to be complete before any item is judged; a single pass would test each item against a half-built map, and whether a cross-area reference resolved would depend on readdir order.
  • Recipes are keyed by filename stem, deliberately not by doc.area. The two must agree, and that agreement is itself checked one line down — keying by a mismatched doc.area would 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 under scripts/ (that would move dispatch-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 exercises areaRecipeKeys only. Observed exactly 27, exactly that set, and the full gate refused with the provisioning-resolve check's own positive control FAILED rather 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 an EXIT INT TERM trap (sha back to 3fa7b602…, marker count 0, clean git 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: sha 39d95f98…, recipesByArea occurrences 10 → 0, git diff --quiet origin/main clean) while keeping this PR's data change. The base gate reds on exactly the converted item:

✗ records-forms.json · records-forms.crud-roundtrip: "fixtures.provisioning.use" names
"search:qa-contributor-bound-member" but this area file has no area-level "fixtures"
block to resolve it against — …

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):

injected usewhat the gate said
search: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-roundtrip clause 7 now carries use: "search:qa-contributor-bound-member"; its knownGaps and clause-7 verify text stop saying the reference cannot be spelled.
  • search.json's recipe knownGaps stops saying no cross-area spelling exists, and now describes a recipe with consumers in two areas, both machine-resolved.

Both consuming items take a revision bump and a history entry. For search.rls-both-personas that follows the precedent its own revision 5 set: area-level recipes carry no revision/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.

⚠️ Two things this PR's green does NOT tell you

1. check:platform-checklist is 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 green Lint & Repo Gates on this PR says nothing whatsoever about it, and a typo'd use is 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 pristine origin/main @ 63da5867 before any edit: coverage.json · manifest: UNCLASSIFIED. packages/spec/liveness/manifest.json landed 2026-08-23 (#11338) without a matching coverage.json entry — 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.json sha256 unchanged), the gate goes fully green and reports the census:

check-platform-checklist: OK — 15 areas, 205 items (205 active); coverage: 30 kinds mapped,
1 waived; traps: 19 documented, 19 in use; provisioning: 4 area recipes, 7 item references
resolved (1 area-qualified) (self-checks: 22 trap-vocabulary + 34 provisioning-resolve assertions).

Gates

All on the final commit 4638ae27, exit codes captured before any pipe, each verdict quoted from the line the gate printed itself. Derived with node 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.

gateexitverdict line
check:cross-package-test-inputs0OK: 14 package(s) read outside themselves, all declared…
check:doc-authoring0✓ doc authoring guard: 389 files clean
--filter @objectstack/lint check:doc-formula-expressions0✓ … 22 record-scoped formula example(s) across 420 files / 1449 TS blocks judged clean
check:entry-guard0✓ … 139 scripts/ file(s) — every entry guard goes through invoked-as.mjs
check:parse-guard0self-test + scan clean
check:pnpm-filter-targets0✓ … 120/148 --filter occurrence(s) … resolve
scripts/check-ci-filter-parity.mjs0OK: all 89 declared cross-package glob(s)…
scripts/check-cross-package-test-inputs.mjs0OK: 14 package(s) read outside themselves, all declared
check:nul-bytes0OK (scanned 6455 text file(s) … no raw ASCII control bytes)
check:platform-checklist1the pre-existing #11421 problem only — identical on base and branch

eslint scripts/check-platform-checklist.mjs exit 0. The repo-wide pnpm lint was narrowed deliberately, and the narrowing is a measurement: eslint --print-config on the changed file reports parserOptions.project, projectService and EXPERIMENTAL_useProjectService all undefined, so type-aware linting is not enabled and this diff cannot move the verdict of any file it does not touch; --format json judged all 4 changed files, 0 problems. CI runs the full farm regardless.

Dependency closure built under scripts/pm/os-verify-lock.sh before any gate was trusted; git status --porcelain empty afterwards, so nothing generated is in this diff.

Deliberately not in this PR

No changeset: internal QA gate plus internal QA ledger, nothing published changes — same posture as #10810, which changed these same files. skip-changeset applied.

Generated by Claude Code


Generated by Claude Code

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
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 24, 2026
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation labels Aug 24, 2026
@os-steve
os-steve marked this pull request as ready for review August 24, 2026 02:13
@os-steve
os-steve added this pull request to the merge queueAug 24, 2026
Merged via the queue into main with commit d10e214Aug 24, 2026
32 checks passed
@os-steve
os-steve deleted the claude/issue-10593-checklist-use-resolve-check branch August 24, 2026 02:24
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
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@os-steve@claude