feat(spec): declare the save door's request members and de-cast the REST PUT literal (#12004) - #13599
Conversation
…EST PUT literal (wip) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
…liberate non-declarations Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
…ons the save-door diff shifted check-system-context-census caught pure line rot: the new import line shifted every site below it by +1 and the de-cast door block shifted the three sites below it by +17. The gate's --fix refused (same population-sanity reason as the history-door landing), so the eight anchors are rewritten by hand to the lines the census and ledger already resolve to. Census now: 109 sites all anchored, 145 anchors resolve, 27 declared non-read. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
📓 Docs Drift CheckThis PR changes 2 package(s): 16 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 2 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 128 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 88612888280db45443f56bed4a6c0786d8660a68 && git checkout 88612888280db45443f56bed4a6c0786d8660a68
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3d0897459577835a78fc96a8f718eb3b139708a3 722b069fa7f5c361b7d94861404e4bdd940e0264 && git checkout -B drift-repro 3d0897459577835a78fc96a8f718eb3b139708a3 && git merge --no-ff 722b069fa7f5c361b7d94861404e4bdd940e0264
node scripts/docs-audit/affected-docs.mjs --json 3d0897459577835a78fc96a8f718eb3b139708a3
|
|
Released by maintainer instruction. Provenance: maintainer, live PM session chat, 2026-08-31 ~04:0xZ, verbatim 「pr 绿了为什么不合并」 — read as a personal release of the green parked contract-face PRs, per the recorded #12606 precedent (「12606 绿了」= personal release lifting the Generated by Claude Code |
Fixes #12004
PUT /api/v1/meta/:type/:namewas the biggest remaining request-shape gap in the meta write family:saveMetaItemis a REQUIRED protocol member, butSaveMetaItemRequestSchemadeclared 3 of the ~11 members the REST door sends, so the call-site literal was castas any— pure request-shape smuggling (TS2353 on every undeclared key once the cast comes off), never member-existence feature detection. This lands the #11006 maintainer-ruled pattern (2026-08-22, option B: per-member protocol-vs-transport walk, mirror the implementation's parameter type member for member) exactly as PR #12003 landed it for the audit and reset doors and PR #13521 for the history door.Per-member verdicts (triage 2026-08-25, executed here)
organizationId(optional string)parentVersion(optional, string or null)actor(optional string)force(optional boolean)objectsaves reach that diffmode(optional, draft or publish)draft(absent included) is the legacy straight-to-live defaultpackageId(optional, string or null)writeFace(optional, closed enum of the three faces)environmentIdTransportScopedMetaRequest, pinned shape-absentsourceThe writeFace walk (converged: declare)
meta-envelope), the runtime dispatcher door (meta-dispatch, packages/runtime/src/domains/meta.ts), and the duplicate-package internal call (package-duplicate); measured consumers — the 409 destructive-change remedy and the 422 findings clause both branch on it. A real parameter, not speculation.issues[]on the envelope, but message-only faces (duplicatePackagefailed[].error) forbid the #10524 trim until they declare a structured channel #10888: every producer builds the request field by field, never spreads a wire body, so a client-sent face is simply never read) — the same treatment the merged reset twin givesactor. Declaring records the truth; hiding it keeps a phantom surface only the cast could reach.The de-cast, and its reverse verification
The literal is now a named const typed
TransportScopedMetaRequestwrappingSaveMetaItemRequest— the reset-door spelling, because this door still spreads the transport-levelenvironmentId(long-standing wire shape, deliberately unchanged). Wire payload byte-identical: same keys, same conditional spreads, same server-stated face; the rest-server diff adds a type annotation and a comment, nothing else.Reverse verification (predicted red, from the committed fix): a
smuggledKeypasted into the literal turned the rest typecheck red withand the error text prints the widened member list from the rebuilt spec dist — proving the literal compiles against the regenerated declarations, not a cache. Restore proven byte-identical:
git diff HEADempty, worktree blob hash equals the HEAD blob hash, zero grep hits for the smuggled key.One measured behaviour pin rode along: the untouched
item: z.unknown()member is key-REQUIRED at parse (absent key refused; a present explicit null parses and is then refused 400 by the implementation's own guard) — pre-existing contract behaviour, now pinned instead of silent.Verification (all quoted verdicts from head 397d9ea; census repair at 722b069, union re-run on that final head reported on the card)
check-dts-emitted: @objectstack/spec - 34/34 declared declaration file(s) present.— VERDICT command-exit 0check:test-typecheck: OK — @objectstack/spec's test layer compiles— VERDICT command-exit 0Test Files 2 passed (2) · Tests 181 passed (181)— the existing Iso135 isomorphism pin still holds (widening adds only optional/nullable/enum members, no defaults or transforms)Tasks: 25 successful, 25 totalcheck:test-typecheck: OK — @objectstack/rest's test layer compiles— VERDICT command-exit 0110 passed+261 passedcheck:generatedproved exactly one stale (content/docs/references — the new describe() rows) and--fixregenerated only it;check:authorable-surface,check:api-surface,check:export-origins,check:strictness-ledgerall green (no new z.object sites, no new exports — the authorable-surface api.json picked up the seven new member rows from the rebuilt schema, as the history precedent's did)--fixrefused as in the history landing, anchors re-pointed by hand;check-system-context-census: OK — 109 elevation read sites in 20 packages across 45 files, all anchored; 145 anchors resolve, 27 declared non-read.turbo run build, and the whole union re-runs on the final head — quoted verdicts land in the card reportskip-changesetdoes not apply: this PR publishes a spec minor and a rest patch (changeset pair mirrors the merged history precedent).Out of scope, untouched here: the door's 501 envelope, tenant-scoping questions, and the dispatcher door's own literals (#13598 records the adjacent runtime service-handle casts). #12004 is the only card addressed by this PR.
Generated by Claude Code
Generated by Claude Code