The severable half of objectui#6240, recorded as its own card because the resolution is a public-signature judgement rather than a shape conversion.
The measurement
ObjectSchema.fields is not merely typed, it is REQUIRED. Measured against the installed @objectstack/spec 17.2.0:
ObjectSchema.safeParse({ name: 'account', label: 'Account' })
=> success = false invalid_type @ fields ("expected record, received undefined")
objectui#6240 fixed the SHAPE: when a caller passes existingFields, toObjectPayload now emits the name-keyed map and the body parses. When a caller does NOT pass it, fields is omitted and the body is still refused — unchanged from before that card, and deliberately so.
Why objectui#6240 did not simply default to {}
Because {} parses green and PUT /api/v1/meta/object/:name is an UPSERT. Emitting {} for a caller that just did not supply the argument would land as "this object has no fields" and delete every field of the object on a save that only meant to rename it — trading a loud, harmless 422 for silent data loss. That is pinned as a positive control (omits fields entirely when the caller supplied none — it does NOT write {}), with an ablation showing that defaulting to {} reds that one case.
The decision this needs
saveObject(obj, existingFields?: FieldMetadataPayload[]) is public API — MetadataService is reachable from app-shell's entry through useMetadataService(): MetadataService | null, so its signature is part of the published surface even though the class is not re-exported by name. Three readings, none of them free:
- Make the fields argument required. Honest — the method cannot write a valid document without it — but a breaking signature change for any consumer calling
saveObject(obj). - Fetch and merge, the way
saveFields already does.saveFields GETs the current document and spreads it, so an object save could preserve the stored fields instead of asking the caller to hand them back. Costs a request; makes the parameter redundant, which then wants retiring on its own terms (ADR-0049 shape). - Leave it and refuse client-side. Throw before the request when no fields are supplied, so the author gets the diagnosis without a round trip. Same outcome as today, better message, still a behaviour change for callers relying on the request being issued.
No in-repo caller passes existingFields at all today (the only call sites are tests), so the population this breaks is entirely external consumers — which is what makes it a judgement rather than a mechanical fix.
Refs: objectui#6240 (the shape half, landed) · objectui#5761 (parity family, coverage note 4)
Generated by Claude Code
The severable half of objectui#6240, recorded as its own card because the resolution is a public-signature judgement rather than a shape conversion.
The measurement
ObjectSchema.fieldsis not merely typed, it is REQUIRED. Measured against the installed@objectstack/spec17.2.0:objectui#6240 fixed the SHAPE: when a caller passes
existingFields,toObjectPayloadnow emits the name-keyed map and the body parses. When a caller does NOT pass it,fieldsis omitted and the body is still refused — unchanged from before that card, and deliberately so.Why objectui#6240 did not simply default to
{}Because
{}parses green andPUT /api/v1/meta/object/:nameis an UPSERT. Emitting{}for a caller that just did not supply the argument would land as "this object has no fields" and delete every field of the object on a save that only meant to rename it — trading a loud, harmless 422 for silent data loss. That is pinned as a positive control (omits fields entirely when the caller supplied none — it does NOT write {}), with an ablation showing that defaulting to{}reds that one case.The decision this needs
saveObject(obj, existingFields?: FieldMetadataPayload[])is public API —MetadataServiceis reachable from app-shell's entry throughuseMetadataService(): MetadataService | null, so its signature is part of the published surface even though the class is not re-exported by name. Three readings, none of them free:saveObject(obj).saveFieldsalready does.saveFieldsGETs the current document and spreads it, so an object save could preserve the storedfieldsinstead of asking the caller to hand them back. Costs a request; makes the parameter redundant, which then wants retiring on its own terms (ADR-0049 shape).No in-repo caller passes
existingFieldsat all today (the only call sites are tests), so the population this breaks is entirely external consumers — which is what makes it a judgement rather than a mechanical fix.Refs: objectui#6240 (the shape half, landed) · objectui#5761 (parity family, coverage note 4)
Generated by Claude Code