Skip to content

app-shell FormSectionSpec re-declares fields as REQUIRED, so the spec-legal { group } section does not compile — and SchemaForm's three unguarded s.fields reads are a fourth form-section consumer #8725

Description

@os-justin

Found while implementing objectui#8641 (the apps/console FormPage half of form.sections[].group). Filed rather than fixed there: it is a different package, a different renderer and a widening of a shared authoring type, so it is outside that card's scope.

What was measured

@objectstack/spec 17.3.0 declares group on FormSectionSchema and makes fields OPTIONAL — a section declares its members exactly one way (objectstack#13855, ADR-0085 §5). Measured against the installed 17.3.0:

FormSectionSchema.safeParse({ group: 'contact_info' })
  -> ACCEPT  { collapsible: false, collapsed: false, columns: 1, group: 'contact_info' }

packages/app-shell/src/views/metadata-admin/form-spec.ts derives FormSectionSpec from that same spec symbol and then re-declares fields as REQUIRED:

export type FormSectionSpec =
  & Omit<FormSection, 'fields' | 'label' | 'description' | 'visibleWhen' | 'visibleOn'>
  & {
    ...
    /** The authored field list. Element type is the converged leaf (objectui#5542). */
    fields: Array<string | FormFieldSpec>;
  };

Two consequences, both static readings of the current main:

  1. The spec-legal shape does not compile. A TypeScript author writing { group: 'contact_info' } into a FormSectionSpec is refused by the very type whose header says it "describes what an AUTHOR WROTE, so it stays as wide as the document". objectui#8641's own pin fixture has to cast around it. This is the same declaration defect PR fix(plugin-form,types): resolve form.sections[].group through the single field-group assembler, and bound the section loop that blanked the form #8644 fixed on the sibling type: ObjectFormSection gained group and made fields optional so the spec-legal shape compiles.

  2. SchemaForm is a FOURTH form-section renderer, and its fields reads are unguarded. packages/app-shell/src/views/metadata-admin/SchemaForm.tsx:

    • line 786 for (const f of s.fields) {
    • line 1030 const fields = s.fields.map(normaliseField)
    • line 1147 s.fields.map(normaliseField).some(...)

    All three are above the JSX they feed, so a section carrying no fields throws Cannot read properties of undefined (reading 'map') out of the component body rather than degrading to an empty section — the same above-the-loop shape that blanked SimpleObjectForm before fix(plugin-form,types): resolve form.sections[].group through the single field-group assembler, and bound the section loop that blanked the form #8644, which a per-section boundary could not have contained either.

Not measured, stated as such

Reachability was not measured. The reads above are unguarded by inspection; whether a { group } section can reach SchemaForm today (its inputs are this repo's own *.form.ts create/edit schemas, which do not author group) was not established, and neither was a DOM reproduction. Whoever takes this should reproduce first, exactly as objectui#8641 required of itself — a defect established only by reading is the thing this queue has been wrong about before.

Why it is a decision and not a mechanical widening

Making fields optional is one character in form-spec.ts and then a choice at each of the three read sites, and the choice is the card:

The standing constraint applies either way (objectui#7051, objectstack#13855): no assembly rule may be re-implemented on the objectui side. @object-ui/plugin-form now publishes resolveSectionGroupReferences for exactly this, so the second option is an import rather than a new derivation.

Refs


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

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpackage: app-shellpm:blocked

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions