You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Observation-class finding, surfaced while verifying #4676 (the placeholder chain). No live defect today — all three known instances are resolved. Filed unassigned; finding, deliberately notpm:queue.
The class
A field designer offers a control that writes a key FieldSchema refuses by name. The author sees the control work (and, in metadata-admin, sees the preview render it), then PUT /api/v1/meta/object/:name returns a hard 422 INVALID_METADATA that blocks every subsequent save of that object until the key is stripped.
This repo has now filed that same shape three times:
packages/plugin-designer/src/MetadataFieldsPage.tsx — a second, independently-maintained RETIRED_FIELD_KEYS = ['indexed']
prose tombstones in packages/types/src/field-types.ts, MetadataService.ts, metadataConverters.ts, FieldDesigner.tsx
Every one of these is written after an instance is discovered in production. Nothing detects the next one.
What is missing
No test or gate asserts: every key the field designers can emit is a key FieldSchema accepts.
Measured on origin/main @ 9850c6e4e, with controls:
grep -rn "FieldSchema" packages/app-shell packages/plugin-designer — 30 hits, all prose about indexed plus a local structural interface ServerFieldSchema (plugin-designer's own subset type, not the spec's schema). Zero executable assertions against the spec's FieldSchema.
Control: grep -c "@objectstack/spec/data" across packages/apps returns 127 hits, so the repo does import that subpath heavily — the zero above is a real absence, not a broken instrument.
scripts/check-spec-symbol-derivation.mjs is not this guard: it checks symbol NAME collisions between app-shell and the spec, a different dimension entirely.
A parity test on the designer write path, not a per-key allowlist. The tractable version: take what the designer draft I/O actually emits and parse it. Measured working during the #4676 verification (throwaway probe, not landed):
The controls matter: without them a green parity test proves nothing, because a loosened or wrongly-resolved schema accepts everything.
Note the harder half, stated honestly rather than hidden: enumerating the full set of keys the two designers can emit is not mechanical — ObjectFieldInspector writes through many conditional patchDef({...}) calls, and fromDesignerField spreads prev verbatim to preserve unknown keys. A guard over the statically declared payload shapes (FieldMetadataPayload, DesignerFieldDefinition, ServerFieldSchema) is the achievable version and would have caught all three instances above.
Why this is not urgent
All three known instances are closed, and the two RETIRED_FIELD_KEYS sets are correct as they stand. The cost of the gap is that instance four is found by a user hitting a save-blocking 422, not by CI.
Observation-class finding, surfaced while verifying #4676 (the
placeholderchain). No live defect today — all three known instances are resolved. Filed unassigned;finding, deliberately notpm:queue.The class
A field designer offers a control that writes a key
FieldSchemarefuses by name. The author sees the control work (and, in metadata-admin, sees the preview render it), thenPUT /api/v1/meta/object/:namereturns a hard 422INVALID_METADATAthat blocks every subsequent save of that object until the key is stripped.This repo has now filed that same shape three times:
indexeddistance_metricplaceholder@objectstack/spec17.1.0Three instances, three different correct answers. That is a class, not a coincidence.
What exists today
Per-key tombstones, each keyed to one literal:
packages/app-shell/src/views/metadata-admin/previews/object-fields-io.ts—RETIRED_FIELD_KEYS = ['indexed']packages/plugin-designer/src/MetadataFieldsPage.tsx— a second, independently-maintainedRETIRED_FIELD_KEYS = ['indexed']packages/types/src/field-types.ts,MetadataService.ts,metadataConverters.ts,FieldDesigner.tsxEvery one of these is written after an instance is discovered in production. Nothing detects the next one.
What is missing
No test or gate asserts: every key the field designers can emit is a key
FieldSchemaaccepts.Measured on
origin/main@9850c6e4e, with controls:grep -rn "FieldSchema" packages/app-shell packages/plugin-designer— 30 hits, all prose aboutindexedplus a local structural interfaceServerFieldSchema(plugin-designer's own subset type, not the spec's schema). Zero executable assertions against the spec'sFieldSchema.grep -c "@objectstack/spec/data"acrosspackages/appsreturns 127 hits, so the repo does import that subpath heavily — the zero above is a real absence, not a broken instrument.scripts/check-spec-symbol-derivation.mjsis not this guard: it checks symbol NAME collisions between app-shell and the spec, a different dimension entirely.Shape of the fix
A parity test on the designer write path, not a per-key allowlist. The tractable version: take what the designer draft I/O actually emits and parse it. Measured working during the #4676 verification (throwaway probe, not landed):
The controls matter: without them a green parity test proves nothing, because a loosened or wrongly-resolved schema accepts everything.
Note the harder half, stated honestly rather than hidden: enumerating the full set of keys the two designers can emit is not mechanical —
ObjectFieldInspectorwrites through many conditionalpatchDef({...})calls, andfromDesignerFieldspreadsprevverbatim to preserve unknown keys. A guard over the statically declared payload shapes (FieldMetadataPayload,DesignerFieldDefinition,ServerFieldSchema) is the achievable version and would have caught all three instances above.Why this is not urgent
All three known instances are closed, and the two
RETIRED_FIELD_KEYSsets are correct as they stand. The cost of the gap is that instance four is found by a user hitting a save-blocking 422, not by CI.Refs: #4676 (where this surfaced), #4644, #4687, #5138, objectstack#9019.