Observation-class finding, surfaced by the key-level parity gate built for #5761 (scripts/check-designer-field-key-parity.mjs). Filed unassigned, finding, deliberately notpm:queue. Filed rather than fixed per #5761's ruling.
The key
sortOrder is not in FieldSchema's accept set. The spec has sortable (a boolean — "whether field is sortable in list views"), which is a different concept, and no field-level ordering key at all.
FieldSchema.safeParse({ type:'text', label:'L', sortOrder: 3 })
=> success = false
=> [{ code: 'unrecognized_keys', keys: ['sortOrder'] }]
Why it is latent rather than live
sortOrder is declared on both packages/app-shell/src/services/MetadataService.ts (FieldMetadataPayload, line 67) and packages/types/src/designer.ts (DesignerFieldDefinition), and toFieldPayload (MetadataService.ts:108) writes sortOrder: field.sortOrder onto the payload that saveFields PUTs.
But a repo-wide scan finds no writer that populates a field's sortOrder. The sortOrder hits in ObjectManager.tsx are the object-level property, a different schema. So toFieldPayload emits sortOrder: undefined, JSON.stringify drops the key, and the PUT never carries it.
That is a one-line change away from being live: any designer that adds field reordering and persists an index — the obvious next feature for a drag-to-reorder field list — turns this into the #4644 shape (hard 422 INVALID_METADATA blocking every subsequent save of the object). The gate catches it as declared today precisely so the change that populates it is not the moment anyone finds out.
The likely resolutions, unadjudicated
This reads like the #4687distance_metric shape — a declaration with zero readers and zero writers, where the right answer was to delete it. But there is a real alternative: the spec models field order by declaration order in the object's fields record, so a designer that wants explicit ordering may need to reorder that record rather than carry an index. Which of those is right, and whether DesignerFieldDefinition.sortOrder should survive as UI-only state, is not this card's call.
Note the two declarations differ in reach and may resolve differently: on DesignerFieldDefinition it is in-memory UI state; on FieldMetadataPayload it is on the wire.
Verification
pnpm check:designer-field-key-parity reports it; recorded in that gate's KNOWN_UNPARSEABLE_KEYS ledger pointing here. If it is deleted, the ledger entry must go with it — a stale entry is red too.
Refs: #5761 (the gate) · #4687 · #4644 · #6041 · #6043
Observation-class finding, surfaced by the key-level parity gate built for #5761 (
scripts/check-designer-field-key-parity.mjs). Filed unassigned,finding, deliberately notpm:queue. Filed rather than fixed per #5761's ruling.The key
sortOrderis not inFieldSchema's accept set. The spec hassortable(a boolean — "whether field is sortable in list views"), which is a different concept, and no field-level ordering key at all.Why it is latent rather than live
sortOrderis declared on bothpackages/app-shell/src/services/MetadataService.ts(FieldMetadataPayload, line 67) andpackages/types/src/designer.ts(DesignerFieldDefinition), andtoFieldPayload(MetadataService.ts:108) writessortOrder: field.sortOrderonto the payload thatsaveFieldsPUTs.But a repo-wide scan finds no writer that populates a field's
sortOrder. ThesortOrderhits inObjectManager.tsxare the object-level property, a different schema. SotoFieldPayloademitssortOrder: undefined,JSON.stringifydrops the key, and the PUT never carries it.That is a one-line change away from being live: any designer that adds field reordering and persists an index — the obvious next feature for a drag-to-reorder field list — turns this into the #4644 shape (hard 422
INVALID_METADATAblocking every subsequent save of the object). The gate catches it as declared today precisely so the change that populates it is not the moment anyone finds out.The likely resolutions, unadjudicated
This reads like the #4687
distance_metricshape — a declaration with zero readers and zero writers, where the right answer was to delete it. But there is a real alternative: the spec models field order by declaration order in the object'sfieldsrecord, so a designer that wants explicit ordering may need to reorder that record rather than carry an index. Which of those is right, and whetherDesignerFieldDefinition.sortOrdershould survive as UI-only state, is not this card's call.Note the two declarations differ in reach and may resolve differently: on
DesignerFieldDefinitionit is in-memory UI state; onFieldMetadataPayloadit is on the wire.Verification
pnpm check:designer-field-key-parityreports it; recorded in that gate'sKNOWN_UNPARSEABLE_KEYSledger pointing here. If it is deleted, the ledger entry must go with it — a stale entry is red too.Refs: #5761 (the gate) · #4687 · #4644 · #6041 · #6043