Status: Proposed (2026-06-15)
Deciders: ObjectStack Protocol Architects
Builds on: ADR-0014 (record form field type), ADR-0017 (object-bound views), ADR-0049 (declared ≠ enforced discipline)
Consumers: @objectstack/spec (ui/view.zod.ts FormViewSchema), @object-ui/plugin-form (ObjectForm + TabbedForm/WizardForm/SplitForm/DrawerForm/ModalForm), @object-ui/app-shell (RecordFormPage, AppContent, useActionModal), examples + templates form views.
Surfaced by: framework #1890 (viewschema liveness audit) → objectui #1762 (full-page form layout fix) → the modelling debt found while browser-verifying it.
FormViewSchema.type is a single enum holding two orthogonal dimensions:
| value | real meaning | dimension |
|---|---|---|
simple / tabbed / wizard |
the form's internal layout | Layout |
drawer / modal |
where the form is opened | Presentation / container |
split |
a list+detail master-detail mode | neither — a view mode |
Because it's one field, "a modal containing a tabbed form" is inexpressible — type can only be modal or tabbed. That's exactly why the real modal create/edit entry points (AppContent, useActionModal) set formType:'modal' and the form inside can only ever be simple.
Decision: split the dimensions. FormView.type becomes layout only (simple | tabbed | wizard). Presentation (drawer/modal/inline/page) is already a caller concern and already modelled elsewhere — reuse it. split is removed (master-detail is already subforms + the list's split-detail open mode).
The audit + the #1762 browser verification established that ObjectForm already implements every variant (real TabbedForm/WizardForm/SplitForm; drawer/modal fall through to DrawerForm/ModalForm). The gap was entry wiring (fixed for the full-page route in #1762).
Crucially, "how a form is opened" is already spec'd, independently of FormView.type:
- Detail open mode —
NavigationModeSchema:page/drawer/modal/split/popover(view.zod.ts). - Add-record mode — list
addRecord.mode:inline/form/modal+formView. - Action open — action
type:'modal'+target.
So drawer/modal/split as FormView.type values are redundant with — and orthogonal to — these. And they have zero real business usage: the only definitions are 5 showcase/template named views built to demo variants (app-showcase task.view split/quick; hotcrm lead.view split/drawer/modal). No default form view, no business flow depends on them.
FormView.type= layout only:z.enum(['simple','tabbed','wizard'])(defaultsimple).- Remove
drawer/modalfromFormView.type. A form is placed in a drawer/modal by the caller (listaddRecord.mode,NavigationMode, actiontype:'modal'), and the container renders anObjectFormwhosetypeis a layout. This makes "modal + tabbed" expressible — the whole point. - Remove
splitfromFormView.type. Master-detail is alreadysubforms(single-record parent/child) + the list'sNavigationMode:'split'(list+detail). A form-levelsplitis a third, redundant spelling. ObjectFormdrops itsdrawer/modal/splitbranches (DrawerForm/ModalFormbecome caller-supplied containers;SplitFormretires in favour ofsubforms). It keepssimple/tabbed/wizard.
- Spec: narrow the
FormViewSchema.typeenum (spec-major: the removed values are a breaking surface change). - Renderer: remove the 3 retired
ObjectFormbranches. - The 5 demo named views (showcase
tasksplit/quick, hotcrmleadsplit/drawer/modal): convert totabbed/simplelayout demos, and demo the open modes via the list'sNavigationMode/addRecord.modeinstead. No business metadata changes. - Reconcile with the deferred entry wiring: modal/drawer create/edit (
AppContent/useActionModal) should forward the form view's layout into the container (so a modal can host a tabbed form) — the concrete follow-up this re-model unlocks.
- Positive. Authors describe structure with
type(layout) and placement with the existing open-mode fields — no overloaded field, and modal/drawer forms can finally be tabbed/wizard. Smaller, honestFormViewsurface; one master-detail spelling. - Negative / cost. Breaking enum narrowing (spec-major). Requires a coordinated spec +
plugin-form+ examples/templates change. Mitigated by the near-zero real usage of the removed values. - Sequencing. This ADR is the design; implementation is a spec-major change (like ADR-0021's cutover) and should land after architect sign-off, ideally bundled with the modal/drawer layout-forwarding follow-up so the "modal + tabbed" capability ships demonstrably.
- Re-modelling
NavigationMode/addRecord.mode/ actionmodal— they already carry presentation; this ADR reuses them. - The viewschema key-drift cleanup (objectui#1763) — separate.