Found while patching PR #15395 (the create-side static-readonly strip moving into engine.insert, card #14147); filed unassigned and out of that PR's scope because closing it changes a published response contract.
What is true today
After the 2026-09-03 ruling (option C on #14147), every DataProtocol create face hands the caller's payload to engine.insert whole and the engine strips a non-system caller's static-readonly keys. Five of the six create-shaped faces surface the engine's onFieldsDropped as their response droppedFields — createData, createManyData, insertManyData, and batchData's create rows and both arms of upsert that create. cloneData does not. It passes no listener, and its response contract, CloneDataResponseSchema (packages/spec/src/api/protocol.zod.ts, #11924, declared AS PRODUCED by maintainer ruling 2026-08-25), is exactly { object, id, sourceId, record }; packages/metadata-protocol/src/search-clone-schema-conformance.test.ts asserts in so many words that the body carries no droppedFields. PR #15395 pins that absence with its reason (protocol.readonly-insert.test.ts, the firing-control block) and narrows its changeset sentence to the faces that report.
Why it is worth a card
A clone IS a create, and it is the one create shape that can carry a read-only column without the caller typing it: cloneData copies the source row (approval_status: 'approved' included) and applies overrides on top, then inserts. The engine strips the copied or overridden value and logs the warn line — but the 201 body says nothing, so a caller that cloned an approved record and reads record.approval_status: 'draft' back has no field in the response telling it why. Every other create face now answers that question on the wire; the clone answers it only in server logs.
Suggested shape (a decision, not a patch)
Either add an optional droppedFields member to CloneDataResponseSchema (same shape as CreateDataResponseSchema's), wire the listener in cloneData exactly as createData does, and flip the two conformance pins and the firing-control pin — a @objectstack/spec surface widening, minor under the bump-level rule — or record that a clone deliberately does not report drops and say why on the schema. Both are legitimate; neither is a delegation detail of #14147.
Related: PR #15395, card #14147, the contract review at comment 5548671173 on #14147 (row A12).
Generated by Claude Code
Found while patching PR #15395 (the create-side static-
readonlystrip moving intoengine.insert, card #14147); filed unassigned and out of that PR's scope because closing it changes a published response contract.What is true today
After the 2026-09-03 ruling (option C on #14147), every DataProtocol create face hands the caller's payload to
engine.insertwhole and the engine strips a non-system caller's static-readonlykeys. Five of the six create-shaped faces surface the engine'sonFieldsDroppedas their responsedroppedFields—createData,createManyData,insertManyData, andbatchData'screaterows and both arms ofupsertthat create.cloneDatadoes not. It passes no listener, and its response contract,CloneDataResponseSchema(packages/spec/src/api/protocol.zod.ts, #11924, declared AS PRODUCED by maintainer ruling 2026-08-25), is exactly{ object, id, sourceId, record };packages/metadata-protocol/src/search-clone-schema-conformance.test.tsasserts in so many words that the body carries nodroppedFields. PR #15395 pins that absence with its reason (protocol.readonly-insert.test.ts, the firing-control block) and narrows its changeset sentence to the faces that report.Why it is worth a card
A clone IS a create, and it is the one create shape that can carry a read-only column without the caller typing it:
cloneDatacopies the source row (approval_status: 'approved'included) and appliesoverrideson top, then inserts. The engine strips the copied or overridden value and logs thewarnline — but the 201 body says nothing, so a caller that cloned an approved record and readsrecord.approval_status: 'draft'back has no field in the response telling it why. Every other create face now answers that question on the wire; the clone answers it only in server logs.Suggested shape (a decision, not a patch)
Either add an optional
droppedFieldsmember toCloneDataResponseSchema(same shape asCreateDataResponseSchema's), wire the listener incloneDataexactly ascreateDatadoes, and flip the two conformance pins and the firing-control pin — a@objectstack/specsurface widening,minorunder the bump-level rule — or record that a clone deliberately does not report drops and say why on the schema. Both are legitimate; neither is a delegation detail of #14147.Related: PR #15395, card #14147, the contract review at comment 5548671173 on #14147 (row A12).
Generated by Claude Code