Uh oh!
There was an error while loading. Please reload this page.
feat(devx,qa): refuse a folded /meta plural spelling in any checklist call string - #13248
Queued
os-elon wants to merge 1 commit into
Queued
feat(devx,qa): refuse a folded /meta plural spelling in any checklist call string#13248os-elon wants to merge 1 commit into
/meta plural spelling in any checklist call string#13248os-elon wants to merge 1 commit into
Conversation
…list `call` string A QA checklist `call` is the one field in that ledger an operator REPLAYS, so a step reading `PUT /api/v1/meta/objects/:name` instructs a plural item write on the metadata write door. It answers 200 only because the boundary folds the spelling; the `/meta` type segment is always singular. The population is why this is a gate and not a sweep. The four sites measured at `2866d5f97e` were all in `areas/attachments-storage.json` and have since been repaired; `areas/records-forms.json` held ZERO occurrences at that ref and gained one four days later in `5737222b89`, authored, reviewed and merged with nothing in the tree able to see it. The class is still being newly introduced, so the deliverable is what stops the next one. `check-platform-checklist.mjs` now scans every `call` string, at any depth, and refuses a `/meta` or `/metadata` path segment that is a key of `META_URL_TO_SINGULAR` — the closed set of folded spellings generated in `packages/spec/src/meta-spelling/meta-url-data.generated.ts` and re-derived from its live sources on every CI lap by `check:meta-url-spelling`, so the guard cannot drift from the contract it enforces. Read as source text, with the same refusal discipline the trap-vocabulary extractor beside it already has: a literal it cannot parse is a hard failure, never an empty vocabulary. `call` ONLY. The neighbouring `why` / `expect` / `source` fields legitimately quote plural spellings to narrate the fold, and `attachments-storage.json`'s `requires` prose correctly names the parameterized `PUT /api/v1/meta/:type/:name`. That is also why this could not be a root expansion of the sibling `check-doc-route-spelling.mjs`: that gate scans a file's text, and text-scanning these files would flag the narration along with the instruction. The fold itself is untouched — no wire behaviour, no router, no accept/reject decision changes. The refusal is about the repo not INSTRUCTING a spelling it merely tolerates. The fifth site is repaired in the same commit, so the ratchet lands at a zero baseline with no grandfather record. Finding filed as #13010. 53 new assertions run on every invocation, not only behind `--self-test`: this gate is not CI-wired by maintainer decision, so a `--self-test` leg here would execute nowhere. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw
This was referenced Aug 29, 2026
os-elon
marked this pull request as ready for review
August 29, 2026 16:48
os-elon
enabled auto-merge
August 29, 2026 16:48
os-elon
added this pull request to the merge queueAug 29, 2026
Any commits made after this event will not be merged.
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#13010
A QA checklist
callis not prose. RUNNER.md has an operator replay it against a live boot, so it is the one field in that ledger that is an executable instruction — andareas/records-forms.jsontold its operator to sendPUT /api/v1/meta/objects/qa_nofeeds, a plural item write on the metadata write door. The/metatype segment is always singular; that step answers 200 only because the boundary folds the spelling.The point is not the site — the population GREW after it was measured
The card's argument, re-measured on this branch's base
5a9b7a0b10:#11042's four sites, measured at2866d5f97eareas/attachments-storage.json— since repaired by9598f0ee7a(PR #13011), so they no longer appear in the populationareas/records-forms.jsonat that same refgrep -c 'meta/objects'→ 05737222b89(PR #12382)So this is not a site a sweep missed; it is a site that did not exist yet. The class is still being newly introduced, one honest PR at a time, which makes the deliverable what stops the next one rather than are there more. A sweep — however complete on the day it runs — is by construction the thing that already failed here.
What lands
A refusal in
scripts/check-platform-checklist.mjs. Everycallstring in every area document, at any depth, is scanned for a/metaor/metadatapath segment, and the segment is refused when it is a key ofMETA_URL_TO_SINGULAR.The vocabulary is read from the contract, never copied.
META_URL_TO_SINGULARlives inpackages/spec/src/meta-spelling/meta-url-data.generated.ts— derived bybuild-meta-url-spelling.tsfromPLURAL_TO_SINGULARandDEFAULT_METADATA_TYPE_REGISTRY, and re-derived from those live sources on every CI lap bycheck:meta-url-spelling(lint.yml, "Check the meta-url-spelling data module is current and spellings agree" — green on this tree:34 spellings, 27 registry-declared types; manifest/derived agreement holds). Both halves confirmed: it is the right closed set and it is gate-held, so this guard cannot drift from the contract it enforces.packages/specis read only — nothing there is edited.Keying on the closed set rather than a trailing-
sheuristic matters in both directions: it catches the camelCase folds a heuristic would miss (sharingRules,analyticsCubes,ragPipelines) and it leaves alone anything that merely looks plural.Read as source text, not imported, because this gate is a zero-dependency
nodescript on an unbuilt tree (README "Operating cadence"). Same refusal discipline as the trap-vocabulary extractor beside it: a literal it cannot parse — missing declaration, no object literal, unclosed, zero pairs, a partial parse under the floor, or a map that has lost theobjectsanchor — is a hard failure, never an empty vocabulary that would validate everycallagainst nothing and print a green identical to a working parse.The fifth site is repaired in the same commit, so the ratchet lands at a zero baseline with no grandfather record. Nothing is parked, so there is nowhere for the next one to hide. The shape of the repair is not invented here:
9598f0ee7amade exactly this edit on the four sibling sites.Two fences, both load-bearing
The fold is not narrowed.
/meta/objects/:nameand/meta/object/:nameanswer identically today and must keep doing so. Nothing in this PR reaches the wire, the router, or any accept or reject decision — the refusal is about the repo not instructing a spelling it merely tolerates. The sibling gatecheck-doc-route-spelling.mjscarries the same fence in its own header.callonly. The neighbouring fields legitimately contain plural spellings, because their job is to narrate the fold or the defect: awhyexplaining why the plural door was a hole, anexpectdescribing what the old spelling answered, asourceciting a registration that was retired, andattachments-storage.json'srequiresprose correctly naming the parameterizedPUT /api/v1/meta/:type/:name— a segment-shaped literal, not a plural. That is also why this could not be a root expansion ofcheck-doc-route-spelling.mjsinstead: that gate scans a file's text against the route ledgers, and text-scanning these files would flag the narration along with the instruction. Field-addressed collection is what makes the ruling mechanical rather than a matter of care.Depth rather than a curated path list, for the same reason the gate exists at all: every
callon the ledger today sits in an area-levelfixtures.RECIPE.sequence[], but a path list that matches where they happen to live now can never match the one added tomorrow.Verification
Union run at
a7864a0161, the final commit on this branch.Baseline (positive control that the gate runs at all), on the unmodified base:
Exit 0 with the plural site present — confirming the card's claim that nothing in the tree could see it. Grepping the checker's source for
callreturns only prose matches.After, at
a7864a0161:--self-test: 128 assertions, exit 0.53 new assertions run on every invocation, not only behind
--self-test. This gate is not CI-wired by maintainer decision (README "Operating cadence"), so a--self-testleg here would execute nowhere — the file's existing batteries are inline for exactly that reason and this one follows them. The battery pins the false-positive direction as hard as the firing direction, because a gate that reds a correct row costs more than the defect it catches.Four ablations, each with its prediction named before the run, mutations proven on disk by decomposed grep counts plus
git hash-objectagainst the HEAD blob, restores proven by an emptygit diff HEADunder an absolute-pathtrap. No rebuild leg applies: this gate is a plain.mjsthatnodeexecutes from source, so nothing resolves through a packageexportsordistthat could go stale.sequence[1].callandobjectsflowtoflows)flows— the refusal is not hardcoded to the anchorwidgets)sexpect) of the real ledgercall-only ruling, on the real corpusFalse positives, tested against the real corpus rather than fixtures only. The guard ran over all 15 live area files and their 19
callstrings and flagged exactly one — the measured site. The corpus already exercises every shape worth worrying about: a query parameter (?package=com.objectstack.qa.feeds), the canonical singular (/meta/object/,/meta/flow/), parameterized prose (/meta/:type/:name), an address wrapped in prose, and calls that are not URLs at all. A3 and A4 add the two the corpus does not carry.Derived gate family —
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, exit 0, derived ata7864a0161; 14 families matched. All green, with two exceptions that are refusals rather than findings, each captured before any pipe:check-test-completeness.mjsexits 3 withNOT MEASURED— it needs a savedturbo run testlog. Not a red.check-doc-formula-expressionsfirst exited 1 withPREREQUISITE NOT MET. After building@objectstack/formulaand@objectstack/lintit was really measured: exit 0.pnpm lint(the full repo scan,eslint . --no-inline-config) exits 0 — run whole, so no narrowing is being declared.pnpm check:nul-bytes:OK (scanned 7342 text file(s) ... no raw ASCII control bytes).Scope
#11042is untouched here and stays open: its triage fences it toattachments-storage.json, this is a different file, and the deliverable is a guard rather than a site repair. No changeset — a QA ledger edit plus a CI-internal gate script releases nothing, which lint.yml calls the textbookskip-changesetcase, and the9598f0ee7aprecedent carried none either.One honest limitation, stated rather than buried.
check:platform-checklistis deliberately out of per-PR CI by maintainer decision, so this refusal catches the next plural at the next manual run — the run the README already prescribes "whenever you touch the checklist", which is exactly the author who would introduce it — not at the moment it merges. Re-wiring that cadence is a maintainer decision and is not taken here.Generated by Claude Code
Generated by Claude Code