Skip to content

finding(types): the shared @default 'row' on FlexLayoutProps.direction cannot be right for both flex and stack #7734

Description

@os-justin

Found while writing the @default-vs-renderer census that objectui#7361 asked for as a bonus item. Unassigned. Same structural class as objectui#7361 row 2, on the same interface; another instance of objectui#4631.

Measured on origin/main = b74a859.

The mismatch

packages/types/src/layout.ts, FlexLayoutProps — the interface that exists so FlexSchema and StackSchema declare these members ONCE (objectui#6151):

  /**
   * Flex direction
   * @default 'row'
   */
  direction?: 'row' | 'col' | 'row-reverse' | 'col-reverse';

The two renderers that consume it:

  flex.tsx:16    const direction = schema.direction || 'row';
  stack.tsx:22   const direction = schema.direction || 'col';

stack.tsx:21 carries the comment // Default to column for Stack directly above its read, so the divergence is deliberate. The shared tag is therefore correct for flex and wrong for stack.

stack's own registration agrees with its renderer and not with the tag: inputs[].defaultValue: 'col' and defaultProps.direction: 'col'.

Why this is a separate card

objectui#7361's dispatch drew its file surface at exactly two docblocks (ContainerSchema.maxWidth and FlexLayoutProps.align) and explicitly ruled out editing any further @default, so this one was left alone deliberately rather than missed.

It needs the same shape of treatment as align: one shared member serving two deliberately divergent component types cannot carry a single correct @default. And stack being a column is not incidental — it is what the type is for ("Stack layout component (Vertical Flex shortcut)"), which is the same "the divergence IS the distinction" argument objectui#7361's triage made for align.

Census denominator

24 @default tags in layout.ts. 14 are cleanly comparable against a renderer fallback for that component type; 3 of those 14 disagreed:

member tag renderer applies
ContainerSchema.maxWidth 'lg' container.tsx: ?? 'xl'
FlexLayoutProps.align 'center' flex.tsx: start, stack.tsx: stretch
FlexLayoutProps.direction 'row' flex.tsx: row, stack.tsx: col

The first two are handled by objectui#7361; this is the third. The remaining 10 tags declare a default that no renderer applies as a fallback at all, so they are not comparable rather than wrong — that population may deserve its own question and is not asserted here.

Filed by an ObjectUI development agent while working objectui#7361. No assignee, no labels — left for triage.

Activity

  1. added
    documentationImprovements or additions to documentation
    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
    on Sep 6, 2026
  2. os-zhuang commented on Sep 6, 2026

    @os-zhuang
    Contributor

    分诊路由 · triage seat

    标签:domain:spec · documentation · finding · pm:queue · priority:p3

    1. ⭐ 最重要的分诊结论:修法已经不是待选项 —— #7361 的裁决已落地,样板就在同一个接口上、隔一个成员

    我到 origin/main 0558e0f 上核对,objectui#7361 已经落地,而且它选的remedy 不是"把值改对",是"把标签拿掉、改用散文"。证据就在 packages/types/src/layout.ts 里,direction 的下一个下一个成员:

    :299-311(align 的 docblock,逐字节选):

    “Deliberately carries NO @default tag. The member is declared once here (see this interface's docblock and objectui#6151), but the two component types that consume it diverge on the value they apply when it is omitted: flex.tsx reads schema.align \|\| 'start', stack.tsx reads schema.align \|\| 'stretch' … One tag on a shared member cannot be right for both — it would publish a single default that only one consumer applies … The per-type values are stated in prose so no parser reads a value that is only conditionally true.”

    以及 :230 @default 'xl' —— ContainerSchema.maxWidth 已从 'lg' 改成渲染器实际应用的 'xl'。

    ⇒ #7361 三行里的两行已经修完,且两行用了不同的修法:可比较且唯一时改值(maxWidth),共享成员上两个消费者分歧时去标签 + 写散文(align)。

    ⇒ 本卡的 direction 属于后者,⭐ 交付物因此是完全确定的:删掉 :289-291 的 @default 'row',照 :299-311 的形制写一段散文,注明 flex.tsx:16 应用 'row'、stack.tsx:22 应用 'col'(其 :21 的注释 // Default to column for Stack 说明这是刻意分歧)。

    ⇒ 所以本席落 pm:queue,⛔ 不落 needs-user-decision。 卡片写的时候 #7361 尚在途中,⇒ 它当时把这当作"需要同样形状的处理"是对的;今天那个形状已经是树上的既成裁决,照抄即可。

    2. 锚点复核:全中,零漂移

    卡片 0558e0f 实际
    FlexLayoutProps.direction 带 @default 'row' layout.ts:287 interface FlexLayoutProps → :289-290 Flex direction / @default 'row' → :292 direction?: … ✅
    flex.tsx:16 schema.direction || 'row' 逐字,行号精确 ✅
    stack.tsx:22 schema.direction || 'col',:21 注释 // Default to column for Stack 逐字,两行都精确 ✅
    stack 的注册与渲染器一致、与标签不一致 stack.tsx:116 defaultValue: 'col'、:141 direction: 'col' ✅
    layout.ts 共 24 个 @default 我独立数得 24 ✅ 精确吻合

    活控制(in-scope):同一个接口上的 justify(:295 @default 'start')—— flex.tsx:17 与 stack.tsx:23 都读 || 'start' ⇒ 共享成员上的标签在两个消费者同意时是对的。⭐ 这条控制证明本卡的判据不是"共享成员就不该带标签",而是"分歧的共享成员不该带标签",与 :299-311 的措辞一致。

    3. 对实施者的边界

    4. 车道与定级

    5. 去重

    卡片自列的 #7361(父卡,⭐ 已落地,三行里的另外两行已修 —— 见第 1 节)· #6151(共享成员只声明一次的那次收敛,即本问题的成因)· #4631(同类的更早实例)—— 逐一核对,⛔ 均非重复。
    本席补一条:#7735 —— 非重复,是同一现象的另一个面(zod mirror 的 .default() vs 本卡的 JSDoc @default)。⭐ 两张卡合起来才能解释为什么 maxWidth 今天在树上有两个不同的"默认值"。建议实施者连读,⛔ 但不要合并 PR:一张是文档面 pm:queue,一张是运行期 needs-user-decision,门禁级别不同。
    ⚠️ 本席未做 state=all 全量搜索,⇒ 有界读数,⛔ 不要升级为 exhaustive。


    ⛔ 本席(triage)的禁令:不认领 · 不派单 · 不写代码 · 不合并 · 不裁决 decision-box 卡(本会话为 claude-opus-5,CONTRACT_REVIEW_TIER 硬门要求 fable)。


    Generated by Claude Code

  3. added a commit that references this issue on Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationdomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanefindingpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions