fix(qa,spec,client): address the metadata write door by its canonical singular type segment - #13011
Conversation
… singular type segment The QA platform checklist told its own operator to send a plural item write (PUT /api/v1/meta/objects/:name) on four executable steps. Those answer 200 only because the boundary fold tolerates the spelling; the /meta type segment is always singular. Rewrite the four executable call strings plus two address-stating notes to the canonical spelling. Part of #11042 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw
📓 Docs Drift Check2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 0d6c4f91c9b6cf4aa68cd51ac8ea6b65a810c22a && git checkout 0d6c4f91c9b6cf4aa68cd51ac8ea6b65a810c22a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8f10a79f7aef471482fdb4761ae150014a7f355a 7d6723e9d248ffacb00bc9a5877285f6944770c2 && git checkout -B drift-repro 8f10a79f7aef471482fdb4761ae150014a7f355a && git merge --no-ff 7d6723e9d248ffacb00bc9a5877285f6944770c2
node scripts/docs-audit/affected-docs.mjs --json 8f10a79f7aef471482fdb4761ae150014a7f355a |
…ecutable-plural-meta-calls # Conflicts: # packages/spec/liveness/object.json
…nt in the http-cache example The seventh site of the card's slice, held back on the first pass because packages/spec/src/api was inside a declared in-flight surface (#12522). That claim has since closed (merged PR #13019), and the re-run check finds the directory's only current claim (#13040) declared file-precisely at endpoint-publish-gate.ts with 'Nothing else' -- disjoint from this file, and its PR touches exactly that one file. No open PR touches http-cache.zod.ts. Only the plural type segment moves. The /metadata prefix is a separate axis the card does not scope, and the sibling example at :188 uses it too. Part of #11042 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw
Part of #11042
The QA platform checklist did not merely mention the plural metadata spelling — on four executable steps it told its own operator to send one, on the write door. All seven of the card's sites are now addressed.
Generated by Claude Code, session
https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw(recorded in prose: a body PATCH eats a trailing rule and the footer after it, per the reading on #12909).Update — merged
main, and the seventh site is now INorigin/main(8f10a79f7a) into the branch — a merge commit, not a rebase, so anyone holding the branch keeps a valid checkout. One conflict, resolved; detail below.packages/spec/src/apisat inside a declared in-flight surface. That claim has since closed. Re-run of the check, below.packages/spec/src/**, so the clause-② PATH leg fires and this PR is ⛔ not enqueueable withoutneeds:contract-review. CONTENT is comment-only — one docblock line, zero behaviour movement. This seat does not attach the gate label.The seven sites
docs/qa/platform-checklist/areas/attachments-storage.json:27,34,41,84PUT /api/v1/meta/object/qa_{vault,shared,nofiles,media}packages/spec/liveness/object.json:104"served on /meta/object/:name. …"packages/client/src/client.hono.test.ts:170fetch(.../api/v1/meta/object)packages/spec/src/api/http-cache.zod.ts:108// GET /api/v1/metadata/object/account4 files, 7 changed lines.
:97; the pre-merge tree had:101; the merged tree has:104. Re-derived on the merged tree each time rather than carried forward./metadataprefix is a separate axis this card does not scope — the declared default is/meta(rest-server.zod.ts:298) but the sibling example at:188also uses/metadata, so rewriting the prefix here would split the file's internal consistency for a reason the card never asked for.The conflict, and how it was resolved
main's #13003 batch 4 (93ea19bcac) re-anchoredliveness/object.jsonfrom line citations to symbol citations, rewriting theactionsentry'sevidenceandnoteand addingverifiedAt. Its new note still opened with the pluralserved on /meta/objects/:name., so this card's change still applied — to different surrounding text.Resolved by taking
main's side wholesale and re-applying only the one word. Nothing of the re-anchoring is reverted;git diff origin/mainfor that file is exactly one line changed:In-flight check, re-run (not carried forward)
The card's
Unlock-actionforbids clearing the block by judging it unlikely to bite, so the check was re-run against the now-current claims, which have turned over almost entirely:http-cache.zod.tsat all.packages/spec/src/api's only current claim is [finding]endpoint-publish-gate.ts's consumer table names two symbols that do not exist —cacheControlHeaderandendpointRateLimiterRegistry, both renamed inendpoint-policy.ts#13040, which declarespackages/spec/src/api/endpoint-publish-gate.ts(the:31docblock table's policy row, comment text only) and closes it with "⛔ Nothing else." File-precise, and its PR docs(spec): name the real policy-gate runtime symbols in the publish-gate consumer table #13046 touches exactly that one file. Disjoint fromhttp-cache.zod.ts.packages/spec/liveness/object.json(site 5) IS inside #13003's declared surface —packages/spec/liveness/*.jsonONLY, a glob, and that card is an in-flight batched worklist with continuation rounds. That collision has already happened once — it is what produced this merge conflict — and it was resolved preserving #13003's work in full. Flagged rather than silently carried, since a later batch could touch this ledger again.The measurements
The fold is still live, so these rewrites are answer-preserving.
check:meta-url-spellingon the merged tree: 34 spellings, 27 registry-declared types; manifest/derived agreement holds. Static dual (an instrument that only ever folds proves nothing):fold("objects")="object"folded,fold("object")passthrough, and negative controlsfold("metadata-objects")/fold("bananas")both unfolded. Runtime dual on the hono suite, by temporarily fetching both spellings plus an unmounted control:expect(singular).toBe(plural)passed andexpect(bogus).toBe(404)passed, so the assertion distinguishes mounted from unmounted rather than passing vacuously; probe applied to a committed tree and reverted withgit checkout HEAD -- ABSOLUTE_PATH, restoration proven by observed state — working blob equal to the HEAD blob,git diff HEADempty.Site 6 is incidental, not a fold pin — re-verified on a deepened clone (11,598 commits;
git logon the default shallow clone answers wrongly with exit 0).839982e3d8(#4018) introduced it to prove a route 404s;e5a4d26901(#4296) flipped it tonot.toBe(404). Neither mentions spelling, plural, or the fold.⛔ What was deliberately left alone
The card's fence is as much the deliverable as the edit. Untouched, on purpose: the deliberate fold pins (
meta-plural-i18n.test.ts,meta-audience-plural.test.ts,meta-state-plural-tolerance.test.ts,protocol-publish-canonical-fold.test.ts,rest-server-meta-org-scope-url-spelling.test.tsand kin — the plural spelling IS the fixture, and rewriting it would leave every assertion green while deleting the coverage); the fold-verification instruction atdocs/protocol-upgrade-guide.md:455; the defect narratives atmetadata-url-spelling.ts:43,rest-route-ledger.ts:196,runtime/src/route-ledger.ts:356;CHANGELOG.md;content/docs/releases/**; #10178's two files;packages/runtime/src/domains/meta.ts:229(a deliberate REST/dispatcher asymmetry — #10178's open question, left as is); the sibling/metadataexample athttp-cache.zod.ts:188; and the test-file and docblock buckets generally.Finding filed, not fixed
#13010 — a fifth executable plural call at
docs/qa/platform-checklist/areas/records-forms.json:35. Not a site the sweep missed: at the card's measurement commit2866d5f97ethat file had zero hits; the call landed in5737222b89on 2026-08-26, four days after the card was filed. The class is still being newly introduced and no gate stops the next one, so the finding is written around the missing guard.Verification
Gate family re-derived from the merged diff (
dispatch-gates --repo, exit 0, no STALE TREE, merge base8f10a79f7). Site 7 added five families:check:authorable-surface(gate sourcepackages/spec/src/api),check:docs,check:skill-refs,check:spec-parsed-alias,check-keyed-text-bounds. All exit codes captured before any pipe — never${PIPESTATUS[0]}, which reads 0 for a red command when the reader closes the pipe early (#12979).Green, quoting each gate's own verdict line:
check:platform-checklistOK — 15 areas, 221 items (221 active)check:meta-url-spellingdata is current (34 spellings, 27 registry-declared types)check:authorable-surfaceauthorable-defaults/ verified against upstream 8f10a79f7aef — 1246 default(s) unchangedcheck:docs(spec)231 generated files in sync with packages/speccheck:skill-refs9 generated files in sync with packages/speccheck:spec-parsed-alias1528 bare z.input aliases, 837 pinned isomorphic, 691 pairedcheck-keyed-text-bounds112 *.object.ts files … walk is repo-widecheck:livenessstate-counts.md is current — the same 31 row(s)check:doc-authoring13338 customer-facing string(s) across 672 spec sources cleancheck:doc-formula-expressions14 predicate(s) on a statically determinable …check:skill-examples260 prose examples type-check across 3 surface(s)check:nul-bytesscanned 7248 text file(s) … no raw ASCII control bytescheck:empty-state/check:strictness-ledger/check:variant-docscheck:engine-double-contract704 pinned, 134 in the DEBT ledger, 3 exemptcheck:where-matcher313 matcher(s) … correctly or refuse it loudlycross-package-test-inputs,objectql-double-limit,page-declaration-shape,published-files,slot-lookup,test-source-alias,type-source-resolution,merge-driver,query-options-erasure,type-check-coverage,ci-filter-parity,comment-mask-adoption,plugin-teardown-shape,undeclared-dep-imports,affected-docsTests:
pnpm --filter @objectstack/client exec vitest run --maxWorkers=2 src/client.hono.test.ts— 4 passed (1 file), after building the dependency closure (33 tasks).Typecheck:
@objectstack/clientexit 0 and@objectstack/specexit 0. Stated precisely, because a green half can measure nothing: plaintsc --noEmitfor client reads 240 files and notclient.hono.test.ts, so site 6 is covered by the chainedcheck:test-typecheckundertsconfig.test.json, whose--listFilesdoes include it (1 of 639). Site 7 is in spec's own program —--listFilesshowshttp-cache.zod.tsamong 906 files.Declared narrowings — two, stated rather than quietly skipped:
pnpm lintnarrowed to the two changed TypeScript files, with all three evidence items: population read from eslint's own--print-config; count read from--format json(2 files, 0 errors, 0 warnings); andparserOptions.projectisnull, so type-aware linting is off and this diff cannot move any untouched file's verdict.check:type-check-debt(the--re-measureratchet) is NOT MEASURED, reported as such rather than as green — it refuses on an unbuilt closure, and with the closure built the re-run exceeds the container's ~10-minute foreground ceiling. CI runs it on every PR regardless.check:dev-prereqsand (before the build)check:doc-formula-expressionsboth exited 1 with PREREQUISITE NOT MET — an unbuilt workspace, which each gate's own text says measures nothing and is not a finding.doc-formula-expressionsis green above once@objectstack/formulawas built.check:platform-checklistis still absent from the derived family even though every area file's own$commentnames it as their validator — the under-naming class of #12956. Run by hand; green.Changeset
None, deliberately —
skip-changeset. Re-derived now that the diff reaches published spec source:docs/qa/platform-checklist/**ships in no package'sfileswhitelist.client.hono.test.tsis a test;check:published-files(green) asserts no package publishes test files.liveness/object.jsonships, but the changed field is anote— governance prose read only by gate scripts andpackages/lint's liveness rule.http-cache.zod.tsships (src/**/*.zod.tsis in the whitelist), but the changed line is a@exampledocblock comment. No API, schema, type or behaviour moves, andcheck:authorable-surfaceconfirms it: 1246 defaults unchanged. Direct in-flight precedent: PR docs(spec): name the real policy-gate runtime symbols in the publish-gate consumer table #13046, a comment-only edit topackages/spec/src/api/endpoint-publish-gate.ts, carries no changeset either.