Uh oh!
There was an error while loading. Please reload this page.
feat(spec): declare previewDrafts/state on meta-read requests; record environmentId as transport-level; retire REST door casts (#9741) - #9804
Conversation
…meta-read requests; record environmentId as transport-level (#9741) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016D9wdJR14KKCxz1WgdAzcw
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016D9wdJR14KKCxz1WgdAzcw
📓 Docs Drift Check5 anchor(s) derived from 2 changed package(s); no hand-written page names any of them. ✅ What this run could not see
Coarse fallback — 117 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 3015903c88b4aed60ad363114f7b259b9d4f6f8e && git checkout 3015903c88b4aed60ad363114f7b259b9d4f6f8e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b8820208c0979e8132bfca571ac49c669f69e8ad 25a3c13b79d098e77d873859d0af978ed40124d3 && git checkout -B drift-repro b8820208c0979e8132bfca571ac49c669f69e8ad && git merge --no-ff 25a3c13b79d098e77d873859d0af978ed40124d3
node scripts/docs-audit/affected-docs.mjs --json b8820208c0979e8132bfca571ac49c669f69e8ad |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#9741
Implements the maintainer ruling recorded 2026-08-18 on the card (comment 5334871606, accepting the four-prong recommendation's split): declare the two draft-visibility switches where the implementation enforces them, record the
environmentIdexclusion as a decision, and retire theas anycasts at the REST meta-read doors.What changed
1. Spec: draft-visibility switches declared exactly where enforced (
packages/spec/src/api/protocol.zod.ts)Measured against the inline parameter types in
packages/metadata-protocol/src/protocol.tson currentorigin/main(11b779e):getMetaItemspreviewDrafts?: boolean(nostate)previewDraftsaddedgetMetaItemstate?: 'active' | 'draft',previewDrafts?: booleangetMetaItemCachedgetMetaItemLayeredEach
describe()states that the member is a draft-visibility switch (ADR-0033 draft-overlay preview / the strict draft-buffer read) and that ADR-0106 masking is unaffected — declaration ≠ authorization: the members only select which stored rows are read; draft access stays admin-gated upstream.2. Spec:
environmentIdexclusion recorded as a decisionA schema-adjacent comment on
GetMetaItemsRequestSchema(the first meta-read request schema a reader meets) names the ruling and the card:environmentIdis the transport-level multi-kernel routing key and stays OUT of the protocol request shape by explicit decision — the REST layer resolves the target kernel from it before the protocol call, and the implementation's parameter types never read it off the request.3. REST: casts retired at the meta-read doors (
packages/rest/src/rest-server.ts)A documented
TransportScopedMetaRequestalias (R & { environmentId?: string }) declares the one transport-level member on top of the spec request type, so each door's request literal now compiles against the declared contract — an undeclared key is a compile error at the call site:getMetaItems):as anyretired; literal typedTransportScopedMetaRequestofGetMetaItemsRequest.getMetaItemCached):as anyretired; typed withGetMetaItemCachedRequest.getMetaItem):as anyretired; plainGetMetaItemRequest(this door never threadsenvironmentId).getMetaItemLayered): carried no cast, butp: anymeant its literal was never checked either — the literal is now typed withGetMetaItemLayeredRequest.No cast had to stay: every member the doors thread is either declared by the spec (
type/name/packageId,organizationIdper the organizationId catch-up card #9726 — not addressed here, it landed already —state,previewDrafts) or is the transport envelope'senvironmentId. Zero runtime change: the outgoing payloads are byte-identical (same conditional spreads).Out of the card's scope and untouched: other
getMetaItemshelper call sites that feature-detect via(p as any)(e.g.loadObjectItems,fetchAudienceBooks, discovery/dataset listings) — the card names the four meta-read doors only.4. Tests (
packages/spec/src/api/protocol.test.ts, extending the #9726 pin pattern — that card is not addressed here)previewDrafts(both verbs) andstate(both values) —successalone proves nothing on non-strict schemas;state: 'archived'and non-string-vocabulary values refused; non-booleanpreviewDraftsrefused;getMetaItemsdeclares nostate; cached/layered declare neither switch (parse strips them);environmentIdNOT-declared pins, ruling cited: a carried value is stripped by parse on all four read schemas, plus a shape-level pin (Object.keys(schema.shape)contains noenvironmentId).5. Regen + changeset
gen:schema/gen:openapi/gen:docsre-run (content/docs/references/api/protocol.mdx+3 lines;authorable-surface/api.jsonrewritten by the generator as designed). Changeset: spec minor (accept-set widening catch-up), rest patch. No behaviour change anywhere — the wire accepted and honoured these members before; only the declared surface and call-site typing changed.Verification (all at head
25a3c13)pnpm --filter @objectstack/spec test— 413 files / 11007 tests passed (re-run at final head); the 18 new [finding] Meta-read request schemas still omit previewDrafts / state / environmentId, so the REST call-site casts survive the organizationId catch-up #9741 pins pass targeted (vitest run src/api/protocol.test.ts -t 9741: 18 passed).pnpm --filter @objectstack/spec typecheck— clean (incl. scripts + test-typecheck ledgers).pnpm --filter @objectstack/rest test— 128 files / 2096 tests passed;pnpm --filter @objectstack/rest typecheck— clean with the casts retired (the cast-retirement's own proof)..d.ts, both directions): injectingstate: 'archived'into the uncached door literal →TS2322(not assignable to"draft" | "active" | undefined); injectingbogusKey: 1→TS2353unknown-property error whose printed type showsstate?: "draft" | "active"andpreviewDrafts?: booleanresolved from dist; restored → green.node scripts/pm/dispatch-gates.mjs, no hand-fed paths): all 26 path-matched + 5 convention-triggered gates run locally and green, includingcheck:cross-package-test-inputs, speccheck:empty-state/check:liveness/check:strictness-ledger/check:variant-docs/check:authorable-surface,check:merge-driver,check:spec-parsed-alias,check:type-source-resolution,check:generated(13/13 artifacts in sync),check:docs,check:route-envelope,check:engine-double-contract,check:where-matcher,check:query-options-erasure,check:type-check-coverage,check:type-check-debt --re-measure(33 entries, none above recorded),check-changeset-no-major,check-nul-bytes,check-dev-prereqs.turbo run build --filter=./packages/* --filter=./packages/*/*, 70/70 packages green (totality covers every consumer of @objectstack/spec).Generated by Claude Code