Found while measuring objectui#7361's triage boundary 3 (does anything downstream consume the declared default). Unassigned. Higher stakes than objectui#7361, because this surface is RUNTIME rather than documentation.
Measured on origin/main = b74a859, against the built packages/types/dist.
What is declared
packages/types/src/zod/layout.zod.ts is a published runtime validator (@object-ui/types/zod, reached through safeValidateSchema, which is AnyComponentSchema.safeParse and returns the parsed object as result.data). Several members carry .optional().default(VALUE), and .default() is not documentation — parse SUBSTITUTES that value into the output when the key is absent.
layout.zod.ts:170 maxWidth: ... .optional().default('lg')
layout.zod.ts:191 align: ... .optional().default('center') (on FlexSchema only)
What the renderers apply
container.tsx:26 const maxWidth = schema.maxWidth ?? 'xl';
flex.tsx:18 const align = schema.align || 'start';
stack.tsx:24 const align = schema.align || 'stretch';
Measured, not inferred
Feeding three minimal nodes through the built safeValidateSchema:
container success: true | parsed: {"type":"container","maxWidth":"lg","centered":true}
flex success: true | parsed: {"type":"flex","direction":"row","justify":"start","align":"center","gap":2,"wrap":false}
stack success: true | parsed: {"type":"stack"}
The consequence
A container that omits maxWidth renders max-w-xl when it reaches the renderer as authored, and max-w-lg when it has been through the mirror first — the mirror writes 'lg' into the node, so the renderer's ?? finds a value and never applies its own default. Same shape for flex and align: 'center' is substituted, and 'center' is a value NEITHER flex nor stack applies. One authored document, two code paths, two layouts.
StackSchema in the same file declares no defaults at all (see the stack row above), so it is unaffected by the substitution — the mirror is also internally inconsistent between the two sibling types.
Relationship to other cards
- objectui#7361 corrected the JSDoc
@default on ContainerSchema.maxWidth and FlexLayoutProps.align. Its file surface was the docblocks only and its dispatch explicitly ruled out moving any default VALUE, so the mirror was deliberately left as it stands. After that change the two surfaces disagree with each other in the tree, which is part of why this needs its own ruling.
- Distinct from objectui#5927, which is about mirrors being NARROWER than their TypeScript declarations (accept-set drift). This is default substitution — a different axis.
- Another instance of objectui#4631.
Why a ruling rather than a patch
Two candidate shapes, and an implementing agent should not pick between them:
- Move the values (
.default('lg') becomes .default('xl'), .default('center') becomes .default('start')). This changes what parse emits for every existing document, so it is a behaviour change on a published surface.
- Drop
.default() from the mirror entirely, so parsing stops substituting and the renderer's fallback is the single source of the default. This is arguably the contract-first shape, and it is already what StackSchema does.
The wider question behind both: whether a validator on this surface should be allowed to author values at all.
Filed by an ObjectUI development agent while working objectui#7361. No assignee, no labels — left for triage.
Found while measuring objectui#7361's triage boundary 3 (does anything downstream consume the declared default). Unassigned. Higher stakes than objectui#7361, because this surface is RUNTIME rather than documentation.
Measured on
origin/main=b74a859, against the builtpackages/types/dist.What is declared
packages/types/src/zod/layout.zod.tsis a published runtime validator (@object-ui/types/zod, reached throughsafeValidateSchema, which isAnyComponentSchema.safeParseand returns the parsed object asresult.data). Several members carry.optional().default(VALUE), and.default()is not documentation —parseSUBSTITUTES that value into the output when the key is absent.What the renderers apply
Measured, not inferred
Feeding three minimal nodes through the built
safeValidateSchema:The consequence
A
containerthat omitsmaxWidthrendersmax-w-xlwhen it reaches the renderer as authored, andmax-w-lgwhen it has been through the mirror first — the mirror writes'lg'into the node, so the renderer's??finds a value and never applies its own default. Same shape forflexandalign:'center'is substituted, and'center'is a value NEITHER flex nor stack applies. One authored document, two code paths, two layouts.StackSchemain the same file declares no defaults at all (see thestackrow above), so it is unaffected by the substitution — the mirror is also internally inconsistent between the two sibling types.Relationship to other cards
@defaultonContainerSchema.maxWidthandFlexLayoutProps.align. Its file surface was the docblocks only and its dispatch explicitly ruled out moving any default VALUE, so the mirror was deliberately left as it stands. After that change the two surfaces disagree with each other in the tree, which is part of why this needs its own ruling.Why a ruling rather than a patch
Two candidate shapes, and an implementing agent should not pick between them:
.default('lg')becomes.default('xl'),.default('center')becomes.default('start')). This changes whatparseemits for every existing document, so it is a behaviour change on a published surface..default()from the mirror entirely, so parsing stops substituting and the renderer's fallback is the single source of the default. This is arguably the contract-first shape, and it is already whatStackSchemadoes.The wider question behind both: whether a validator on this surface should be allowed to author values at all.
Filed by an ObjectUI development agent while working objectui#7361. No assignee, no labels — left for triage.