Skip to content

A per-option default written in a *.form.ts is silently inert — the metadata-admin renderer never reads it #6263

Description

@yinlianghui

Blocked-by: objectstack-ai/objectstack#12868

Filing unassigned — recording, not claiming. Found while verifying #6247 (the per-option visibleWhen of the same shape); this is the sibling key on the same object and is out of scope for that card's fix.

⚠️ RULED 2026-08-28 (甲) — and this card's premise was FALSE at the platform level

Do not implement or re-measure from the body below. Two things changed under it:

  1. default is NOT inert platform-wide. objectstack#7246 (2026-08-10) asked this same question and the maintainer overrode a delegated retire-ruling verbatim: "enforce." PR objectstack#7388 merged, and the engine has honoured option-level default on the insert path since — applyFieldDefaults falls back to the option marked default: true when the field declares no defaultValue, with defaultValue winning when both are declared, and a precedence pin. So "declared, honoured by no surface" is true only of the form-view / metadata-admin surface, not of the platform.
    That same ruling carried an objectui rider, verbatim: "the console needs no second default contract" — so the metadata-admin renderer not seeding from option default is by ruling, not by omission.
  2. The file this card describes no longer exists in that shape. PR Honour per-option visibleWhen in the metadata-admin renderer #6618 (card A per-option visibleWhen written in a *.form.ts is silently inert — the metadata-admin renderer never reads it #6247, merged 2026-08-27) rewrote FormFieldSpec.options: it was the last hand-written element type in form-spec.ts, mirroring 3 of the spec's 5 keys and dropping the other 2 by silence. It now derives from the spec type with its narrowings named in an OmitvisibleWhen re-pointed to the local predicate, and defaultdropped and now saying so in the type. So default is currently declared inert rather than invisibly inert.

The ruling (maintainer, 2026-08-28, verbatim 「同意」): option 甲 — narrow default out of the FORM-VIEW options vocabulary only, Omit-derived at the form-view reference site, with ⛔ SelectOptionSchema itself untouched, and tombstone prose pointing authors at the object definition (field-level defaultValue, or the object option's default: true — both enforced there).

Execution carrier: objectstack-ai/objectstack#12868 (domain:spec, clause-② both limbs, contract-review tier), with a census-first precondition: count form-view corpus usage with a positive control, and a non-zero count means stop and report.

On unlock: re-verify on the merged ref. The ui-side derived type already tombstones the key (PR #6618), so the residual ui work is likely nil and this card closes on verification.

⭐ Worth keeping: option B (retire the key outright) was recommended and accepted in a batch, and was caught only by the pre-filing dedup that surfaced objectstack#7246. Retiring it would have reverted a ruled, implemented, pinned behaviour. The dedup pass is what stopped it.

The observation

SelectOptionSchema (@objectstack/spec@17.2.0, spec/data) declares five keys:

label · value · color · default · visibleWhen

Measured against the installed package — FormViewSchema.safeParse of a whole *.form.ts carrying options: [{ label, value, default: true }] is ACCEPTED. The schema is z.core.$strict, so this is a genuine declaration and not a passthrough artefact: the same parse with an undeclared key is REFUSED with unrecognized_keys.

The metadata-admin renderer has exactly three controls that consume fieldSpec.options, and none of them reads default:

controlsite
Select (builtin scalar chain)packages/app-shell/src/views/metadata-admin/SchemaForm.tsx:1666-1681
MultiSelectWidgetpackages/app-shell/src/views/metadata-admin/widgets.tsx:1128-1129
colorPaletteOptionsColorSwatchGroupWidgetpackages/app-shell/src/views/metadata-admin/widgets.tsx:2183-2184

All three map label / value (and color in two of them) and drop the rest.

objectui's own local mirror of the authoring type also omits it — FormFieldSpec.options in packages/app-shell/src/views/metadata-admin/form-spec.ts:103 is declared as Array<{ label: string; value: string; color?: string }>, i.e. three of the spec's five keys. That file's header is explicit that every deliberate narrowing is "named in an Omit list and restated once, next to its reason"; the options element type is hand-written and its narrowing is not among the named ones, so it is not currently distinguishable from an oversight. (⚠️ Superseded by PR #6618 — see the box above; the narrowing is now named.)

Why it is worth a card

Same ADR-0049 declared-but-unenforced shape as #6247, and the same costly direction: an author writing default: true on an option is asking for a pre-selected value, gets a clean parse, and gets an empty picker. Nothing tells them, and no gate notices.

Note this is not the same as #4047 (create dialog ignoring a field's defaultValue), which is closed and concerns the FIELD-level key on the runtime surface — this is the per-OPTION key on the metadata-admin surface.

Zero *.form.ts in @objectstack/spec@17.2.0 uses the key today, so nothing is broken right now; this is about the next author who reaches for it.

Directions (⛔ SUPERSEDED by the 甲 ruling above — retained as the record of what was asked)

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:on-holdpriority:p2

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions