Skip to content

feat(types,core)!: an authored flex takes its props in the properties bag; the flat spelling is refused by name (objectui#11276) - #11406

Merged
os-zhuang merged 5 commits into
mainfrom
claude/issue-11276-flex-properties-bag
Oct 2, 2026
Merged

os-zhuang merged 5 commits into
mainfrom
claude/issue-11276-flex-properties-bag

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #11276
Clause-②: yes

Clause-②: yes, copied from the PM claim 5940199465: the strict face starts accepting the properties bag on an authored flex, which widens it, while the flat spelling narrows. This is objectui's narrowing-as-minor rule with a BREAKING (authoring) banner in the changeset, as in PR objectui#11328 and PR objectui#11399.

The flex batch of objectui#11276, the last one. It executes triage's addendum 5926377520 under the maintainer's ruling on objectui#11300, verbatim: 「A」. With this PR the card's three arms (object-chart, object-grid, flex) all take the spec's bag.

What changed

  • packages/types/src/zod/layout.zod.ts: new FlexBlockSchema, the authored flex arm, built the way ObjectChartBlockSchema is (PR objectui#11328): BaseSchema + the flex literal + NODE_ENVELOPE + properties through propsBag.
    • @objectstack/spec 17.5.0 has no ComponentPropsMap['flex'] row (55 rows, none flex; object-grid present as the control), so no row is invented. The bag's direction, justify, align, gap and wrap are the flat FlexSchema mirror's own members, the same schema objects. The bag keeps the mirror's .passthrough() posture.
    • The child list is in the bag, properties.children, where the showcase writes it and the spec's page walk reads it. It keeps the mirror's accept set (one node, or a list), but the LIST's entries are left to the page-walk judgment AnyComponentSchema already runs at every properties.children (objectui#11223), as on the page: containers. Measured before choosing this: with the mirror's own children member in the bag, a refused child was reported twice (an invalid_union at properties.children plus the child's own issue) and every nesting level was judged twice. A single node, which the walk does not descend, is judged by SchemaNodeSchema.
    • Flat members are refused by name through the shared flatPropRefusals (public-blocks.zod.ts), the helper PR objectui#11399 used. It gains an optional third argument, the refusal detail for a bag that is not a spec row, because its default detail names ComponentPropsMap['KEY'], a row flex does not have. Every existing caller is unchanged. body is refused toward properties.children, replacing the mirror's refusal, whose remedy (a node-level children) this arm refuses.
    • FlexBlockSchema replaces FlexSchema in LayoutSchema. FlexSchema stays exported, unchanged in shape: it is the node as the flex renderer reads it after the hoist, and as code composes it.
  • packages/types/src/layout.ts: the TypeScript FlexSchema is re-declared, not retired (docblock only). FlexLayoutProps, already exported, is the bag's TypeScript type; typed authored nodes check their bag with satisfies FlexLayoutProps.
  • packages/core/src/builder/schema-builder.ts: flex() builds { type: 'flex', properties: { … } }; build() is typed BaseSchema & { type: 'flex'; properties: FlexLayoutProps }.
  • objectui's own producers, moved to the bag (census below).
  • objectui#6751's fence (5460852791, and 5542677253's "flex declares its own keys") is revoked for flex only. Its pins flip to bag pins; its props-envelope half (the deliberate counter-examples in skills/**) stands and is untouched.
  • New pins: packages/types/src/__tests__/flex-properties-bag-11276.test.ts (43 rows plus a type-level block) and packages/components/src/__tests__/flex-properties-bag-render-11276.test.tsx (render through the real SchemaRenderer and registry: a bag node and a stored flat node draw the same class string and subtree; SchemaRenderer's hoist is untouched).
  • Two changesets: .changeset/11276-flex-properties-bag.md (@object-ui/types minor, BREAKING (authoring), names the migration) and .changeset/11276-core-flex-builder-properties-bag.md (@object-ui/core minor, the builder's output shape).
  • skills/objectui/guides/page-builder.md §3b ("Pick the right layout primitive"), commit 56d5f7fd, under the seat's claim amendment 5941574748: the section said the layout primitives all "take children and read every key off the node" and tabulated flex's props, which after this PR is a refused shape. Two sentences change: the intro now says stack, grid, container and box keep reading every key off the node while flex takes its props and children in properties, and the flex / stack intent sentence names the refusal (objectui validate refuses flat flex props). The table's "Key props (renderer defaults)" column is unchanged: those stay the renderer's defaults, only where they are authored moved. Net skills/** lines 0 (file 353 → 353; bundle 4806 → 4806). Measured first at 5af4d2e7 through the built faces: the flat flex as §3b taught it is refused by safeValidateSchema and StrictAnyComponentSchema (four invalid_type refusals, each naming properties.KEY), the bag and the showcase leaf shape pass both, a flat stack / grid / container / box passes both (a bag stack is refused by the strict face: the bag is not the contract there). §3b holds no fence and the bundle holds no flex node (grep over skills/, .claude/, docs/adr is empty; object-grid hits page-builder.md as the control). Three-way git merge-file against draft PR objectui#11404's page-builder.md (head d83d2748): exit 0, 0 markers, both edits present. With this file the guard answers GOVERNED (skills/** x1 of 94 paths), so the PR takes the governed path: draft until an authorized APPROVED review, as the amendment says.

Files beyond the claimed surface, each forced: census rows the new export forces (zod-mirror-parity.test.ts, one EXCLUSIONS row); fixture triage where a reader reads flex props off a catalog node (examples/schema-catalog/test/layout-props-conversion.test.tsx gains a propsOf reader and the nine space-x site paths now step through properties.children; badge-demo-label-6829.test.tsx locations); the guide's remedy fixture in page-actions-refusal-7926.test.ts; packages/types/src/zod/README.md.

Zone 2, measured first

  1. Held. At base 6aa029b63f, through the built faces: a bag flex node ({ type: 'flex', properties: { direction: 'col', gap: 2, children: [ … ] } }) and the showcase shape ({ id, type: 'flex', responsiveStyles, properties: { children: [] } }) passed safeValidateSchema and were refused by the strict face (unrecognized_keys naming properties); the flat spelling passed both. The spec's PageComponentSchema accepted the bag node and refused the flat one (unrecognized_keys naming direction, gap, children); control: a flat action:button actionType is refused the same way.
  2. No row. ComponentPropsMap in installed 17.5.0 has no flex key, so the bag is judged by objectui's mirror members (direction, justify, align, gap, wrap, and the child list as described above).
  3. Census at base 6aa029b63f. Instrument: git grep -lE for a type key equal to flex in any quoting, minus tests / snapshots / e2e: 84 non-test files; plus a search for code that emits a flex node without that literal.
class files action
catalog documents 70 under examples/schema-catalog/src/schemas, 248 nodes (a JSON walk: node keys were only type, gap, wrap, children, align, justify, className, direction) moved: 245 nodes in 68 canonically formatted files by a script (JSON round trip, byte-identical outside the moved nodes), 3 nodes in auth/login-simple.json and auth/signup.json by hand to keep their compact lines
guides / reference content/docs/guide/layout.md (3 nodes), content/docs/components/layout/flex.mdx (its Schema block) moved; flex.mdx now shows a bag JSON example and the bag's members, with the full declared align / justify vocabularies
@object-ui/types docs and examples README.md (2 nodes), examples/dashboard.ts (2 nodes), examples/data-display-examples.json (1 node) moved; the TS ones use BaseSchema plus satisfies FlexLayoutProps
code that emits a node @object-ui/core's flex() builder (super('flex'), missed by the literal grep) emits the bag
not an authored node packages/plugin-designer/src/PageDesigner.tsx (a palette entry that builds a DesignerComponent, the designer canvas model, carrying no props); packages/sdui-parser/verify.ts (a manifest entry for the kind: 'html' JSX tier, whose compiled nodes never meet the zod face); the flex registration's defaultProps (a designer / manifest seed; no composer turns it into a document node on the zod path) kept
declarations packages/types/src/layout.ts docblock only
prose / history 3 pending changesets, 3 CHANGELOGs not edited; the pending ones are answered in the new changeset
governed skills/objectui/guides/page-builder.md §3b: prose that taught flex's props on the node. No governed file holds a flex node (the same grep over skills/, AGENTS.md, CLAUDE.md, .claude/, docs/adr/ exits 1, while the pattern for other types hits skills/, the control) edited in commit 56d5f7fd under the seat's amendment 5941574748, so this PR is GOVERNED

The census's "16 tests in 8 files" re-derives differently because this PR moves the catalog together with the arm: red first at b453ead8 before the pins flipped was Tests 30 failed in 7 files (the 6751, 8761 and 10872 pins, the 7926 remedy row, the parity census row, and two catalog readers that read flex props off the node); the catalog-through-the-face pins the census simulation turned red (8499, 6939, 6942, 8284) stay green because the documents moved too.
4. layout.md and PR objectui#11069. git merge-file of this branch's layout.md against PR objectui#11069's head a11f73347d over their merge base: exit 0, 0 conflict markers, both edits present. git merge-tree of this head against that PR: the only conflict is packages/cli/src/commands/check.ts, the same one main at base has with it; layout.md auto-merges.
5. The showcase. objectstack origin/main 3a7b6eb0 has 15 type: 'flex' literal sites in command-center.page.ts and styling-gallery.page.ts, plus 1 taught node in skills/objectstack-ui/rules/pages.md: the 16. Evaluated with their helpers they are 60 runtime nodes in the two pages plus the taught one (a scratch instrument, not committed):

reading base 6aa029b63f this head
each flex node's own verdict, tolerant face 61 / 61 clean 61 / 61 clean
each flex node's own verdict, strict face (the face PR objectui#11069 wires) 0 / 61 (properties refused) 61 / 61 clean
whole subtree, both faces 58 / 61 tolerant, 0 / 61 strict 58 / 61 both
spec PageComponentSchema 61 / 61 61 / 61

The 3 whole-subtree refusals at head are not flex: they are the command-center root, its KPI panel and its KPI band, whose object-metric tiles write filter as a record ({ status: 'active' }), which the spec's own ComponentPropsMap['object-metric'] row refuses ("takes the ViewFilterRule ARRAY form"). See Acceptance notes. PR objectui#11069's own census script does not read objectstack, so its count is read through the strict face it wires, which accepts all 61 flex nodes. Through the real objectui validate binary (packages/cli/dist/cli.js, built from this tree): a flat flex exits 1 with three named refusals pointing at properties.children, properties.justify and properties.gap; the bag node and the showcase leaf shape exit 0.

Construct reused, and why

PR objectui#11328's (object-chart: no spec row, the bag is the mirror's own members by reference, the description says so) rather than PR objectui#11399's (object-grid: a spec row by reference), because flex has no row. The refusals come from PR objectui#11399's shared flatPropRefusals rather than a per-arm map, so there is one copy; its new optional detail is the no-row counterpart of propsBag's optional description, which PR objectui#11328 added for the same reason.

Tests (heavy runs through os-verify-lock.sh, slot issue-11276-flex; Node v22.22.2 from /opt/nvm)

  • Union at b453ead8/bacbb4d7 (the content of this head; the later commits merge main, which touched only packages/layout, and edit one changeset sentence): pnpm exec vitest run packages/types/ examples/schema-catalog/ packages/cli/ packages/core/src/builder/ packages/components/src/__tests__/flex-properties-bag-render-11276.test.tsx: Test Files 384 passed (384), Tests 11192 passed (11192), VERDICT 0.
  • Consumer sweeps: 42 files naming the catalog, the moved docs, FlexSchema, LayoutSchema, the builder, plus packages/sdui-parser/: Test Files 61 passed (61), Tests 1035 passed, 16 skipped (the skips are TimelineGantt.dateOnlyZone-10866's timezone-gated rows). 73 more files naming the faces, content/docs or a flex literal: Test Files 72 passed (72), Tests 1089 passed. scripts/__tests__/: Test Files 177 passed, 2 skipped (179), Tests 5417 passed, 2 skipped. packages/core/, packages/components/src/__tests__/, packages/sdui-parser/, packages/plugin-designer/: Test Files 441 passed, 1 skipped (442), Tests 6294 passed, 34 skipped. Each VERDICT 0.
  • Build and type-check: pnpm --filter @object-ui/types build, type-check (all three legs: the examples program holds examples/dashboard.ts, the test program holds the new pin, read with --listFilesOnly), pnpm --filter @object-ui/core build, type-check: VERDICT 0.
  • Runtime ablation (objectstack scripts/ablation-replace.mjs, WRAP mode with its EXIT/INT/TERM restore on the absolute path): FlexSchema back in LayoutSchema in place of FlexBlockSchema (anchor x1 to x0, blob 1c3af5f2 to 4fbf4fb8). Over 7 files: Tests 37 failed, 218 passed (255): red = 29 rows of the new pin, 6 of the 10872 file, the builder row, the 8761 counter-probe; green = the object-chart and object-grid pins (controls) and the 6751 file, which parses FlexBlockSchema directly. Restored: blob == HEAD 1c3af5f2, git diff HEAD empty.
  • Ablation of the child-list choice: the bag's children returned to the mirror's own member (anchor x1 to x0). Exactly the 3 judged-once rows go red; restored, blob == HEAD.
  • Type-level reverse check: the flatPropRefusals spread deleted, tsc -p tsconfig.test.json exits 2 with exactly 2 TS2344 errors, the two flat-keys-take-nothing rows of the new pin. Restored, blob == HEAD.
  • Corpus sweeps (scratch instruments): every catalog document holding a flex node (70), both faces: the refused set is identical at base and head (2 tolerant, 4 strict, the same documents, codes and keys: carousel items, resizable panels.N.id, input-otp separator, label required), only the paths gaining properties.. Every JSON fence in tracked .md / .mdx holding a flex node: base 3, head 4, refused 0 at both. objectui check over the repository (the same binary at both trees): base 267 validated / 166 recognised / 1 did not validate; head 287 / 146 / 1 (the same file, the vscode-extension JSON schema): root-flex documents that carry no other structural key are now parsed in full, and pass.
  • Changed TS doc blocks: the README nesting block and the flex.mdx interface compile against the built types (tsc --strict, exit 0); control: direction: 'column' in the README bag is TS2322, exit 2.

Gates (exit codes captured before any pipe)

  • exit 0 at 25844284: check:new-line-citations ("VERDICT new-cross-file-line-citations: 0 new citation(s)"), check:control-bytes ("OK (scanned 9930 tracked text file(s); skipped 85 binary)"), markdown-test-inputs --audit ("109 candidate test files, all adjudicated; 81 declared entries, all present"), check-changeset-presence ("13 source file(s) of 3 released package(s) changed, and this change declares 2 changeset(s)"), changeset:check, check:spec-symbols, check:doc-types, check:doc-example-ids ("415 real reference(s) all resolve"), check:doc-fences, docs:check-links, check:handler-key-reads, check:pending-changeset-literals, check:test-path-roots, check-governed-queue-guard.mjs --test over the 93 paths ("NOT GOVERNED"; at 56d5f7fd, with §3b added, GOVERNED over 94 paths). Re-run at 5af4d2e7 (a changeset sentence only): presence, changeset:check, check:control-bytes, check:new-line-citations, each exit 0.
  • exit 0 at bacbb4d7: check:vi-mock-specifiers, check:component-surface-parity, check:element-data-source-declaration, check:installed-pin-claims, check:prompt-keys, check:doc-example-readers, check:governed-queue-guard, check:changeset-claims (report-only; its re-read list holds no paragraph this change falsifies).
  • ESLint narrowing: the config's population is **/*.{ts,tsx}; eslint --no-inline-config --format json over the 16 changed .ts / .tsx files: 16 files, 0 errors, 5 no-explicit-any warnings, none on an added line. eslint.config.js sets no projectService / parserOptions.project, so no untouched file's verdict can move. The full pnpm lint is CI's.
  • NOT MEASURED, PREREQUISITE NOT MET (unbuilt packages): check:doc-snippets (exit 2), check:doc-examples (exit 2), check:readme-exports (exit 1, "the population COLLAPSED"; among what it judged, 0 wrong-path, 0 fabricated). The changed TS blocks were compiled separately (above). CI builds first.

Deviations

  • The bag's children is not the mirror's member by reference. It keeps the mirror's accept set and hands the list's entries to the page-walk judgment, because the mirror's member double-reports every nested refusal (measured, and pinned by the child-list ablation above). One edge differs from the mirror and matches the page: containers: an entry that is itself an array is not visited by the walk.
  • flatPropRefusals gained an optional argument (the no-row refusal detail). Existing callers and their messages are unchanged.
  • Two changesets, as the order allows when another package's published files change: @object-ui/core ships the builder.
  • A detached comparison worktree at base (../objectui-issue-11276-flex-base, removed after use) carried the before readings; a copy of this tree's built CLI (its code is unchanged by this PR) ran objectui check there.

Acceptance notes

  • The showcase's 3 non-flex refusals. The command-center KPI tiles (object-metric) write filter: { status: 'active' } and { status: { $ne: 'done' } }; the installed spec row ComponentPropsMap['object-metric'] refuses the record form by its own message, and so do both objectui faces, at the tiles' properties.filter. This is independent of flex and of this PR (it was already refused at base on the tolerant face). Whether os validate's props gate refuses it too is NOT MEASURED. Filed by the seat as finding(examples): the app-showcase command-center KPI tiles write object-metric filter as a record, which the spec's own ComponentPropsMap['object-metric'].filter refuses ("takes the ViewFilterRule ARRAY form") objectstack#21251.
  • The governed skills/objectui/guides/page-builder.md §3b said the layout primitives "take children and read every key off the node" and tabulated flex's props. It holds no flex node, but after this PR it would teach the flat spelling for flex, so it is fixed in this PR (commit 56d5f7fd, under the seat's amendment 5941574748).
  • scripts/measure-strict-authoring-face.mjs (PR objectui#11069's M1 instrument) reads 3 documents cleaner than the shipped strict face at this head (whole-tree strict refusals 69 to 66: 2 catalog, 1 authored), because its strict twin does not close components nested at a bag's child list. The shipped face refuses all 3, unchanged from base (checked on examples/dashboard.ts: the pre-existing card content, at properties.children.1.properties.children.1.children.0). Its flex row reads FlexBlockSchema, 250 documents, 0 strict-refused.
  • Pending changesets that describe a flat flex (10872-flat-arm-responsive-styles, 6751-flex-props-envelope-lift, 8284-content-channel-per-component, and the 7926 remedy) describe the flat mirror, which still behaves as they say; the new changeset says so.

Overlap

Merged main at 7728c67c (one commit, packages/layout only) before opening. Open PRs touching these files: PR objectui#11069 (layout.md, measured above).

Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 (dispatched by seat domain:ui#2; this run is its subagent on branch claude/issue-11276-flex-properties-bag).

维护者速读(草稿)

改了什么。 ObjectUI 页面里的 flex(横向排布容器)从此和其他业务组件一样,把自己的参数(方向、对齐、间距、换行)和子节点统一写进 properties 包里;平铺在节点上的旧写法被 objectui validate 按键名拒绝,错误信息逐键指出新位置。配套一并改到位:仓内 70 份示例目录文档(248 个 flex 节点)、layout.md 与 flex.mdx 两份文档、@object-ui/types 的 README 与示例、@object-ui/core 的 flex() 构建器全部改成新写法;新增两组 pin 测试(校验面 43 行 + 渲染 1 份);发布给客户 agent 的技能包 skills/objectui/guides/page-builder.md §3b 改两句话,说明 flex 的参数去向(技能包净行数 0)。渲染端一个字节不动:已存的旧节点照样画出来,且与新写法画得完全一致。

为什么改。 这是您在 objectui#11300 的裁决「A」的落地。@objectstack/spec 的页面契约要求组件自己的参数住在 properties 包里;ObjectUI 过去的 flex 却只认平铺写法,于是 objectstack showcase 里按 spec 写的 16 处 flex 节点在严格校验面上全部被拒,而 ObjectUI 自己文档教的写法又被 os validate 拒绝 —— 两端各说一套,AI 按任一端写都会在另一端报错。统一到 spec 的写法后,showcase 的 61 个运行时 flex 节点按节点自身判定在两个校验面上全部通过(整树读数 58/61,余下 3 处是 object-metric 的 filter 写法,与 flex 无关,已另立 objectstack-ai/objectstack#21251);平铺写法被按键名拒绝并给出改法。

风险与代价(含回滚)。 对按 ObjectUI 旧文档写页面的外部作者是 authoring 层面的 breaking:他们的 flex 节点要把参数移进 properties,错误信息会逐键指出目标位置;changeset 已标 BREAKING(authoring)并写明迁移。运行时零风险:SchemaRenderer 的 hoist 不变,已存的平铺节点与新写法渲染结果逐字节一致(有渲染 pin)。本 PR 超过 5000 行人工合并阈值(大头是 70 份示例文档的机械迁移),故停在 draft 等您批准。回滚代价低:整体 revert 这一个 squash commit 即可,无数据迁移、无存储格式变化;注意 skills/** 那两句与代码改动同 PR 落地,只回滚其中一半会让技能包教一个被拒绝的写法。

席位意见。

你要做的。 在 PR objectui#11406 上留下一条 APPROVED review(或回复「不批」并说明理由)—— 一个动作。


Generated by Claude Code

claude added 4 commits October 1, 2026 21:22
… bag; the flat spelling is refused by name

The `flex` batch of objectui#11276, under the maintainer's ruling A on
objectui#11300. `@objectstack/spec`'s strict page component refuses a prop
written on the node as mis-layered (ADR-0089 D3a); objectui's `flex` arm
was the flat mirror, so the strict face refused the objectstack showcase's
bag-shaped `flex` nodes and both faces accepted the flat node `os validate`
refuses.

- `FlexBlockSchema` arms the authored node: `BaseSchema` + the literal +
  `NODE_ENVELOPE` + `properties`. The spec has no `ComponentPropsMap['flex']`
  row, so the bag is the flat mirror's own members by reference (the
  `object-chart` construct); the child list is in the bag, its list entries
  judged once by the spec page walk. Flat members are refused by name through
  the shared `flatPropRefusals` (new optional no-row guidance), `body` toward
  `properties.children`. `FlexSchema` stays published as the post-hoist node.
- objectui's own producers move to the bag: 70 schema-catalog documents
  (248 nodes), `layout.md`, `flex.mdx`, the `@object-ui/types` README and
  examples. `@object-ui/core`'s `flex()` builder emits the bag.
- objectui#6751's fence is revoked for `flex` only; its pins flip to bag
  pins. `stack` and every other node-level arm are unchanged. The render
  path is untouched: a stored flat node still draws (pinned).

Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 40 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/10387-header-bar-unread-keys-retired.md

  • names packages/types/examples/dashboard.ts → packages/types/examples/dashboard.ts — edited by this change

    The in-tree examples that used these keys (packages/types/examples/dashboard.ts, the @object-ui/types README and the ObjectOS integration guide) now use crumbs and actions.

.changeset/4895-retire-block-schema-family.md

  • names packages/types/src/layout.ts → packages/types/src/layout.ts — edited by this change

    ⚠️ Not this family, and not touched. The live slotted record-page vocabulary — PageNodeSchema.kind === 'slotted' with slots?: PageSlotMap (packages/types/src/layout.ts), rendered by usePageAssignment / PageBlockCanvas / PageBlockInspector in @object-ui/app-shell — shares the words "block" and "slot" with the retired family and shares no declaration, type or file with it. Neither is the type: 'component' NAVIGATION item kind ({ type: 'component', componentRef }, NavigationItemSchema in zod/app.zod.ts), a different declaration in a different module.

.changeset/5632-svg-host-dom-passthrough.md

  • names packages/types/src/layout.ts → packages/types/src/layout.ts — edited by this change

    Both renderers forwarded their whole prop bag to the SVG they render, so every authored SDUI key on the node became an attribute — 14 per target, and icon="check" on all 71 icon nodes in the schema catalog. The two nodes declare different keys, and each renderer consumes its own node's keys by name: IconSchema (packages/types/src/layout.ts) against the pass-through docblock in renderers/basic/icon.tsx, and SpinnerSchema (packages/types/src/feedback.ts) against the one in renderers/feedback/spinner.tsx. icon and color are IconSchema's alone — SpinnerSchema declares neither. Read those two declarations for what they carry; this paragraph deliberately copies no member list. So the SDUI pass-through list withholds nothing they need.

.changeset/5903-objectgantt-declared-keys.md

  • names packages/types/src/__tests__/zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⚠️ The JUSTIFICATION for that parity clause was retired (objectui#9743); the clause itself stands and did not move. As first written it credited the zod-mirror-parity ratchet with a zero-drift reading for this pair — but at that time the ratchet measured three directions and was structurally blind to the MIRRORED-but-undeclared one (objectui#9711), so a zero from it recorded that it had not looked in that direction, not that nothing was there. objectui#9725 landed the fourth direction, and it covers this pair BY NAME: packages/types/src/__tests__/zod-mirror-parity.test.ts registers objectql.zod.ts#ObjectGanttSchema in both its mirror map and its declaration map; assertionMirroredUndeclaredMatchesLedger requires every registered pair's mirrored-but-undeclared key set to equal that pair's MirroredUndeclared ledger entry — never for a pair the ledger does not name — and assertionNoVacuousMirroredUndeclaredMeasurement refuses a measurement that has degenerated to any. ⛔ Read this pair's verdict off that reconciliation, which re-derives it on every run, rather than off any figure written here; when this paragraph was authored, on 2026-09-18, it required no MirroredUndeclared entry for the pair.

.changeset/5928-classname-style-props-rename.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Where the non-pair is recorded now. zod-mirror-parity.test.ts keys its existing EXCLUSIONS entry — the mechanism that accounts for every exported const with no TypeScript declaration to mirror, each with its stated reason — to ClassNameStylePropsSchema. Named for its own two keys, the const leaves no like-named declaration for a name-derived pairing to reach for.

.changeset/6051-gantt-flat-config-declared-keys.md

  • names packages/types/src/__tests__/zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⚠️ The JUSTIFICATION for that parity clause was retired (objectui#9743) — the same retirement objectui#5903's entry carries, for the same clause and the same reason; the clause itself stands and did not move. As first written it credited the zod-mirror-parity ratchet with a zero-drift reading for this pair, at a time when that ratchet measured three directions and was structurally blind to the MIRRORED-but-undeclared one (objectui#9711), so a zero from it recorded that it had not looked in that direction. objectui#9725 landed the fourth direction, and it covers this pair BY NAME: packages/types/src/__tests__/zod-mirror-parity.test.ts registers objectql.zod.ts#ObjectGanttSchema in both its mirror map and its declaration map; assertionMirroredUndeclaredMatchesLedger reconciles every registered pair's mirrored-but-undeclared key set against that pair's MirroredUndeclared ledger entry — never for a pair the ledger does not name — and assertionNoVacuousMirroredUndeclaredMeasurement refuses a measurement that has degenerated to any. ⛔ Read this pair's verdict off that reconciliation, which re-derives it on every run, rather than off any figure written here; when this paragraph was authored, on 2026-09-18, it required no MirroredUndeclared entry for the pair.

.changeset/6150-undeclared-but-consumed-keys.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⭐ AMENDED, and the amendment ships in this same release. The TreeViewSchema slice (604476d97) gave the key a zod arm after all — a NAMED REFUSAL (handlerKeyRefusal(key, 'runtime-slot', label)), never a shape — because "no mirror entry" is not neutral under BaseSchema.passthrough(): it meant an authored { "type": "tree-view", "onNodeClick": { "action": "toast" } } parsed GREEN, survived the parse, and reached a call site that expects a function. ⇒ the three clauses this bullet used to carry are no longer true of the code shipping beside it. The key is now a MEMBER of TreeViewSchema.shape and an authored value is refused BY NAME at path onNodeClick; it has LEFT zod-mirror-parity.test.ts's RuntimeOnlyDeclared for that file's KnownDrift; and it is no longer "the first pair to sit there without also sitting in UnmirroredDeclared" — draining it emptied that difference, so RuntimeOnlyDeclared is now a SUBSET of UnmirroredDeclared and the union of the two equals UnmirroredDeclared itself. ⛔ objectui#6152's ruling is untouched by any of this: what the key still does not have, and never will, is a z.function() shape — no serialized document could satisfy one.

.changeset/6170-retire-timeline-dead-keys.md

  • names packages/types/examples/data-display-examples.json → packages/types/examples/data-display-examples.json — edited by this change

    Also in this change: the in-repo example packages/types/examples/data-display-examples.json (its timeline node authored all three) is migrated to items / variant; the two content/docs/api/schema-reference.md snippets that authored events: [] now author items: []; and the plugin-timeline docs callout says retired rather than deprecated.

.changeset/6175-column-state-persistence.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Nothing is retired. Both spellings remain declared on DataTableSchema; onColumnReorder stays declared and stays unwired, exactly as the RuntimeOnlyDeclared ledger in zod-mirror-parity.test.ts records it. Which of the two survives is a declared-surface ruling that stays open and is deliberately not settled here.

.changeset/6475-gantt-block-face-declared.md

  • names skills/objectui/guides/page-builder.md → skills/objectui/guides/page-builder.md — edited by this change

    Maintainer ruling, objectui#6475 (2026-08-27), Option A — enforce as-is, immediately, no warning window (the startup-stage no-gradualism rule, objectstack#12668: transitions do not get phased windows without named external-user evidence, and none exists here). A census of every gantt block reachable through ObjectGanttSchema in this repository — the examples/schema-catalog fixtures, content/docs/plugins/plugin-gantt.mdx, and the published skills/objectui/guides/page-builder.md guide — found zero blocks missing the trio.

.changeset/6639-objectgrid-title-mirrored.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    The gain is the typed refusal: the mirror's .passthrough() base was already admitting any title unexamined, and it now enforces the declared string. zod-mirror-parity.test.ts's UnmirroredDeclared ledger records the key as worked off — the ledger's first shrink by repair (97 + 1 mirrored + 23 reclassified is what the seeded "121" now means).

.changeset/6872-app-shell-branding-title-jsdoc.md

  • names content/docs/guide/layout.md → content/docs/guide/layout.md — edited by this change

    Correct the AppShellBranding.title doc comment. It read "Page title suffix (sets document.title)" while useAppShellBranding assigns document.title = title wholesale — nothing is appended; the caller composes the whole string (the console passes "App label — Product name"). That comment ships in dist/index.d.ts and is the only description a consumer sees on editor hover, so a reader who followed it passed a suffix-only fragment and got a truncated title with no error. The comment now carries the same wording as content/docs/layout/app-shell.mdx, and agrees with the AppShellProps tables in the package README and content/docs/guide/layout.md. No runtime behaviour changes; the wholesale assignment and the four-surface agreement are now pinned by tests.

.changeset/6896-retire-chart-inline-data.md

  • names packages/types/examples/data-display-examples.json → packages/types/examples/data-display-examples.json — edited by this change

    ⚠️ One correction to the record the ruling rests on. The ruling states zero authorship of a populated series[].data outside tests across packages/ / apps/ / examples/. The re-measurement finds one such site inside those roots — packages/types/examples/data-display-examples.json (2 series) — plus four outside them, in documentation: content/docs/api/schema-reference.md (3) and content/docs/core/report-schema.mdx (1).

.changeset/6939-tree-view-nodes-mirror.md

  • names packages/types/examples/data-display-examples.json → packages/types/examples/data-display-examples.json — edited by this change

    For AUTHORS this widens on both faces. data goes from required to optional on the mirror and on the TypeScript twin in the same stroke; nothing that validated before validates less, and no document that type-checked as a literal stops doing so. A document authored on data — such as the tree-view entry in packages/types/examples/data-display-examples.json — is untouched, and both spellings together stay legal.

.changeset/6951-text-value-retired.md

  • names layout.ts → packages/types/src/layout.ts — edited by this change

    Two published faces, one retirement. The TypeScript interface TextSchema (@object-ui/types, layout.ts) declares value?: never; the Zod mirror TextSchema (@object-ui/types/zod, layout.zod.ts) declares value as a retirementTombstone(), so the key stays DECLARED and is refused BY NAME — a plain deletion would have let an authored value ride BaseSchema's .passthrough() into a silent blank, which is worse than the tolerated fallback it replaces. The value?: string members of TextSpanSchema and TabsSchema in the same file are other schemas' contracts and are unchanged.

  • names layout.zod.ts → packages/types/src/zod/layout.zod.ts — edited by this change

    Two published faces, one retirement. The TypeScript interface TextSchema (@object-ui/types, layout.ts) declares value?: never; the Zod mirror TextSchema (@object-ui/types/zod, layout.zod.ts) declares value as a retirementTombstone(), so the key stays DECLARED and is refused BY NAME — a plain deletion would have let an authored value ride BaseSchema's .passthrough() into a silent blank, which is worse than the tolerated fallback it replaces. The value?: string members of TextSpanSchema and TabsSchema in the same file are other schemas' contracts and are unchanged.

.changeset/6951-tree-view-data-retired.md

  • names packages/types/examples/data-display-examples.json → packages/types/examples/data-display-examples.json — edited by this change

    What was measured, on this branch's base. TreeViewSchema declared two spellings for its one inline-nodes slot — nodes (read second) and data (read third: boundData || schema.nodes || schema.data || [] at renderers/data-display/tree-view.tsx:105), both declared by objectui#6150. data had been REQUIRED until 777e5c6f4 (PR fix(types): tree-view mirror stops requiring the limb it reads third (group 2 of objectui#6939) #7533) made it optional, so this retirement starts from a declared-and-optional member on both faces. The in-repo corpus at the retirement: seven tree-view nodes under examples/schema-catalog and packages/types/examples plus one content/docs fence — six on nodes, two on data (packages/types/examples/data-display-examples.json and content/docs/api/schema-reference.md), both rewritten; no package source authored either spelling.

.changeset/7344-handler-string-any-mirrors.md

.changeset/7654-floating-chatbot-trigger-icon-tombstone.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Every other tombstone in this package pairs ?: never with a retirementTombstone() refusal on the Zod twin. There is no twin here to carry one: FloatingChatbotConfig has no Zod mirror at all, and floatingConfig sits in the UnmirroredDeclared ledger (zod-mirror-parity.test.ts, complex.zod.ts#ChatbotSchema). BaseSchema is .passthrough(), so the whole floatingConfig object rides through unvalidated — before this change and after it. Minting a mirror to host a refusal would be the declared-but-UNMIRRORED axis (objectui#6152), a different defect: a key can be mirrored and inert, or unmirrored and live, and fixing one says nothing about the other. This change does not widen into it.

.changeset/7694-chart-series-chart-type-alias-refusal.md

  • names zod/index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    • Before: series: [{ name: 'revenue', chartType: 'line' }] validated green through @object-ui/types/zod (safeValidateSchema, objectui check / objectui validate, any pipeline that keeps parse()'s output) — and the key was gone from the output, so a consumer of the parse result drew that series in the chart's own family, precisely what the author was overriding. On the TypeScript face the key was merely an excess property on a fresh literal; a widened object carrying it assigned structurally. - After: the same document REFUSES at series[i].chartType (issue code invalid_type) with one message on both channels — the parse-time issue and the .describe() metadata: Unrecognized key(s) on this chart series: \chartType`. Did you mean `chartType` → `type`? …followed by the reason and the remedy. Writetype: 'bar' | 'line' | 'area'. On the TypeScript face ChartDataSeries.chartTypeis a?: nevertombstone, so both the fresh literal and the widened assignment aretsc errors. - **Both written** ({ type: 'bar', chartType: 'line' }) is refused at chartTypealone — the key is not folded ontotypeand no precedence is minted between the two spellings. - **Which documents to scan.** The narrowing does not stop atChartDataSeriesSchema; it reaches every document through the parents that embed it — ChartSchema.series (zod/data-display.zod.ts, z.array(ChartDataSeriesSchema)) and, one level further out, ReportSectionSchema.chart (zod/reports.zod.ts, ChartSchema.optional()). Authors meet it through safeValidateSchema() (zod/index.zod.ts, which parses AnyComponentSchema) and through the CLI's objectui validate command (packages/cli/src/cli.ts). In practice: every chartnode'sseries[], and every report section whose chart` carries one.

.changeset/7735-zod-mirrors-stop-authoring-defaults.md

  • names layout.zod.ts → packages/types/src/zod/layout.zod.ts — edited by this change

    What changed. All 41 .default() call sites under packages/types/src/zod/ are removed — layout.zod.ts 22, crud.zod.ts 11, form.zod.ts 5, views.zod.ts 2, app.zod.ts 1. @object-ui/components reconciles the third face a separate finding found: flex's registration defaultProps.align seeded 'center', the value its own renderer never applies, so a designer-made node laid out differently from a hand-authored one; it now seeds 'start'.

.changeset/7804-tree-view-handler-slot.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    The pair moves from zod-mirror-parity.test.ts's RuntimeOnlyDeclared to its KnownDrift, which empties the former of the one entry the latter did not also hold — so the two unmirrored ledgers are now in a containment relation, and the cross-ledger figure that recorded their difference states the containment instead.

.changeset/7917-export-breadcrumb-object-tree-zod-schemas.md

  • names index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    AnyComponentSchema declares 107 node component types. 105 of them could be named on the ./zod barrel — ButtonSchema.safeParse(node), which is what a designer, a form builder or a targeted test needs. The arms declaring type: 'breadcrumb' (navigation.zod.ts) and type: 'object-tree' (objectql.zod.ts) could not: both were already export const in their own module, but index.zod.ts — the package's only zod entry point — did not re-export them, so the schemas existed, were maintained, and were applied by the union while no consumer could name them.

.changeset/7926-page-node-refuses-actions.md

  • names content/docs/guide/layout.md → content/docs/guide/layout.md — edited by this change

    Scope. One key, by name; the node is NOT strict. A census over this tree read 91 authored page-tagged objects with a blind-spot reading of 8 unreadable sites, and found only actions (3 sites, all in content/docs/guide/layout.md) and breadcrumbs (1 site, its own question, untouched) surviving passthrough on a real page node — every other undeclared key belongs to a different declaration that merely spells type: 'page'. PageNodeSchema still passes unknown renderer props through.

.changeset/8338-retire-toast-action.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    The parity ledgers drain with it, every figure re-derived by zod-mirror-parity.test.ts's own AST and mirror instruments rather than stepped by hand: KnownDrift 42 entries / 64 keys → 41 / 63 (the entry's whole content, so the entry went too — the ledger's first loss by RETIRING a key rather than by moving either face toward the other), and WiderThanDeclared 23 / 36 / 47 arms, split 6 / 30 / 0 / 11 → 22 / 35 / 45, split 6 / 29 / 0 / 10. The pair itself stays registered, so EXPECTED_MIRROR_PAIRS does not move.

.changeset/8344-node-recursion-point-redirect.md

  • names zod/index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    Two mechanical notes for anyone editing the wiring. AnyComponentSchema is built in zod/index.zod.ts from all 13 category modules and 14 modules import zod/base.zod.ts, so the arm cannot be an import — z.lazy defers evaluation, not the module graph. It is a written option slot that index.zod.ts fills inside AnyComponentSchema's own initializer, and it is a z.union option rather than a z.lazy holder because z.lazy memoises its getter: a holder would let whichever module graph parsed first decide the accept set for the whole process. Both constraints are measured, and the reasoning lives on defineNodeComponentUnion in zod/base.zod.ts.

  • names index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    Two mechanical notes for anyone editing the wiring. AnyComponentSchema is built in zod/index.zod.ts from all 13 category modules and 14 modules import zod/base.zod.ts, so the arm cannot be an import — z.lazy defers evaluation, not the module graph. It is a written option slot that index.zod.ts fills inside AnyComponentSchema's own initializer, and it is a z.union option rather than a z.lazy holder because z.lazy memoises its getter: a holder would let whichever module graph parsed first decide the accept set for the whole process. Both constraints are measured, and the reasoning lives on defineNodeComponentUnion in zod/base.zod.ts.

  • names zod/public-blocks.zod.ts → packages/types/src/zod/public-blocks.zod.ts — edited by this change

    ⚠️ Dated note, 2026-09-28 — a second category module — objectui#10872. Later still in this same release zod/public-blocks.zod.ts joins them as well and imports zod/base.zod.ts too, so both counts in the mechanical note above are two higher, not the one the objectui#10859 note says; the reason the arm cannot be an import is unchanged. The rest of this entry, and that note, are kept as their readings.

.changeset/8478-describe-line-addresses.md

.changeset/8478-zod-pins-form-layout.md

.changeset/8499-node-slot-registered-arms.md

  • names zod/layout.zod.ts → packages/types/src/zod/layout.zod.ts — edited by this change

    • SemanticElementSchema (zod/layout.zod.ts) — the seven HTML sectioning tags renderers/layout/semantic.tsx registers: aside main header nav footer section article. - HtmlElementSchema (zod/layout.zod.ts) — the 37 safe flow/inline tags renderers/basic/html-elements.tsx registers (h1…h6, p, a, ul, img, …), plus the per-tag keys that module forwards to the DOM (href, target, rel, title, src, alt, width, height, dateTime, cite). ⚠️ Dated note, 2026-09-27 — that set has since gained code — objectui#10756. At this change TAGS and this arm both named 37 tags; both now name 38, and the parity pin counts 38. The rest of this entry is kept as the reading of this change. - InputShorthandSchema (zod/form.zod.ts) — email / password, the two aliases renderers/form/input.tsx registers onto the input renderer with inputType pinned. inputType is deliberately NOT declared on this arm: the wrapper spreads its own value last, so an authored one is overwritten. ⚠️ Dated note, 2026-09-28 — inputType is now declared on this arm, as a refusal — objectui#8762. Later in this same release the arm declares inputType on both faces and refuses it by name (?: never on the TypeScript face, a retirementTombstone on the zod mirror, at path inputType), with guidance pointing at { "type": "input", "inputType": "email" }. So "inputType is deliberately NOT declared on this arm" no longer holds; the reason does, since the wrapper still spreads its own value last. The rest of this entry is kept as the reading of this change. - UiCalendarSchema (zod/form.zod.ts) — ui:calendar, the date-picker primitive renderers/form/calendar.tsx registers under exactly that key (skipFallback, because bare calendar belongs to the plugin-calendar view).
  • names zod/index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    Why 49 is acceptable here where 2 was a defect: the 2 were data blocks a consumer had a standing reason to validate on their own, and objectui#7917 (PR feat(types): re-export BreadcrumbSchema and ObjectTreeSchema from the zod barrel (objectui#7917) #8777, open at the time of writing, and the holder of zod/index.zod.ts) exists to export exactly those. The 47 added here are HTML primitives and two input aliases — they have no per-tag consumer to serve, and exporting a SemanticElementSchema / HtmlElementSchema pair would publish a NAMED authoring surface (z.enum families, not per-tag schemas) that this card's ruling does not cover: the ruling is "arm the registered renderers", not "add public exports to @object-ui/types". So the metric is left to move and said out loud instead. ⇒ Whoever next runs that measurement should expect 49, and whoever wants the number back down should treat naming these families as its own decision. If PR feat(types): re-export BreadcrumbSchema and ObjectTreeSchema from the zod barrel (objectui#7917) #8777 lands first, the same movement reads 0 to 47.

.changeset/8505-grid-columns-breakpoint-narrowing.md

.changeset/8516-8556-mirror-partial-record-narrowing.md

  • names zod/layout.zod.ts → packages/types/src/zod/layout.zod.ts — edited by this change

    | key | mirror was | mirror is | | :-- | :--------- | :-------- | | GridSchema.columns (zod/layout.zod.ts) | z.record(z.string(), z.number()) | a PARTIAL record over the six breakpoints | | ReportComponentSchema.exportConfigs (zod/reports.zod.ts) | z.record(z.string(), ReportExportConfigSchema) | a PARTIAL record over ReportExportFormat |

.changeset/8572-chatbot-body-retired.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Why this key and not another. It was the ONE place in this vocabulary where body did not mean "what goes inside this component": zod-mirror-parity.test.ts carried the pair under KnownDrift as "two different meanings of one key", and the same collision was the whole reason chatbot was the single arm of the component union whose output was not assignable to SchemaNode. Both ledger rows move with this change, and the two pins that recorded the old state are INVERTED rather than deleted (see below).

.changeset/8598-zod-subpath-single-bundled-module.md

  • names src/zod/index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    The defect. This package declares "sideEffects": false, and src/zod/index.zod.ts fills the node recursion point as the initializer of its AnyComponentSchema const. tsc emitted that barrel as a module whose only other content is re-exports — so a bundler resolving import { CardSchema } from '@object-ui/types/zod' followed the re-export to dist/zod/layout.zod.js, needed nothing from the barrel's own body, and the flag let it drop that body whole. The fill went with it, and every child slot then validated against the pre-spec(types): redirect the node recursion point from BaseSchemaCore to AnyComponentSchema — measured at 9 newly-refused documents, and it drops 118 phantom strict refusals #8344 BaseSchemaCore arm — the ~21 base keys and nothing type-specific — with no error, no warning and no way for the guard inside the dropped code to notice. objectui#8344 shipped that window DECLARED and pointed here to close it.

.changeset/8871-page-node-refuses-breadcrumbs.md

  • names layout.ts → packages/types/src/layout.ts — edited by this change

    What was measured, on this branch's base 93127bd6f. Zero readers, with a point-access probe rather than a bare word: on that base \.breadcrumbs scores 0 tree-wide (exit 1) against \.breadcrumb\b's 12 files tree-wide (10 under packages/) as the lit control. At head the same two probes read 16 and 13 and \.breadcrumbs is exit 0 over 4 files — every hit one of this branch's own four files (this changeset, the refusal pin, layout.ts, zod/layout.zod.ts) quoting the probe string, and the pin's own exclusions put head back at exit 1. The base reading is the measurement; the head reading is this branch's echo of it. The bare word would have lied — it also names Sentry's own unrelated concept (app-shell/src/observability/sentry.ts) and appears in two comments listing UI surfaces (core/src/utils/record-title.ts, layout/src/NavigationRenderer.tsx), so a bare probe reports five readers that do not exist.

  • names zod/layout.zod.ts → packages/types/src/zod/layout.zod.ts — edited by this change

    What was measured, on this branch's base 93127bd6f. Zero readers, with a point-access probe rather than a bare word: on that base \.breadcrumbs scores 0 tree-wide (exit 1) against \.breadcrumb\b's 12 files tree-wide (10 under packages/) as the lit control. At head the same two probes read 16 and 13 and \.breadcrumbs is exit 0 over 4 files — every hit one of this branch's own four files (this changeset, the refusal pin, layout.ts, zod/layout.zod.ts) quoting the probe string, and the pin's own exclusions put head back at exit 1. The base reading is the measurement; the head reading is this branch's echo of it. The bare word would have lied — it also names Sentry's own unrelated concept (app-shell/src/observability/sentry.ts) and appears in two comments listing UI surfaces (core/src/utils/record-title.ts, layout/src/NavigationRenderer.tsx), so a bare probe reports five readers that do not exist.

  • names content/docs/guide/layout.md → content/docs/guide/layout.md — edited by this change

    Three author sites, all teaching passages in content/docs/guide/layout.md, and that count corrects the actions refusal's "1 site": its census reads every git-tracked JSON file, every json fence in .md/.mdx, and every TS/TSX object literal via the TypeScript AST (PR fix(types,docs): refuse actions by name on the page node, teach the shape that draws (objectui#7926) #8870), and it undercounted for two different reasons. The Schema API block declared breadcrumbs?: ArrayANGLE-BRACKETS({ label, href }) outright and its literal does carry type: 'page', but that literal sits inside a markdown typescript fence — a fence language the census's json-fence reader never visits, so it was never read at all. Best Practices §2 authored it on a fragment inside a json fence the census does read, but that fragment never writes type, so a page-tagged filter correctly excluded it. No example app, catalog fixture, template or customer document writes the key, so the refusal strands no authored document in this tree.

.changeset/9067-zod-barrel-named-arms.md

  • names packages/types/src/zod/index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    Two new exported symbols, both on packages/types/src/zod/index.zod.ts:

.changeset/9092-inline-locale-declared-face.md

  • names layout.ts → packages/types/src/layout.ts — edited by this change

    • AppComponentSchema.label (app.ts) - ObjectGridSchema.label and .description (objectql.ts) - PageNodeSchema.aria.ariaLabel (layout.ts)

.changeset/9511-record-id-is-a-string.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⭐ The declaration alone would not have been enough, and this is the reusable part. A TypeScript declaration does not run at parse time. The hand-written zod mirror is the only face in this repository that can refuse an authored number, so narrowing the declaration without the mirror would have shipped declared !== enforced on a published surface — and zod-mirror-parity.test.ts would have stayed GREEN through it, because that instrument asserts a mirror accepts everything its declaration declares and a mirror left WIDER passes. Both faces moved together for that reason.

.changeset/9659-node-recursion-point-inert-clause.md

  • names zod/index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    Prose amended where this change falsified it, ⛔ not rewritten. zod/index.zod.ts said defineNodeComponentUnion "wraps it rather than replacing it" — true only while a wrapper existed. zod/base.zod.ts's note on the loose parameter bound still names ChatbotSchema's record body in the present tense; the paragraph is kept for the reason it records and carries an AMENDED note saying the exclusion set that pin reads is now empty.

.changeset/catalog-layout-props-sweep-4891-4890.md

  • names examples/schema-catalog/test/layout-props-conversion.test.tsx → examples/schema-catalog/test/layout-props-conversion.test.tsx — edited by this change

    Also in the same sweep, and visible only in the published example catalog rather than in any package API: 140 catalog nodes that were already flex / stack / container stopped hand-writing their own declared props in className (items-center → align, justify-between → justify, gap-2 → gap, flex-wrap → wrap, p-4 → a container's padding) — 231 tokens in all (objectui#4891). Breakpoint-prefixed overrides and everything decorative stay in className, because the props are not responsive. Both facts are ratcheted in examples/schema-catalog/test/layout-props-conversion.test.tsx.

.changeset/layout-default-jsdoc-7361.md

  • names layout.ts → packages/types/src/layout.ts — edited by this change

    The published @default documentation on two layout.ts members now matches the value the renderer actually applies. ContainerSchema.maxWidth documented 'lg' while container.tsx applies schema.maxWidth ?? 'xl', and the shared FlexLayoutProps.align documented 'center' while flex.tsx applies schema.align || 'start' and stack.tsx applies schema.align || 'stretch'. The renderers are unchanged — they are the authority for what runs — so only the docblocks moved; align now states both consumers in prose instead of carrying a single @default, because one member shared by two deliberately divergent component types cannot have one correct default.

.changeset/object-view-unmirrored-keys-7779.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Who is NOT affected: every correctly typed document, and every document that never wrote these keys — absent stays valid on all nine. No renderer changed. The parity ledger (zod-mirror-parity.test.ts) records the move: UnmirroredDeclared 14 entries / 96 keys to 14 / 87, the ObjectViewSchema entry re-derived into the SPEC-DERIVED half because the mirror now references the spec in code.

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Angle-bracketed names in the quoted prose above are rewritten as ANGLE-BRACKETS(name): GitHub deletes tag-shaped fragments from a stored body, and a quote that silently loses the identifier it is about is worse than a visible repair.

Compared the checked-out tree with 7728c67c8 (merge-base with origin/main): 92 file(s) changed outside .changeset/, read against 1953 pending declaration(s) that publish a body (2574 pending in total). · run

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3552.9 KB 3574.6 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-D7WfoV63.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 570.62KB 136.73KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 230.46KB 63.94KB
fields (index.js) 260.89KB 66.19KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.40KB 11.41KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 53.15KB 15.45KB
plugin-charts (index.js) 84.07KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.77KB 37.47KB
plugin-designer (index.js) 216.57KB 44.72KB
plugin-detail (index.js) 245.70KB 64.54KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.14KB 44.58KB
plugin-gantt (index.js) 178.11KB 44.87KB
plugin-grid (index.js) 231.72KB 63.65KB
plugin-kanban (index.js) 49.49KB 15.54KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 34.29KB 10.13KB
plugin-tree (index.js) 14.58KB 5.17KB
plugin-view (index.js) 90.32KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.07KB 2.39KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

…props and children (objectui#11276)

The guide's "Pick the right layout primitive" section said the layout
primitives all "take `children` and read every key off the node", and
tabulated `flex`'s props. With this PR an authored `flex` takes its props
AND its child list in its `properties` bag, and the flat spelling is
refused by name on both authoring faces, so the sentence taught a shape
`objectui validate` refuses.

Two sentences change, nothing else: the intro now says `stack`, `grid`,
`container` and `box` keep reading every key off the node while `flex`
takes its props and `children` in `properties`, and the `flex` / `stack`
intent sentence names the refusal. The table's "Key props (renderer
defaults)" column stays as it is: those are the renderer's defaults, and
only where they are authored moved. Net `skills/**` lines: 0 (353 → 353).

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3552.9 KB 3574.6 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-D7WfoV63.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 570.62KB 136.73KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 230.46KB 63.94KB
fields (index.js) 260.89KB 66.19KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.40KB 11.41KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 53.15KB 15.45KB
plugin-charts (index.js) 84.07KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.77KB 37.47KB
plugin-designer (index.js) 216.57KB 44.72KB
plugin-detail (index.js) 245.70KB 64.54KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.14KB 44.58KB
plugin-gantt (index.js) 178.11KB 44.87KB
plugin-grid (index.js) 231.72KB 63.65KB
plugin-kanban (index.js) 49.49KB 15.54KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 34.29KB 10.13KB
plugin-tree (index.js) 14.58KB 5.17KB
plugin-view (index.js) 90.32KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.07KB 2.39KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)

domain:ui seat 2 · session_01JG2jy8a9su7ia4Hx7zxv42 · 2026-10-01T22:46Z。席位已对照 diff 校正草稿(PR 正文的草稿也已同步修正)。卡上复核:ACCEPT 见 objectui#11276,契约复审档复核 5942151375 PASS。

改了什么。 落地您在 objectui#11300 的裁决「A」:页面里手写的 flex 节点,参数和子节点改写进 spec 规定的 properties 包;平铺写法被 objectui validate 按键名拒收,并给出改法。ObjectUI 自己的文档与示例同批迁移,包括 70 份 schema-catalog 文档(248 个节点)、layout.md、flex.mdx、types 的 README 与示例,以及 @object-ui/core 的 flex() 构建器。已存的平铺 flex 节点照常渲染,只是写作面收紧。另有一处受管指南:skills/objectui/guides/page-builder.md §3b 原先说「布局节点都在节点上读键」,现在点明 flex 例外(净 0 行)。

为什么改。 spec 的页面契约要求组件参数住在 properties 包里,而 ObjectUI 过去只认平铺的 flex。结果是两端各说一套:objectstack showcase 按 spec 写的 16 处 flex 在 ObjectUI 严格面上全被拒,ObjectUI 文档教的写法又被 os validate 拒。AI 按任一端写,都会在另一端报错。统一后,showcase 的 61 个运行时 flex 节点按节点自身判定在两个校验面上全部通过。整树读数是 58/61,余下 3 处是 object-metric 的 filter 写法,与 flex 无关,已另立 objectstack-ai/objectstack#21251。

风险与代价(含回滚)。

  • 这是写作面的破坏性变更:照旧文档平铺写 flex 的外部作者会被校验拒收。changeset 已按 objectui 的规则标为 minor,带 BREAKING(authoring)横幅和迁移说明:把 direction / justify / align / gap / wrap / children 移进 properties。
  • 渲染不变(有 pin 证明已存的平铺节点照画),所以线上已存页面不受影响。
  • 改动 10787 行,超过 5000 行的人工合并线,所以需要您批准。其中绝大部分是 70 份 catalog 文档的机械迁移,复核已逐字节复现其中 68 份。CI 全绿。
  • 回滚 = revert 这一个 squash commit。

席位意见。 建议批准。路线是您裁过的「A」,形状照 triage 的 addendum,没有发明 spec 行(17.5.0 没有 flex 行,包内成员按引用复用 ObjectUI 自己的 mirror)。验收各项都有实测,契约复审档复核 PASS。一处可选措辞:复核建议把 §3b 意图句中的分号改成句号("direction: "col" on a flex. objectui validate refuses flat flex props."),免得读成「因为会被拒,所以不要设 col」。不阻塞;改动会换 head,需重跑一次复核。另一处顺序提示:PR objectui#11404 也改同一份指南,但改的是另一节,两者无冲突,谁后落地谁先合并 main。

你要做的。 在本 PR 留一条 APPROVED review(os-zhuang 或 hotlong 任一)。批准后由认领席转 ready、挂 auto-merge,走合并队列落地。

domain:ui seat 2 · 维护者速读(终稿) · 2026-10-01T22:46Z


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 1, 2026 23:52
@os-zhuang
os-zhuang enabled auto-merge October 1, 2026 23:52
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 1, 2026
Merged via the queue into main with commit 138ad45 Oct 2, 2026
45 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-11276-flex-properties-bag branch October 2, 2026 00:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

3 participants