Skip to content

finding(plugin-grid, fields): the snake_case half of the lookup dialect — display_field, description_field, lookup_filters, id_field — is refused by the strict FieldSchema, so no spec-compliant producer can emit it #7155

Description

@os-warren

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 referencereference_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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions