Skip to content

[finding] SelectOption.default is an authorable key that nothing on the insert path ever reads — the select idiom is inert #7246

Description

@yinlianghui

Found while implementing objectstack-ai/objectui#4047 (objectstack-ai/objectui#4068), which had to decide which of two default spellings a create form honours. Filed unassigned; observation-class, severity for triage.

The finding

SelectOptionSchema declares an authorable default:

// packages/spec/src/data/field.zod.ts:140default: z.boolean().optional().describe('Is default option'),

so this is spec-valid and reads like a declaration of the initial value:

status: Field.select({label: 'Status',options: [{label: 'Draft',value: 'draft',default: true},{label: 'Approved',value: 'approved'},],}),

Nothing applies it. The insert path — ObjectQL.applyFieldDefaults, packages/objectql/src/engine.ts — resolves f.defaultValue and nothing else: Expression envelopes, then the DEFAULT_VALUE_TOKENS (current_user, NOW()), then static literals. It never looks at options. So a create that omits the field stores null, not draft.

The only consumer of the key found anywhere in the repo is a nullability analysis:

// packages/lint/src/validate-expressions.ts — isNullableField()if(Array.isArray(options)&&options.some((o)=>(oasAnyRec).default===true)){returnfalse;// treated as always-valued}

That comment calls it "the select idiom", and the verdict it feeds is build-breaking. So the one place that reads the key concludes the column is always valued — on the strength of a declaration the engine does not honour. A field defaulted only that way is in fact nullable, and a predicate reading it can be silenced by this heuristic.

Why it surfaced now

objectstack-ai/objectui#4047's reporter wrote both spellings on the same field — defaultValue: 'draft'and the option's default: true — which is what an author does when it is not clear which one is load-bearing. objectstack-ai/objectui#4068 deliberately honours defaultValue only, on the grounds that it is the key the server enforces: a console that also seeded from option-level default would preselect values the server would never have applied, i.e. a UI-only second default contract. That reasoning holds only as long as this stays true, so it is worth recording either way.

Dispositions, for triage

  • Enforce it — have applyFieldDefaults fall back to the option marked default: true when the field declares no defaultValue. Makes the lint heuristic honest and the idiom real. Needs a precedence rule when both are declared and disagree (defaultValue should win — it is the more specific declaration), and the driver DDL side wants a look.
  • Retire it (ADR-0049 enforce-or-remove; spec-property-retirement playbook) — drop the key, and with it the isNullableField branch that trusts it.
  • Leave it declared and narrow the lint — the least appealing: the key keeps looking meaningful to authors while doing nothing.

No claim is made here about which; the startup-focus question ("does a real business scenario pull on option-level default, given defaultValue already covers it?") is the one that decides it, and that is a maintainer call.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions