Cross-repo half of objectui#5939, filed from that card's implementation so whoever takes this does not have to re-derive the measurement. Filed unassigned for triage.
Fact
ObjectMasterDetailFormPropsSchema.formType accepts any string:
ObjectMasterDetailFormPropsSchema.safeParse({objectName: 'po',details: [],formType: 'wizzard'}).success// → truewhile its sibling ObjectFormPropsSchema.formType rejects an out-of-vocabulary value (its options are simple / tabbed / wizard / split / drawer / modal). Both blocks funnel into the same objectui renderer, which switches on those variant names, so a value outside them matches no branch: the parent half falls through to a flat field list and the authored sections silently disappear, with no diagnostic anywhere.
Measured consequence, and the reason objectui filed it rather than shrugging: in objectui#3840's binding-reach probe the generic sample for a string input is 'x', so object-master-detail-form skipped the section loop entirely and read green, while object-form (enum sample 'simple') painted an error card on identical malformed input. A value outside a prop's vocabulary routes around the block instead of exercising it — here, around a real crash, for as long as anyone had looked.
The measurement, so it is not re-derived
objectui#5939 tightened the objectui registry declaration to exactly simple | tabbed, measured rather than copied from the sibling's six. Per parent formType, mounting the block with two parent sections and one detail collection and driving its single Save bar:
| value | branch reached (ObjectForm.tsx) | outcome for the PARENT half |
|---|
simple | :1134 SimpleObjectForm sections path | honoured; saves through the atomic batch |
tabbed | :236 TabbedForm | presentation honoured, rendered inline |
wizard | :260 WizardForm | only the current step's fields mount; the master-detail Save bar acts as Next |
split | :287 SplitForm | renders inline, but persists via dataSource.create |
drawer | :316 DrawerForm | parent half goes into a portal dialog outside the master-detail container — the Save bar has no form to submit |
modal | :346 ModalForm | same portal shape |
objectui already stated the same two independently in two places, which is the corroboration the enum was derived against: MasterDetailFormSchema.formType?: 'simple' | 'tabbed', and the coercion formType === 'tabbed' ? 'tabbed' : 'simple' that ObjectForm applies when it routes a subforms schema into the block.
(objectui#6176 records that tabbed is presentationally honoured but escapes the atomic batch. That is a renderer defect and does not change the vocabulary this spec key should carry.)
Why this repo's half is the load-bearing one
Per objectui#5155's standing maintainer ruling, rejection lives at the zod/publish boundary. The objectui enum fixes the authoring surface — the manifest, the JSX-page compiler, the save gate (sdui-parser now reports invalid-enum at severity error) and what a probe samples — and it does not by itself make an out-of-vocabulary value rejected. os validate / os build / os lint and publish-time validation read this schema, so until it narrows, a published app can still carry formType: 'wizzard' and render a silently sectionless parent form.
Proposed change
Narrow ObjectMasterDetailFormPropsSchema.formType to simple | tabbed. It is a narrowing of an authorable surface, so it needs the usual treatment for that in this repo (liveness check for existing authored values, and whatever the removal route requires) rather than an unannounced tightening. objectui found zero out-of-vocabulary formType values authored anywhere in its own repo — fixtures, examples, catalog entries and tests — across all of simple/tabbed/wizard/split/drawer/modal; the equivalent census here has not been run.
Related: objectui#5939 (the objectui half, with the full measurement) · objectui#3840 (where it surfaced, and the probe that read green) · objectui#5155 (why the objectui enum is not by itself a rejection) · objectui#6176 (tabbed's atomic-batch leak).
Cross-repo half of objectui#5939, filed from that card's implementation so whoever takes this does not have to re-derive the measurement. Filed unassigned for triage.
Fact
ObjectMasterDetailFormPropsSchema.formTypeaccepts any string:while its sibling
ObjectFormPropsSchema.formTyperejects an out-of-vocabulary value (its options aresimple/tabbed/wizard/split/drawer/modal). Both blocks funnel into the same objectui renderer, which switches on those variant names, so a value outside them matches no branch: the parent half falls through to a flat field list and the authored sections silently disappear, with no diagnostic anywhere.Measured consequence, and the reason objectui filed it rather than shrugging: in objectui#3840's binding-reach probe the generic sample for a
stringinput is'x', soobject-master-detail-formskipped the section loop entirely and read green, whileobject-form(enum sample'simple') painted an error card on identical malformed input. A value outside a prop's vocabulary routes around the block instead of exercising it — here, around a real crash, for as long as anyone had looked.The measurement, so it is not re-derived
objectui#5939 tightened the objectui registry declaration to exactly
simple | tabbed, measured rather than copied from the sibling's six. Per parentformType, mounting the block with two parent sections and one detail collection and driving its single Save bar:ObjectForm.tsx)simple:1134SimpleObjectForm sections pathtabbed:236TabbedFormwizard:260WizardFormNextsplit:287SplitFormdataSource.createdrawer:316DrawerFormmodal:346ModalFormobjectui already stated the same two independently in two places, which is the corroboration the enum was derived against:
MasterDetailFormSchema.formType?: 'simple' | 'tabbed', and the coercionformType === 'tabbed' ? 'tabbed' : 'simple'thatObjectFormapplies when it routes asubformsschema into the block.(objectui#6176 records that
tabbedis presentationally honoured but escapes the atomic batch. That is a renderer defect and does not change the vocabulary this spec key should carry.)Why this repo's half is the load-bearing one
Per objectui#5155's standing maintainer ruling, rejection lives at the zod/publish boundary. The objectui
enumfixes the authoring surface — the manifest, the JSX-page compiler, the save gate (sdui-parsernow reportsinvalid-enumat severityerror) and what a probe samples — and it does not by itself make an out-of-vocabulary value rejected.os validate/os build/os lintand publish-time validation read this schema, so until it narrows, a published app can still carryformType: 'wizzard'and render a silently sectionless parent form.Proposed change
Narrow
ObjectMasterDetailFormPropsSchema.formTypetosimple | tabbed. It is a narrowing of an authorable surface, so it needs the usual treatment for that in this repo (liveness check for existing authored values, and whatever the removal route requires) rather than an unannounced tightening. objectui found zero out-of-vocabularyformTypevalues authored anywhere in its own repo — fixtures, examples, catalog entries and tests — across all ofsimple/tabbed/wizard/split/drawer/modal; the equivalent census here has not been run.Related: objectui#5939 (the objectui half, with the full measurement) · objectui#3840 (where it surfaced, and the probe that read green) · objectui#5155 (why the objectui enum is not by itself a rejection) · objectui#6176 (
tabbed's atomic-batch leak).