Surfaced by the key-level parity gate built for #5761 (scripts/check-designer-field-key-parity.mjs). Filed rather than fixed, per #5761's ruling: the gate surfaces instances and each surfaced key gets its own card, because #4644indexed, #4687distance_metric and #4676placeholder took three different correct resolutions.
The key
formula is not in FieldSchema's accept set. Measured against the installed @objectstack/spec 17.2.0:
FieldSchema.safeParse({ type:'formula', label:'Tax', formula:'price * quantity' })
=> success = false
=> [{ code: 'unrecognized_keys', keys: ['formula'] }]
The spec carries two keys for this concept, and that is what makes it non-mechanical:
expression — Formula expression (CEL). e.g. F`record.amount * 0.1`
returnType — Inferred value type of a formula field (number/text/boolean/date)
Why this is live
packages/plugin-designer/src/FieldDesigner.tsx:312 — live textarea control, visibleWhen: "record.type == 'formula'", placeholder e.g. price * quantity.packages/plugin-designer/src/FieldDesigner.tsx:222,243 — create and update paths both write it.fromDesignerField (MetadataFieldsPage.tsx) and toFieldPayload (MetadataService.ts:105) both write it onto the shape that is PUT.
An author creating a formula field and typing an expression writes a key the API refuses: PUT /api/v1/meta/object/:name returns a hard 422 INVALID_METADATA that blocks every subsequent save of that object until the key is cleared.
Why a rename is not obviously the answer
Three things a resolver has to settle, none of which this tooling card can:
- Expression language. The designer's placeholder is
price * quantity; the spec's expression is CEL, documented as F`record.amount * 0.1` — bare field names and a different root. Renaming the key alone would ship syntactically invalid expressions under a valid key name, trading a loud 422 for a silent runtime failure. That is strictly worse. returnType. The spec models it as a separate key the designer has no control for. Whether it is authored, inferred, or omitted is a design decision.- The
formulafield type itself (FieldDesigner.tsx:91,120) needs to be checked against the spec's FieldType vocabulary in the same pass.
The read direction is broken symmetrically too: toDesignerField reads raw.formula, but a spec-parsed server sends expression, so an existing formula field loads with an empty box.
Verification
pnpm check:designer-field-key-parity reports it; it is recorded in that gate's KNOWN_UNPARSEABLE_KEYS ledger pointing here. The ledger ratchets both ways, so a fix that leaves the entry behind is also red.
Refs: #5761 (the gate) · #4644 · #4687 · #4676
Surfaced by the key-level parity gate built for #5761 (
scripts/check-designer-field-key-parity.mjs). Filed rather than fixed, per #5761's ruling: the gate surfaces instances and each surfaced key gets its own card, because #4644indexed, #4687distance_metricand #4676placeholdertook three different correct resolutions.The key
formulais not inFieldSchema's accept set. Measured against the installed@objectstack/spec17.2.0:The spec carries two keys for this concept, and that is what makes it non-mechanical:
Why this is live
packages/plugin-designer/src/FieldDesigner.tsx:312— live textarea control,visibleWhen: "record.type == 'formula'", placeholdere.g. price * quantity.packages/plugin-designer/src/FieldDesigner.tsx:222,243— create and update paths both write it.fromDesignerField(MetadataFieldsPage.tsx) andtoFieldPayload(MetadataService.ts:105) both write it onto the shape that is PUT.An author creating a formula field and typing an expression writes a key the API refuses:
PUT /api/v1/meta/object/:namereturns a hard 422INVALID_METADATAthat blocks every subsequent save of that object until the key is cleared.Why a rename is not obviously the answer
Three things a resolver has to settle, none of which this tooling card can:
price * quantity; the spec'sexpressionis CEL, documented asF`record.amount * 0.1`— bare field names and a different root. Renaming the key alone would ship syntactically invalid expressions under a valid key name, trading a loud 422 for a silent runtime failure. That is strictly worse.returnType. The spec models it as a separate key the designer has no control for. Whether it is authored, inferred, or omitted is a design decision.formulafield type itself (FieldDesigner.tsx:91,120) needs to be checked against the spec'sFieldTypevocabulary in the same pass.The read direction is broken symmetrically too:
toDesignerFieldreadsraw.formula, but a spec-parsed server sendsexpression, so an existing formula field loads with an empty box.Verification
pnpm check:designer-field-key-parityreports it; it is recorded in that gate'sKNOWN_UNPARSEABLE_KEYSledger pointing here. The ledger ratchets both ways, so a fix that leaves the entry behind is also red.Refs: #5761 (the gate) · #4644 · #4687 · #4676