Provenance: measured while implementing objectui#6875. Filed unassigned, as a finding
rather than a fix card — the measurement is clean, the disposition is a judgement call
about back-compat that a maintainer should make.
What's measured
@objectstack/spec 17.2.0's FieldSchema is a strict object of 71 properties.
Enumerated off FieldSchema.shape on the installed package, with name / type / label
as the positive control:
DECLARED : displayField, descriptionField, lookupColumns, lookupFilters, reference
absent : display_field, description_field, lookup_columns, lookup_filters,
reference_to, reference_field, id_field, idField, referenceTo, titleFormat
Being strict, an absent key does not pass through — it parses to unrecognized_keys,
the same code a nonsense key gets, and PUT /api/v1/meta/object/:name refuses the
document. Measured directly:
FieldSchema.safeParse({ name:'account', type:'lookup', reference_field:'name' })
→ success: false, code: 'unrecognized_keys', keys: ['reference_field']
FieldSchema.safeParse({ name:'account', type:'lookup', displayField:'name' })
→ success: true
The producer repo agrees. Across all of objectstack (whole repo, not just src):
| key | files | | key (control) | files |
|---|
display_field | 3 | | displayField | 68 |
description_field | 1 | | descriptionField | 17 |
lookup_columns | 2 | | lookupColumns | 13 |
lookup_filters | 4 | | lookupFilters | 50 |
reference_field | 0 | | | |
Every snake_case hit is prose, not a producer: release notes, a CHANGELOG line, and
packages/spec/liveness/field.json notes describing what objectui reads. The platform's
own published authoring rule states the split explicitly
(skills/objectstack-data/rules/naming.md): field KEYS are snake_case, schema PROPERTIES
are camelCase, with lookup_filters: [] shown as ❌ and lookupFilters: [...] as ✅.
Nothing manufactures a snake spelling on the way in, either. getObjectSchema in
@object-ui/data-objectstack is the choke point every schema read passes through, and its
only key rewrites are normalizeSchemaReferenceKeys (the reference ⇄ reference_to
pair) and applyFieldWidgetOverrides (widget).
The asymmetry this leaves behind
After objectui#6875, ObjectGrid's copy set is
reference_to, reference, display_field, displayField, description_field,
descriptionField, lookupColumns, lookupFilters, lookup_filters, id_field.
Four of those — display_field, description_field, lookup_filters, id_field —
cannot be produced by a spec-compliant author. They are carried with verdict legacy-alias
in packages/plugin-grid/src/relationalMetaKeys.ts, and a gate asserts mechanically that
each is genuinely absent from FieldSchema so the claim cannot go stale. The consumers in
@object-ui/fields read the snake spelling FIRST in every chain
(display_field || displayField || reference_field), i.e. the dialect the contract refuses
outranks the one it declares.
reference_to is the one honest exception: not spec-declared, but stamped onto every def
by the adapter's choke point, so it really is present at runtime.
Why this is a finding and not a fix
AGENTS.md #0.1 is explicit that a tolerant renderer fossilises a second de-facto contract,
and objectui#6531 already removed one such consumer-side alias (objectDef.titleField) on
exactly this reasoning — spec's strict object refuses it, so no producer ships it. The same
argument applies to these four.
⚠️ What the measurement does NOT cover, and why it should not be acted on blindly:
- It bounds
objectstack + objectui. A host application outside them may hand-feed a
DataSource whose getObjectSchema returns snake_case defs; nothing here can see that. - Metadata authored before
FieldSchema became strict is not measurable from here — if
any is stored, it would still be served. - Retiring a shipped key is its own adjudication in this repo (objectui#6711 and
objectui#6874 each were), and the ordering matters: the chains read snake FIRST, so
removing the snake leg changes which value wins, not merely which keys exist.
Suggested disposition
Enforce-or-remove triage per ADR-0049's spirit, one key at a time, starting with the
question this finding cannot answer: is there a real host that feeds snake_case field defs?
If not, the chains in @object-ui/fields collapse to the spec spelling and the four
legacy-alias verdicts become deletions.
Generated by Claude Code
Provenance: measured while implementing objectui#6875. Filed unassigned, as a
findingrather than a fix card — the measurement is clean, the disposition is a judgement call
about back-compat that a maintainer should make.
What's measured
@objectstack/spec17.2.0'sFieldSchemais a strict object of 71 properties.Enumerated off
FieldSchema.shapeon the installed package, withname/type/labelas the positive control:
Being strict, an absent key does not pass through — it parses to
unrecognized_keys,the same code a nonsense key gets, and
PUT /api/v1/meta/object/:namerefuses thedocument. Measured directly:
The producer repo agrees. Across all of
objectstack(whole repo, not justsrc):display_fielddisplayFielddescription_fielddescriptionFieldlookup_columnslookupColumnslookup_filterslookupFiltersreference_fieldEvery snake_case hit is prose, not a producer: release notes, a CHANGELOG line, and
packages/spec/liveness/field.jsonnotes describing what objectui reads. The platform'sown published authoring rule states the split explicitly
(
skills/objectstack-data/rules/naming.md): field KEYS are snake_case, schema PROPERTIESare camelCase, with
lookup_filters: []shown as ❌ andlookupFilters: [...]as ✅.Nothing manufactures a snake spelling on the way in, either.
getObjectSchemain@object-ui/data-objectstackis the choke point every schema read passes through, and itsonly key rewrites are
normalizeSchemaReferenceKeys(thereference⇄reference_topair) and
applyFieldWidgetOverrides(widget).The asymmetry this leaves behind
After objectui#6875,
ObjectGrid's copy set isreference_to,reference,display_field,displayField,description_field,descriptionField,lookupColumns,lookupFilters,lookup_filters,id_field.Four of those —
display_field,description_field,lookup_filters,id_field—cannot be produced by a spec-compliant author. They are carried with verdict
legacy-aliasin
packages/plugin-grid/src/relationalMetaKeys.ts, and a gate asserts mechanically thateach is genuinely absent from
FieldSchemaso the claim cannot go stale. The consumers in@object-ui/fieldsread the snake spelling FIRST in every chain(
display_field || displayField || reference_field), i.e. the dialect the contract refusesoutranks the one it declares.
reference_tois the one honest exception: not spec-declared, but stamped onto every defby the adapter's choke point, so it really is present at runtime.
Why this is a finding and not a fix
AGENTS.md #0.1 is explicit that a tolerant renderer fossilises a second de-facto contract,
and objectui#6531 already removed one such consumer-side alias (
objectDef.titleField) onexactly this reasoning — spec's strict object refuses it, so no producer ships it. The same
argument applies to these four.
objectstack+objectui. A host application outside them may hand-feed aDataSourcewhosegetObjectSchemareturns snake_case defs; nothing here can see that.FieldSchemabecame strict is not measurable from here — ifany is stored, it would still be served.
objectui#6874 each were), and the ordering matters: the chains read snake FIRST, so
removing the snake leg changes which value wins, not merely which keys exist.
Suggested disposition
Enforce-or-remove triage per ADR-0049's spirit, one key at a time, starting with the
question this finding cannot answer: is there a real host that feeds snake_case field defs?
If not, the chains in
@object-ui/fieldscollapse to the spec spelling and the fourlegacy-aliasverdicts become deletions.Generated by Claude Code