Found while implementing #11679 (the deleteMetaItem request-schema catch-up; PR #12003) — the save door is the same request-shape gap one door over, and it is the biggest remaining one in the meta write family. Filed rather than touched: per-member protocol-vs-transport verdicts are a packages/spec contract decision (the #11006 ruled pattern), out of the fold's declared scope.
Measured on the PR #12003 merge head 2c8a3183 (base d63b0143)
SaveMetaItemRequestSchema (packages/spec/src/api/protocol.zod.ts) declares exactly three members:
exportconstSaveMetaItemRequestSchema=lazySchema(()=>z.object({type: z.string().describe('Metadata type name'),name: z.string().describe('Item name'),item: z.unknown().describe('Metadata item definition'),}));The REST PUT /meta/:type/:name door (packages/rest/src/rest-server.ts, p.saveMetaItem({ ... } as any) — the closing cast sits at ~:5630 on that head) sends up to eleven: type, name, item, organizationId, writeFace: 'meta-envelope', and conditionally environmentId, parentVersion, actor, force, packageId, mode: 'draft'.
The implementation's parameter type (ObjectStackProtocolImplementation.saveMetaItem, packages/metadata-protocol/src/protocol.ts:13140) declares { type, name, item?, organizationId?, parentVersion?, actor?, force?, mode?, packageId?, source?, writeFace? } — so unlike the pre-#11679 delete door, every key the door sends IS read and enforced; only the spec contract is starved. saveMetaItem is a REQUIRED protocol member, so the cast here is pure request-shape smuggling (the TS2353 half, as measured for the delete door in PR #12003), not feature detection.
The decision (why filed, not fixed)
Same as #11679, on a bigger and subtler surface — per member, protocol vs transport:
Precedents: #11006 (maintainer ruling 2026-08-22, option B) · #11426 (publish request landing) · PR #12003 (#11678 audit + #11679 delete — the immediately preceding doors in this series).
⛔ Nothing about this door is addressed in PR #12003; its casts and payload are byte-identical there.
Generated by Claude Code
Found while implementing #11679 (the
deleteMetaItemrequest-schema catch-up; PR #12003) — the save door is the same request-shape gap one door over, and it is the biggest remaining one in the meta write family. Filed rather than touched: per-member protocol-vs-transport verdicts are apackages/speccontract decision (the #11006 ruled pattern), out of the fold's declared scope.Measured on the PR #12003 merge head
2c8a3183(based63b0143)SaveMetaItemRequestSchema(packages/spec/src/api/protocol.zod.ts) declares exactly three members:The REST
PUT /meta/:type/:namedoor (packages/rest/src/rest-server.ts,p.saveMetaItem({ ... } as any)— the closing cast sits at ~:5630 on that head) sends up to eleven:type,name,item,organizationId,writeFace: 'meta-envelope', and conditionallyenvironmentId,parentVersion,actor,force,packageId,mode: 'draft'.The implementation's parameter type (
ObjectStackProtocolImplementation.saveMetaItem,packages/metadata-protocol/src/protocol.ts:13140) declares{ type, name, item?, organizationId?, parentVersion?, actor?, force?, mode?, packageId?, source?, writeFace? }— so unlike the pre-#11679 delete door, every key the door sends IS read and enforced; only the spec contract is starved.saveMetaItemis a REQUIRED protocol member, so the cast here is pure request-shape smuggling (theTS2353half, as measured for the delete door in PR #12003), not feature detection.The decision (why filed, not fixed)
Same as #11679, on a bigger and subtler surface — per member, protocol vs transport:
environmentIdis already ruled transport-level ([finding] Meta-read request schemas still omit previewDrafts / state / environmentId, so the REST call-site casts survive the organizationId catch-up #9741, 2026-08-18) and ridesTransportScopedMetaRequest— OUT.writeFaceneeds its own verdict: the door comment records it is server-stated ("a client cannot smuggle a face in", saveMetaItem's spec-validation 422 message restates itsissues[]on the envelope, but message-only faces (duplicatePackagefailed[].error) forbid the #10524 trim until they declare a structured channel #10888) — an argument it is REST-layer coordination rather than a wire-authorable contract member; but it is read by the implementation and shapes the 422 envelope. Not obvious, needs the four-axis walk.sourceexists on the implementation but is never sent by this door — the publish precedent (feat(spec): declare publishMetaItem as an optional MetadataProtocol member with PublishMetaItemRequest #11426) would leave it undeclared unless another producer pulls it.organizationId/parentVersion/actor/force/packageId/modelook like straight feat(spec): declare publishMetaItem as an optional MetadataProtocol member with PublishMetaItemRequest #11426/deleteMetaItemis a declared member whose request schema declares 2 of the 8 members the REST reset door sends, so the call-site cast cannot come off #11679-lineage declares (each read and enforced;packageId's absent-vs-null distinction is already documented on the publish schema).Precedents: #11006 (maintainer ruling 2026-08-22, option B) · #11426 (publish request landing) · PR #12003 (#11678 audit + #11679 delete — the immediately preceding doors in this series).
⛔ Nothing about this door is addressed in PR #12003; its casts and payload are byte-identical there.
Generated by Claude Code