Skip to content

fix(types): AnyComponentSchema prints every category union by name, so its declaration leaves TypeScript's serialization ceiling (objectui#11573) - #11590

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-11573-any-component-emit-headroom
Oct 4, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-11573-any-component-emit-headroom

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11573
Clause-②: yes

objectstack main was not over the ceiling when this was measured. At objectui BASE 7a7660c against objectstack main 1a23054, AnyComponentSchema's declaration read 992,625 on TypeScript's own counter (TypeScript 6.0.3). The ceiling is 1,000,000, so the headroom was 7,375 (0.74%), smaller than the card's 9,426. With this PR the declaration reads 12,294, and the reading is the same against both specs.

What changed

  • The sixteen category unions AnyComponentSchema lists now each have a named type. It is an exported interface in the union's own module. It extends the union's inferred type and adds no member: options is restated by reference only because an empty interface is refused by lint. The value is unchanged: the union is bound to a module-private XSchemaInferred const, and export const XSchema: XZodType = XSchemaInferred.
    • The names: LayoutZodType, FormComponentZodType, DataDisplayZodType, FeedbackZodType, DisclosureZodType, OverlayZodType, NavigationZodType, ComplexZodType, ObjectQLComponentZodType, ObjectQLPublicBlockComponentZodType, CRUDComponentZodType, ReportUnionZodType, ViewComponentZodType, AIComponentZodType, DesignerUnionZodType, PublicBlockComponentZodType.
    • The ZodType suffix follows ReportNodeZodType / DrillDownReportZodType, because the XSchemaType names in these files are z.infer aliases.
  • One explanation, not a sixth. It lives in the new section "Why every category union's TYPE is named" on AnyComponentSchema in index.zod.ts, and each union site's docblock points to it. The RecordLineItemsBlockSchemaType docblock in public-blocks.zod.ts said the headroom was "tracked in objectui#11573" and used the present tense for the inline print. It now points to the pin, and the inline print is in the past tense.
  • The pin is a new test, not a gate: packages/types/src/__tests__/any-component-emit-headroom-11573.test.ts. It sits beside any-component-union-fanout.test.ts. Nothing in any workflow changed.
  • The changeset is .changeset/11573-any-component-emit-headroom.md, a patch for @object-ui/types.

The printed .d.ts text of existing exports changes; the types do not

  • What moved is printed text. AnyComponentSchema's declaration in dist/zod/index.zod.d.ts used to spell out every union inline. It now prints import("./layout.zod.js").LayoutZodType and its siblings. The sixteen unions' own declarations now read export declare const LayoutSchema: LayoutZodType;, and the union's body is printed once, on the private declare const LayoutSchemaInferred.
    • dist/zod/index.zod.d.ts went from 2,300,932 to 1,232,912 bytes.
    • The dist/zod/*.d.ts total went from 4,953,529 to 3,895,071 bytes.
  • Structural identity, proven before and after. This was a one-off check: two dists side by side, resolved through one node_modules, so they read the same zod and @objectstack/spec.
    • BASE 7a7660c dist vs this branch's dist (both built against the installed spec): 56 compile-time rows under tsc --strict, exit 0.
    • For each of the 16 unions, AnyComponentSchema and StrictAnyComponentSchema: Equal on z.input, Equal on z.output, and mutual assignability of the schema types.
    • Also Equal on the return types of validateSchema and safeValidateSchema.
    • Lit control: two different unions compare unequal.
  • The names are exported from their modules, not added to the @object-ui/types/zod entry. Declaration emit can reference a name from another module only if that module exports it. The barrel index.zod.ts re-exports none of them, so nothing new is importable. Clause-②: yes above is copied from the claim. The claim expected the names to widen the ./zod surface and said the review would read the answer from the diff. The answer is that the barrel is unchanged.

Readings (TypeScript's approximateLength, worktree TypeScript 6.0.3)

objectui @objectstack/spec AnyComponentSchema (counter) pin's reading
BASE 7a7660c installed 17.6.0 (lockfile) 980,329 980,295
BASE 7a7660c built from objectstack main 1a23054 992,625 992,591
47c8c67 (named) installed 17.6.0 12,294 12,260
47c8c67 (named) objectstack main 1a23054 12,294 12,260
47c8c67, DisclosureSchema alone un-named (ablation) installed 17.6.0 21,458
  • "Counter" is the exact approximateLength at the end of declaration emit's serializeTypeForDeclaration, read from an instrumented copy of the worktree's typescript.js in scratch. That is the card's own instrument, and nothing from it lands in the tree.
  • "Pin's reading" is the test's instrument on the unmodified compiler. It is always 34 below the counter: that is what the builder adds after its last truncation check. So it moves exactly with the counter (BASE → main: +12,296 on both instruments).
  • H4, the installed spec vs main. Before this PR the two specs differed by +12,296 on this declaration, about 1.2% of the ceiling. After it they differ by 0. The spec's shapes are now printed inside the named unions' own declarations, never in AnyComponentSchema's.
  • Where the spec delta came from (BASE). Most of it is ObjectQLPublicBlockComponentSchema, 98,183 → 111,269, with ObjectGridBlockSchema inside it going 15,011 → 23,219. LayoutSchema went 281,061 → 280,333.
  • H2, which members were inline (BASE, installed spec). All 16 category unions were inline. Each one's counter contribution: LayoutSchema 281,061 · PublicBlockComponentSchema 143,622 · ObjectQLComponentSchema 119,738 · ObjectQLPublicBlockComponentSchema 98,183 · FormComponentSchema 54,759 · ComplexSchema 51,829 · DataDisplaySchema 45,434 · ReportUnionSchema 33,923 · OverlaySchema 31,460 · ViewComponentSchema 23,790 · DesignerUnionSchema 22,461 · FeedbackSchema 18,982 · NavigationSchema 17,300 · DisclosureSchema 9,245 · AIComponentSchema 9,108 · CRUDComponentSchema 7,528.
    • Two arms were inline: AppComponentSchema 9,042 and CloudPlanStatusSchema 2,297.
    • Two arms were already named: AppSchemaRendererNodeSchemaType and PageKindNodeSchemaType.
    • The members sum to 979,762 against the declaration's 980,329; the remainder is the wrapper and the two names.
    • The card's 143.6k for PublicBlockComponentSchema is confirmed: 143,622.
  • After this PR, only the two arms are inline, about 11.3k of the 12,294.

The pin

It reads the node builder's own counter and does not use a proxy.

  • How it reads the counter. checker.typeToTypeNode passes its run-time maximumLength parameter straight through. The parameter is not on the typed public surface. The pin calls it with declaration emit's flags and AnyComponentSchema's type. With NoTruncation set, the builder reports truncation to the tracker exactly when the counter crosses the stated maximum, which is the comparison TS7056 makes at the ceiling.
  • The ceiling is read from the compiler (noTruncationMaximumTruncationLength), not restated.
  • Lit control. A maximum the declaration cannot fit under must report truncation. If a TypeScript upgrade drops or moves the parameter, the pin goes red; it does not go quietly green.
  • The builder's per-node cache. The builder caches serialized types per enclosing node, truncation verdict included. The tracker answers trackSymbol with true, so the builder caches nothing from a probing call.
  • Margin: a tenth of the ceiling. The pin fails above 900,000.
    • The pin reads against the installed spec, the lockfile's 17.6.0, because that is what pnpm test resolves.
    • The Spec Main Shape Gate compiles against main. That delta was +12,296 before this PR and is 0 after it.
    • So 100,000 is about 8× the largest installed-vs-main delta measured, and about 10× the largest arm ever added inline (record:line_items, 9,871). A green pin under the installed spec leaves the gate's compile under the ceiling with room to spare.
  • Enumeration. All 20 members, in source order, each with its kind (category union or arm) and how it prints: the name, or inline. A new member, a member that loses its name, or a renamed type turns the pin red until it is classified there. A second assertion encodes the rule: a category union prints by name, while arms may stay inline.
  • Wall clock (shared box, under the verify lock). The file takes 9.3–10.6s. Almost all of it is the import phase: building the package program and resolving the type. The four tests take 73–120ms.
    • The program build sits at module scope, so it is not bound by the 15s test timeout.
    • On failure, the message adds each inline member's contribution, searched to the nearest 1,000.
  • Red on BASE. The file was copied into a scratch BASE worktree and run there (removed afterwards). 3 of 4 tests failed:
    • margin: "reads 980295 … past 900000", with the per-member contributions;
    • enumeration: 16 category unions read inline;
    • rule: 16 inline category unions.
    • The lit control passed.

H3: naming changes print, not type

  • Compile-time rows in the pin file. Each named type gets one row (NamesItsUnion):
    • its z.input, z.output and _zod equal those of z.ZodDiscriminatedUnion over its own options;
    • it is the very member AnyComponentSchema carries (the Extract row, as in record-line-items-arm-10872.test.ts).
    • Plus a lit control: another union's options compare unequal.
    • The rows combine their verdicts as a tuple, never as A & B. true & false is never, and never satisfies Assert's constraint.
  • Reverse verification. The rows were committed first (3b4f156). Then DisclosureZodType gained _zod: DisclosureSchemaInferredType['_zod'] & { input: any }, through the objectstack scripts/ablation-replace.mjs (wrap mode; anchor 1 → 0, blob 0833fd7240cc → 24c4d1e693f3).
    • The mutated source still compiled: tsc --noEmit exit 0. It is invisible to assignability.
    • tsc -p tsconfig.test.json refused exactly the _Disclosure row: TS2344, exit 2.
    • Restored by git checkout HEAD -- PATH: blob == HEAD 0833fd7240cc, and git diff HEAD is empty.
  • Enumeration ablation. DisclosureSchema's annotation was dropped, by the same tool and restore. The pin went red, 2 failed / 2 passed. The diff shows DisclosureSchema changing from DisclosureZodType to inline. The reading moved 12,260 → 21,458.

Gates

All exit codes were read before any pipe.

On landing head f3d1dce (the last commit only adds the changeset):

  • pnpm --filter @object-ui/types build: exit 0, "dist completeness: 1 package(s) complete (146 emitted files verified)"
  • pnpm --filter @object-ui/types type-check: exit 0; this is tsc --noEmit, the examples project and the test project
  • pnpm --filter @object-ui/types test: exit 0, 351 files / 9,424 tests passed; the pin file is included (350 test files at BASE, 351 here)
  • check:readme-exports: exit 0, "563 of them self-imports judged (563 real, 0 wrong-path, 0 fabricated)"
  • check:skill-examples: exit 0
  • check:doc-snippets: exit 0, "777 of 777 block(s) judged, 0 failed"
  • check:doc-examples: exit 0
  • check:published-dist: exit 0
  • check:doc-types: exit 0
  • check:spec-symbols: exit 0
  • check:changeset-claims: exit 0
  • check:pending-changeset-literals: exit 0
  • check:control-bytes: exit 0
  • changeset:check: exit 0
  • check:new-line-citations: exit 0, "0 new citation(s)"
  • check:test-path-roots: exit 0
  • type-check:coverage: exit 0
  • check-changeset-presence: exit 0

Consumers, at 3b4f156 (src identical to f3d1dce):

  • turbo run type-check for @object-ui/core, @object-ui/cli and @object-ui/react: 12/12 tasks successful.
  • turbo run type-check for @object-ui/components, @object-ui/plugin-kanban, @object-ui/plugin-tree, @object-ui/plugin-dashboard, @object-ui/plugin-detail and @object-ui/app-shell: 35/35 tasks successful. Every type-check task was a cache miss, so each one really ran.
  • The other plugin-* consumers of ./zod are left to CI. The structural identity check above is why they cannot differ.

Spec Main Shape Gate reproduction, narrowed.

  • @objectstack/spec was built and packed from objectstack main 1a23054 in a separate objectstack worktree, then injected with scripts/spec-main-shape-gate.mjs inject into a separate objectui worktree.
  • Then TURBO_FORCE=true turbo run type-check ran for core, cli and react, with their build closure, which includes @object-ui/types build (where TS7056 surfaces).
  • Before (7a7660c): 12/12 tasks successful. After (f3d1dce): 12/12 tasks successful.
  • This is narrowed. The full gate is a whole-repo pnpm type-check, and CI runs it on this PR. The injection never touched this branch's install or any commit.

Lint, targeted at the 17 changed .ts files: eslint --no-inline-config, 0 errors. There were 3 warnings, all no-explicit-any on lines this PR does not touch. The full pnpm lint belongs to CI.

Acceptance notes

  • validateSchema and safeValidateSchema still print in full. Their inferred return types print the full output union: counter 446,147 / 446,122 on 17.6.0, and 450,694 / 450,669 on main. Naming the containers does not change that, because z.output is computed structurally. That is about 45% of the ceiling, so it is no reach today. It is an observation, not a finding; carrier: none.
  • A cross-worktree turbo cache hazard, met during this run. Every objectui worktree on this box shares one turbo cache under the shared checkout. Turbo does not hash node_modules, and the gate workflow's header warns about exactly this.
    • My two TURBO_FORCE=true reproduction runs against the injected main spec wrote main-spec build outputs into that cache, keyed on objectui sources at 7a7660c and f3d1dce.
    • A later unforced build in my own worktree replayed them, and check:doc-snippets went red on three blocks of packages/plugin-grid/README.md (TS2322). After a forced rebuild against the installed spec it read 0 failed.
    • The entries keyed on f3d1dce were overwritten by that forced rebuild. The 23 entries from the reproduction runs, including the ones keyed on 7a7660c (main at dispatch), could not be removed from here: the deletion was refused by this session's permission check. The run's report names them.
  • That accidental reading is also a real one. The three packages/plugin-grid/README.md snippets fail against declarations built from objectstack main. It is an observation for the 17.7.0 adoption; carrier: objectui#11334.

This PR's attribution, written as prose because a footer does not survive an edit: session https://claude.ai/code/session_01CPvhwGcirXqBGEdPSb72TZ, dispatched by the domain:spec @ objectui PM seat.


Generated by Claude Code

claude added 3 commits October 4, 2026 01:49
… lists (objectui#11573)

Each of the sixteen category unions composed into `AnyComponentSchema` now
has a named exported interface in its own module that extends the union's
inferred type and adds no member, so declaration emit prints a reference to
it instead of re-serializing every union's body inside `AnyComponentSchema`.
The values and the types a consumer reads are unchanged; only the printed
`.d.ts` text moves.

Claude-Session: https://claude.ai/code/session_01CPvhwGcirXqBGEdPSb72TZ
Co-authored-by: Claude <noreply@anthropic.com>
…very member's named/inline status (objectui#11573)

Reads the node builder's own counter for `AnyComponentSchema`'s declaration
(declaration emit's flags, through `checker.typeToTypeNode`'s run-time
`maximumLength` parameter, with a lit control that the parameter is
honoured), fails past a tenth of the ceiling below it, enumerates every
member as named or inline, and pins with compile-time rows that each
named category-union type is its union.

Claude-Session: https://claude.ai/code/session_01CPvhwGcirXqBGEdPSb72TZ
Co-authored-by: Claude <noreply@anthropic.com>
… (objectui#11573)

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

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 57 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/10872-container-children-channel.md

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

    Also in this change, with no behaviour change: objectql.zod.ts's two public-block arms (object-metric, object-master-detail-form) build their properties member with the same propsBag helper as the other public-block arms, instead of a byte copy of it. The member's description text is unchanged.

.changeset/11522-kanban-rule-dialect-retired.md

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

    • KanbanConditionalFormattingRule is now an interface that extends SpecConditionalFormattingRule, with field, operator, value, backgroundColor, borderColor and textColor declared ?: never. It used to be the union of SpecConditionalFormattingRule and KanbanNativeConditionalFormattingRule. - KanbanNativeConditionalFormattingRule is removed. Importing it is a compile error (TS2305). - KanbanConditionalFormattingRuleSchema, the rule schema ObjectKanbanSchema applies, is a module export of this package's src/zod/objectql.zod.ts. It is not on the @object-ui/types/zod barrel or on any other entry of the package's exports map, so it is not an import a consumer can name. It is the spec ListViewSchema.conditionalFormatting rule taken by reference and extended, not a union. It keeps the spec rule's strictness and its style map. Its condition is the same schema the list view's and the grid's { condition, style } arm reads, so a string condition is still not canonicalized into an envelope and '' is still accepted. The six retired keys are retirement tombstones.

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

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

    Both halves move together. The TS declaration (packages/types/src/objectql.ts) and its zod mirror (src/zod/objectql.zod.ts) gain the same ten keys at the same requiredness — all optional — and no KnownDrift entry is added. navigation is taken from @objectstack/spec's NavigationConfigSchema by reference rather than restated, matching ObjectGridSchema.navigation.

.changeset/5905-componentinput-inputtype-tombstone.md

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

    The write was measured as a no-op before it was deleted, and re-measured on this branch's base rather than inherited from the card. A structural census over every inputs: array in the repository (211 regions, all tracked TS/TSX/JS sources) scores inputType at exactly ONE authoring site — the plugin-markdown registration — against name 953, type 969, label 966, description 194, enum 119, required 86 and binding 4 in the same pass over the same regions, so the instrument was not blind. The other 192 in-repo inputType hits are a DIFFERENT face: FormField.inputType (zod/form.zod.ts), the text-input renderer's prop, and SchemaBuilder.inputType, none of which sit on a ComponentInput. The publication path is unchanged and was re-confirmed: packages/sdui-parser/src/index.ts forwards exactly seven keys per input — name, type, of, required, enum, binding, description — so an authored inputType could not reach the published sdui.manifest.json even in principle.

.changeset/5926-empty-action-visible-when.md

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

    emptyAction was the one authored-node exception in the tree. The empty-state CTA slot resolved the registry directly — ComponentRegistry.get(node.type) — and mounted the result itself, so the node never passed through SchemaRenderer and its visibleWhen was never evaluated. @objectstack/spec accepts the key (SchemaNodeSchema carries visibleWhen, and data-display.zod.ts types emptyAction as a SchemaNode), so an author wrote a gate, the platform took it, and nothing enforced it — declared-not-enforced, the same class c86185eb5 closed for record:alert, one level down.

.changeset/6349-name-authority-batch-3.md

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

    @object-ui/components — ComboboxOption now IS @object-ui/types' declaration. The component declared its own { value, label }, a strict subset of the ComboboxOption that @object-ui/types declares for ComboboxSchema.options and mirrors in form.zod.ts ({ value, label, disabled? }). The component now re-exports the types declaration (through the @object-ui/types/form subpath — the root barrel does not publish the name), so the name ComboboxOption exported from @object-ui/components gains the optional disabled?: boolean member. Every value that type-checked before still does — nothing narrows and no key changes type; the one thing that moves is keyof ComboboxOption, so a consumer that EXHAUSTS the type (a Record over its keys) will need the new key. Note that the Combobox component itself does not read option.disabled — that member was already declared on the @object-ui/types face and is now visible on this one too; it is recorded as a separate finding, not changed here.

.changeset/6349-types-internal-name-collisions-batch-1.md

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

    BreadcrumbItem / BreadcrumbSchema — re-pointed, because one copy was stale. Both were declared in data-display.ts and in navigation.ts. The data-display pair was not a second dialect but a strict SUBSET: no key declared differently on either side, and missing BreadcrumbItem.icon / onClick / siblings and BreadcrumbSchema.maxItems. Everything that reads a breadcrumb was already on the navigation declaration — registry.ts maps the 'breadcrumb' component type to it, src/index.ts re-exports it under the bare names, zod/navigation.zod.ts mirrors it (icon, onClick, siblings, maxItems included), the ui:breadcrumb renderer consumes it, and the component's own documentation page documents icon and maxItems. data-display.ts now re-exports the one authority.

.changeset/6396-previous-values-dom-leak.md

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

    Scope is the runtime leak only. The declared key stays exactly as declared (packages/types/src/form.ts, packages/types/src/zod/form.zod.ts are untouched): it has a live consumer, so there is nothing here for the enforce-or-remove channel.

.changeset/6646-breadcrumb-separator-max-items.md

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

    BreadcrumbSchema has declared both since it shipped (packages/types/src/navigation.ts, mirrored in zod/navigation.zod.ts), and separator is additionally advertised to authors on the component's own documentation page. The renderer contained zero occurrences of either name: it always emitted the bare BreadcrumbSeparator and it never collapsed. That made separator the sharper of the two — an author who read the page, wrote "separator": "/" and saw a chevron got feedback identical to having misspelled the key, with nothing to tell the two apart.

.changeset/6938-checkbox-wrapper-class.md

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

    packages/components/src/renderers/form/checkbox.tsx:36 reads cn("flex items-center space-x-2", schema.wrapperClass) — classes on the wrapper div around the box and its label — and neither the TypeScript interface in packages/types/src/form.ts nor the zod mirror in zod/form.zod.ts declared the key. It compiled through BaseSchema's index signature and parsed through .passthrough(), admitted unexamined. The same key, on the same class of read, is declared on FileUploadSchema and FilterBuilderSchema (objectui#6150); the checkbox was left out only because its doc page's schema block is a six-line summary.

.changeset/6939-kanban-column-cards.md

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

    Breaking, deliberately. KanbanColumn declared its card list as items in complex.ts and in the zod mirror complex.zod.ts. Every board reads cards. Measured on origin/main 78a3cc238: KanbanImpl.tsx reads .cards on 12 lines, KanbanEnhanced.tsx on 8, and bucketCardsIntoColumns twice more as col.cards || []; .items had zero read sites in either board (a same-shaped .title control on the same two files returns 8 and 3, so those zeros are readings and not a mis-shaped probe). Both catalog entries, the plugin docs and content/docs/api/schema-reference.md all author cards.

.changeset/6940-rowactions-boolean-mirror.md

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

    The hand-written zod mirror in zod/data-display.zod.ts declared rowActions: z.array(z.any()).optional(). Every other face of the same key says boolean: the TS declaration it mirrors (rowActions?: boolean), the renderer's destructuring default (rowActions = false), its two truthiness gates and two colSpan arithmetic sites, the registered authoring input ({ type: 'boolean', label: 'Show Row Actions' }), defaultProps: { rowActions: true }, and the renderer's own docblock example, which authors "rowActions": true. The mirror was the single outlier — and the published one, so safeValidateSchema refused the exact spelling the component's documentation, defaults and authoring UI all teach. Two shipped examples/schema-catalog entries (user-table.json, full-featured-table.json) failed validation for this and no other reason; both now validate unchanged.

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

    The list view's same-named rowActions in zod/objectql.zod.ts — z.array(z.string()), the legacy bare-name action list on ObjectGridSchema — is a different key that is correct as it stands, is in parity with its own TS twin (rowActions?: string[]), and is not touched.

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

  • 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 data-display.zod.ts → packages/types/src/zod/data-display.zod.ts — edited by this change

    Two published faces, one retirement — and why a tombstone, not a deletion. The TypeScript interface TreeViewSchema (@object-ui/types, data-display.ts) declares data?: never; the Zod mirror TreeViewSchema (@object-ui/types/zod, data-display.zod.ts) declares data as a retirementTombstone(). BaseSchema already declares data?: any (z.any().optional() on the mirror), so DELETING the member would not have refused the key — it would have ADMITTED it, unvalidated, through the base member, and the renderer would have drawn an empty tree. The tombstone on the extended schema shadows the base member on both faces; the pin measures the base accepting the very document the extended schema refuses.

.changeset/7081-overlay-trigger-union.md

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

    This is a widening, not a replacement: every singular trigger keeps type-checking unchanged. The Zod mirror is untouched — zod/overlay.zod.ts already spelled every one of these keys z.union([SchemaNodeSchema, z.array(SchemaNodeSchema)]) — and so is the runtime: every overlay renderer hands schema.trigger to renderChildren, whose Array.isArray branch has served the array form all along, and every registration's defaultProps.trigger ships as an array. What changes is that the TypeScript face stops under-reporting an accept set that already ships: copying a renderer's own default into a typed document is no longer a type error against the type that shipped it. The seven docs pages' trigger rows follow the declaration.

.changeset/7113-chart-data-model.md

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

    .extend() with a NEW key still works and preserves the fold and the refinement; .optional(), z.discriminatedUnion, z.toJSONSchema and safeValidateSchema are all unaffected. Nothing in this repository calls the throwing combinators on either const, and the published surface already ships refined mirrors (objectql.zod.ts, complex.zod.ts, form.zod.ts, app.zod.ts), so the class is not new — but it is a real behaviour change on a published export and it belongs in the release note rather than in a reviewer's file.

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

    .extend() with a NEW key still works and preserves the fold and the refinement; .optional(), z.discriminatedUnion, z.toJSONSchema and safeValidateSchema are all unaffected. Nothing in this repository calls the throwing combinators on either const, and the published surface already ships refined mirrors (objectql.zod.ts, complex.zod.ts, form.zod.ts, app.zod.ts), so the class is not new — but it is a real behaviour change on a published export and it belongs in the release note rather than in a reviewer's file.

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

    .extend() with a NEW key still works and preserves the fold and the refinement; .optional(), z.discriminatedUnion, z.toJSONSchema and safeValidateSchema are all unaffected. Nothing in this repository calls the throwing combinators on either const, and the published surface already ships refined mirrors (objectql.zod.ts, complex.zod.ts, form.zod.ts, app.zod.ts), so the class is not new — but it is a real behaviour change on a published export and it belongs in the release note rather than in a reviewer's file.

.changeset/7200-object-form-section-style-keys-undeclared.md

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

    The authored-metadata type now agrees with @objectstack/spec, whose FormSectionSchema is a strict object declaring neither key, and with the ruling's rationale (maintainer 2026-09-01, verbatim): "retire the reads … Declaring the keys was weighed and not adopted: it would formally invite free Tailwind strings into authored metadata, the exact class the boundary exists to keep out." A ?: never tombstone was not used: ObjectFormSection has no zod mirror (ObjectFormSchema in zod/objectql.zod.ts does not declare sections), so there is no parse door to refuse at, and a tombstone is still a declaration in completion and in the published .d.ts.

.changeset/7265-types-user-filter-field-derives.md

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

    zod/objectql.zod.ts declared two schemas under names @objectstack/spec/ui already exports. They were triaged separately, by reading their sites, and went different ways.

.changeset/7322-object-kanban-group-by-limit.md

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

    Breaking for authored metadata: ObjectKanbanSchema.groupField is RETIRED (objectui#7322, ADR-0049 enforce-or-remove), and the two keys the object-kanban renderer actually reads — groupBy and limit — are now DECLARED and validated on both published faces: the TypeScript interface in objectql.ts and the Zod mirror in zod/objectql.zod.ts.

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

.changeset/7352-drill-down-config-mirror.md

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

    DrillDownConfigSchema is the zod mirror of DrillDownConfig, and both declarations that carry drillDown reference it — ChartSchema (zod/data-display.zod.ts) and ObjectDataTableSchema (zod/objectql.zod.ts) — so the published validator under @object-ui/types/zod reads the key for the first time (objectui#7352).

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

    DrillDownConfigSchema is the zod mirror of DrillDownConfig, and both declarations that carry drillDown reference it — ChartSchema (zod/data-display.zod.ts) and ObjectDataTableSchema (zod/objectql.zod.ts) — so the published validator under @object-ui/types/zod reads the key for the first time (objectui#7352).

.changeset/7363-objectql-union-arms.md

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

    ObjectGallerySchema and ObjectDataTableSchema are members of ObjectQLComponentSchema on both faces — the TS union in objectql.ts and the zod union in zod/objectql.zod.ts — so AnyComponentSchema, and with it validateSchema / safeValidateSchema / objectui validate, has an arm for object-gallery and object-data-table nodes (objectui#7363).

.changeset/7530-predicate-envelope-declared.md

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

    • ExpressionWire (type, main entry) — the TypeScript wire union, in packages/types/src/expression.ts. - ExpressionWireSchema (@object-ui/types/zod) — its runtime twin, hoisted out of zod/form.zod.ts (where it was module-private) into zod/expression.zod.ts and imported by both base.zod.ts and form.zod.ts. One envelope type, reused by reference; no second spelling.
  • names form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    • ExpressionWire (type, main entry) — the TypeScript wire union, in packages/types/src/expression.ts. - ExpressionWireSchema (@object-ui/types/zod) — its runtime twin, hoisted out of zod/form.zod.ts (where it was module-private) into zod/expression.zod.ts and imported by both base.zod.ts and form.zod.ts. One envelope type, reused by reference; no second spelling.

.changeset/7664-kanban-arm-plugin-dialect.md

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

    ⚠️ Dated note, 2026-10-03 — KanbanConditionalFormattingRuleSchema is no longer a union — objectui#11522. At this change KanbanConditionalFormattingRuleSchema was "the rule union the 'object-kanban' arm already applied": the native { field, operator, value } comparison or { condition, style }. Now it is one object, the spec list view's { condition, style } rule by reference, and the native rule and a top-level colour key (backgroundColor, borderColor, textColor) are refused by name; the 'kanban' arm it was shared with has itself retired in this release (objectui#8802). It keeps its name, and it is a module export of src/zod/objectql.zod.ts inside @object-ui/types, NOT an export of the @object-ui/types/zod barrel. Measured at objectui#11522's change: that barrel re-exports KanbanCardSchema, KanbanColumnSchema and ObjectKanbanSchema from the kanban family and not this schema, and no entry of the package's exports map carries it. So the bullet above that calls it "newly exported from @object-ui/types/zod" does not hold in this release either; whether it held at objectui#7664's own commit was not measured. The rest of this entry is kept as the reading of this change.

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

  • names zod/data-display.zod.ts → packages/types/src/zod/data-display.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.
  • names zod/reports.zod.ts → packages/types/src/zod/reports.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.
  • 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/7703-chatbot-dark-keys-retired.md

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

    Each member goes to ?: never on packages/types/src/complex.ts and to retirementTombstone(...) on packages/types/src/zod/complex.zod.ts — both halves, in lockstep, the convention MarkdownSchema.sanitize (objectui#6972), TimelineSchema.timeScale (objectui#6355) and ObjectViewSchema.viewTabBar (objectui#7779) already carry. Each refusal names the key, says why it is retired, and points at what to write instead; one string feeds both the parse-time message and the .describe() metadata, so the two cannot drift.

.changeset/7722-wrapper-class-five-more.md

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

    Each of renderers/form/switch.tsx, textarea.tsx, date-picker.tsx, select.tsx and renderers/data-display/list.tsx reads schema.wrapperClass onto its wrapper element, and neither the TypeScript interface (form.ts, data-display.ts) nor the zod mirror (zod/form.zod.ts, zod/data-display.zod.ts) declared the key. The reads compiled through BaseSchema's index signature (objectui#5155) and the values parsed through .passthrough(), admitted unexamined. The same key, on the same class of read, is declared on CheckboxSchema (b74a8598d), FileUploadSchema and FilterBuilderSchema (objectui#6150); these five were left out only because their doc pages never listed it.

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

    Each of renderers/form/switch.tsx, textarea.tsx, date-picker.tsx, select.tsx and renderers/data-display/list.tsx reads schema.wrapperClass onto its wrapper element, and neither the TypeScript interface (form.ts, data-display.ts) nor the zod mirror (zod/form.zod.ts, zod/data-display.zod.ts) declared the key. The reads compiled through BaseSchema's index signature (objectui#5155) and the values parsed through .passthrough(), admitted unexamined. The same key, on the same class of read, is declared on CheckboxSchema (b74a8598d), FileUploadSchema and FilterBuilderSchema (objectui#6150); these five were left out only because their doc pages never listed it.

.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'.

  • names crud.zod.ts → packages/types/src/zod/crud.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'.

  • names form.zod.ts → packages/types/src/zod/form.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'.

  • names views.zod.ts → packages/types/src/zod/views.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/7767-collapsible-trigger-union.md

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

    This is a widening, not a replacement: every singular trigger keeps type-checking unchanged. The Zod mirror is untouched — zod/disclosure.zod.ts already spelled this key z.union([SchemaNodeSchema, z.array(SchemaNodeSchema)]) — and so is the runtime: renderers/disclosure/collapsible.tsx hands schema.trigger to renderChildren, whose Array.isArray branch has served the array form all along, and that same registration's defaultProps.trigger ships as an array. What changes is that the TypeScript face stops under-reporting an accept set that already ships: copying the renderer's own default into a typed document is no longer a type error against the type that shipped it. The docs page's trigger row follows the declaration.

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

  • names navigation.zod.ts → packages/types/src/zod/navigation.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.

  • names objectql.zod.ts → packages/types/src/zod/objectql.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.

  • 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/7918-zod-lazy-getter-identity.md

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

    ⚠️ Also settled while locating the ten: AppMenuItemSchema has no declaration of its own — it is the barrel alias of app.zod.ts's MenuItemSchema, while the barrel's own MenuItemSchema is overlay.zod.ts's. Two different schemas, so the list really is ten entries and not nine.

.changeset/8331-data-table-empty-action-primitive-node.md

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

    Behaviour change on a published surface, deliberately. DataTableSchema.emptyAction is declared SchemaNode on both published faces — packages/types/src/data-display.ts and its Zod twin in packages/types/src/zod/data-display.zod.ts — and SchemaNode is BaseSchema | string | number | boolean | null | undefined. The empty-state render path additionally required typeof … === 'object', so an authored emptyAction: 'Create the first record' rendered nothing and reported nothing: declared wider than enforced, failing in the direction that loses the author's content without a diagnostic. A node that renders nothing today therefore starts rendering.

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

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

    What was wrong. The two faces shared nothing, and one of them had no JSON inhabitant at all. packages/types/src/feedback.ts declared action?: { label: string; onClick: () => void } — both members REQUIRED and onClick a function, so a JSON document could omit the key but never author it — while packages/types/src/zod/feedback.zod.ts declared z.union([SchemaNodeSchema, z.array(SchemaNodeSchema)]), a node or a list of nodes. Disjoint accept sets, one of them empty: the same document got a green safeParse and a tsc refusal, and no spelling satisfied both. renderers/feedback/toast.tsx read NEITHER — it reads variant, title, description, duration, buttonVariant, className and buttonLabel, and the file has exactly one ComponentRegistry.register call, so the zero is a reading and not a failed scan.

.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/ai.zod.ts → packages/types/src/zod/ai.zod.ts — edited by this change

    ⚠️ Dated note, 2026-09-28 — one more category module — objectui#10859. Later in this same release zod/ai.zod.ts joins the category modules AnyComponentSchema is built from and imports zod/base.zod.ts too, so both counts in the mechanical note above are one higher; the reason the arm cannot be an import is unchanged. The rest of this entry is kept as the reading of this change.

  • 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/8415-filter-builder-condition-id.md

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

    Breaking for authored metadata: a filter-builder CONDITION must now declare id (objectui#8415). It is declared on both published faces — the TypeScript interface FilterBuilderCondition in complex.ts and the Zod mirror FilterBuilderConditionSchema in zod/complex.zod.ts — so a key the renderer has always required is finally validated instead of silently discarded.

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

.changeset/8478-zod-pins-complex.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/form.zod.ts → packages/types/src/zod/form.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 |

  • names zod/reports.zod.ts → packages/types/src/zod/reports.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/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/8735-objectql-mirror-docblocks-not-defaulted.md

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

    Correct four zod/objectql.zod.ts docblocks that described the behaviour objectui#8317 removed. Since that change the zod mirrors strip imported @objectstack/spec defaults at this package's import boundary, but the docblocks on HttpRequestSchema, ListColumnSchema, SelectionConfigSchema and PaginationConfigSchema still said, in the present tense, that method, prefix.type, type and pageSize are defaulted on parse — the opposite of what each export does. Each now says the key is declared and accepted but NOT defaulted on parse.

.changeset/8801-object-kanban-allow-collapse-retired.md

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

    • the declarations retired here — packages/types/src/objectql.ts and its mirror packages/types/src/zod/objectql.zod.ts; - the pins that assert the retirement — object-kanban-allow-collapse-retired-8801.test.ts and bare-kanban-node-key-retired-8802.test.ts; - a comment in packages/types/src/zod/complex.zod.ts, recording that the deleted retiredZeroReadKanbanKey helper once carried this spelling on the SIBLING arm; - one row of content/docs/api/schema-reference.md; - the .changeset/ release notes that discuss it — this one, the two historical entries covering the sibling arm's own spelling, and objectui#9629's note recording the correction to this paragraph.
  • names packages/types/src/zod/complex.zod.ts → packages/types/src/zod/complex.zod.ts — edited by this change

    • the declarations retired here — packages/types/src/objectql.ts and its mirror packages/types/src/zod/objectql.zod.ts; - the pins that assert the retirement — object-kanban-allow-collapse-retired-8801.test.ts and bare-kanban-node-key-retired-8802.test.ts; - a comment in packages/types/src/zod/complex.zod.ts, recording that the deleted retiredZeroReadKanbanKey helper once carried this spelling on the SIBLING arm; - one row of content/docs/api/schema-reference.md; - the .changeset/ release notes that discuss it — this one, the two historical entries covering the sibling arm's own spelling, and objectui#9629's note recording the correction to this paragraph.

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

  • 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.

.changeset/8885-object-chart-drilldown-title-compareto.md

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

    ObjectChart.tsx reads all three off schema, and until now neither published copy declared any of them: not the TS interface (packages/types/src/objectql.ts) and not the zod mirror (packages/types/src/zod/objectql.zod.ts). They rode BaseSchema's index signature / .passthrough() and arrived unvalidated. drillDown was the sharpest case — this component's registry inputs advertise it to the designer palette, and @objectstack/spec publishes ChartDrillDownSchema for exactly this carrier, so an author was offered a key that neither published shape mentioned.

.changeset/8913-object-kanban-columns-declared.md

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

    What moved. ObjectKanbanSchema gains columns on both halves that move together — the TypeScript interface (objectql.ts) and its Zod mirror (zod/objectql.zod.ts). Retiring the bare kanban node type key (objectui#8802) removed the only face that judged a lane, and object-kanban had never declared the key, so it rode BaseSchema's [key: string]: any / .passthrough(): read by the renderer at three sites, named by no published face.

.changeset/8990-object-kanban-groupby-optional.md

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

    @objectstack/spec declares the key optional — groupBy: z.string().optional() on ObjectKanbanPropsSchema — while this package required it on the TypeScript declaration (packages/types/src/objectql.ts) and on the Zod mirror (packages/types/src/zod/objectql.zod.ts). objectui was therefore narrower than the protocol on a published key: ObjectKanbanSchema.safeParse and safeValidateSchema refused an object-kanban node the protocol accepts, and such a node could not be annotated with its own type.

.changeset/8992-user-actions-collapse-and-docblock.md

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

    objectql.zod.ts's UserActionsSchema read stripImportedDefaults(Spec).extend({ group, hideFields, rowColor }), an extension that existed only because @objectstack/spec did not declare those three keys while normalizeListViewSchema folded objectui's legacy showGroup / showHideFields / showColor onto them. The protocol adopted all three in 17.3.0 (objectui#5435's ruling), so the extension is now a second local copy of a protocol declaration — the shape two faces start drifting from — and it collapses into the plain by-reference re-export its own note always said it would become.

.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:

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

    • InputShorthandSchema (zod/form.zod.ts) — the email / password shorthand arm. - UiCalendarSchema (zod/form.zod.ts) — ui:calendar, the date-picker primitive renderers/form/calendar.tsx registers, a different component from the calendar plugin view that owns the bare literal.

.changeset/9309-object-gallery-filter-destination-typed.md

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

    ObjectGallerySchema.filter is typed as the destination its own docblock names — QueryParams['$filter'] — on both faces, the TS interface in objectql.ts and the zod mirror in zod/objectql.zod.ts (objectui#9309).

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

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

    The three authorable keys, each on BOTH faces. ObjectFormSchema.recordId (objectql.ts + zod/objectql.zod.ts), DetailViewSchema.resourceId (views.ts + zod/views.zod.ts) and DetailSchema.resourceId (crud.ts + zod/crud.zod.ts). ⚠️ The crud pair is DetailSchema, not DetailViewSchema, and it reaches the same renderer — not by symbol but by data flow: plugin-detail registers the 'detail' node type onto DetailView. A read that follows TypeScript symbols alone finds two keys and is incomplete.

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

    The three authorable keys, each on BOTH faces. ObjectFormSchema.recordId (objectql.ts + zod/objectql.zod.ts), DetailViewSchema.resourceId (views.ts + zod/views.zod.ts) and DetailSchema.resourceId (crud.ts + zod/crud.zod.ts). ⚠️ The crud pair is DetailSchema, not DetailViewSchema, and it reaches the same renderer — not by symbol but by data flow: plugin-detail registers the 'detail' node type onto DetailView. A read that follows TypeScript symbols alone finds two keys and is incomplete.

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

    The three authorable keys, each on BOTH faces. ObjectFormSchema.recordId (objectql.ts + zod/objectql.zod.ts), DetailViewSchema.resourceId (views.ts + zod/views.zod.ts) and DetailSchema.resourceId (crud.ts + zod/crud.zod.ts). ⚠️ The crud pair is DetailSchema, not DetailViewSchema, and it reaches the same renderer — not by symbol but by data flow: plugin-detail registers the 'detail' node type onto DetailView. A read that follows TypeScript symbols alone finds two keys and is incomplete.

.changeset/9549-tree-filter-declared.md

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

    ObjectTreeSchema.filter is declared on both faces, in the shape objectui#9309 settled for ObjectGallerySchema.filter: QueryParams['$filter'] by indexed access on the TS interface in objectql.ts, and the same two-arm union (array first) on the zod mirror in zod/objectql.zod.ts (objectui#9549).

.changeset/9606-object-kanban-card-title.md

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

    Both published faces of the object-kanban arm now name the key: the zod mirror ObjectKanbanSchema in zod/objectql.zod.ts and its TypeScript twin, the ObjectKanbanSchema interface in objectql.ts. Both declare it OPTIONAL, at the same requiredness the other face uses, so the two faces accept and refuse the same documents. (Located and cited by SYMBOL: line addresses in zod/objectql.zod.ts have drifted before, and this change is itself about a drifted mirror.)

.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/action-callback-retired-7068.md

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

    What was measured, on this branch's base (900f8d99). ActionCallback ({ type: 'toast' | 'message' | 'redirect' | 'reload' | 'custom' | 'ajax' | 'dialog', message?, url?, api?, method?, dialog?, handler? }) was declared in crud.ts, mirrored in zod/crud.zod.ts, re-exported by both barrels, and carried on the legacy ActionSchema as onSuccess? / onFailure?. Producers: the package's own phase2-schemas.test.ts fixture and three ts fences in content/docs/core/enhanced-actions.mdx — nothing else (git grep -l ActionCallback over packages content skills hit the five packages/types files; positive control SchemaNodeSchema hit 22). Runtime readers: none — ActionRunner imports UIActionSchema, never this interface, and its own ActionDef.onFailure is a different (runner-native) meaning. It was the THIRD meaning of one key: objectui#5934 had already retired the runner's callback meaning of onSuccess and converged it on the spec's block.

.changeset/issue-5373-retire-crud-schema.md

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

    crud had four declaration faces and no registered renderer, for the whole life of the key: the TS interface (packages/types/src/crud.ts), the zod mirror (packages/types/src/zod/crud.zod.ts), a dedicated branch in validateSchema that affirmatively PASSED it, and CRUDBuilder in @object-ui/core. A node spelling it painted the OBJUI-001 "Unknown component type" panel, and content/docs/api/schema-reference.md published it as reference material — so a reader (or an AI author) who copied the page got a red panel.

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.

Compared the checked-out tree with 0c012a7f4 (merge-base with origin/main): 17 file(s) changed outside .changeset/, read against 2101 pending declaration(s) that publish a body (2737 pending in total). · run

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 331 chunks) 3311.9 KB 3330.4 KB
Main entry chunk (gzip) 151.3 KB 350 KB
Entry file index-BT_7n-XR.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) 17.22KB 6.37KB
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.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 572.97KB 137.34KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 232.57KB 64.51KB
fields (index.js) 261.62KB 66.20KB
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) 35.66KB 9.49KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 39.47KB 11.25KB
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.04KB 3.92KB
plugin-calendar (index.js) 53.17KB 15.46KB
plugin-charts (index.js) 83.60KB 22.89KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 142.83KB 38.55KB
plugin-designer (index.js) 231.28KB 48.76KB
plugin-detail (index.js) 245.60KB 64.57KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 175.44KB 45.28KB
plugin-gantt (index.js) 179.16KB 45.06KB
plugin-grid (index.js) 235.69KB 64.78KB
plugin-kanban (index.js) 49.59KB 15.58KB
plugin-list (index.js) 116.46KB 28.93KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 38.80KB 11.71KB
plugin-tree (index.js) 14.51KB 5.15KB
plugin-view (index.js) 90.23KB 22.73KB
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.31KB 2.07KB
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 (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.16KB 1.96KB
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) 19.93KB 7.25KB
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

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: f3d1dcef9db65092e1d22dff02f2286968379609
Local-runs: none

Inputs, and nothing else: card objectui#11573 (body; triage 5972917927; claim 5975377899; os-dev-report 5975890312), PR #11590 (body, its 18-file list, the net diff against main at the head), and the check-runs on the head. Read-only throughout: git fetch and git show against origin/... refs were the only local reads; nothing was built, run or re-run. Reviewed as a subagent of the seat named in Reviewed-by:, which adopts this verdict.

① Derived judgments

Accept set — unchanged. RIGHT.

  • A1. Each of the sixteen category unions is the same z.discriminatedUnion('type', [...]) call as on main, re-bound through a module-private XSchemaInferred const and exported under its old name with an annotation. No arm is added, removed or reordered in any of the sixteen option lists (every hunk is a one-line rename of the const plus an appended interface and re-export).
  • A2. AnyComponentSchema's own option list is untouched: the index.zod.ts hunk is +27/-0 and is a docblock section only. The pin's enumeration test asserts the AST member count equals the runtime options.length, so a drift between the two would be red.
  • A3. Runtime values are identical objects, so validateSchema / safeValidateSchema / StrictAnyComponentSchema accept and refuse exactly what they did. The dev's cross-dist rows (56 rows, both dists resolved through one node_modules) cover z.input / z.output equality for all sixteen unions plus AnyComponentSchema and StrictAnyComponentSchema; CI's Type Check on the head is green over every in-repo consumer.

Public surface — per the package's exports map, no new published name. RIGHT.

  • P1. The published surface is packages/types/package.json exports: ., ./base, ./layout, ./form, ./data-display, ./feedback, ./overlay, ./navigation, ./complex, ./data, ./zod, ./internal/retired-field-keys. There is no wildcard subpath, so dist/zod/layout.zod.js and its fifteen siblings are not addressable by a consumer.
  • P2. The ./zod barrel (index.zod.ts) carries no star export; its named export lists are unchanged by this diff (comment-only hunk), so none of the sixteen XZodType interfaces is importable from @object-ui/types/zod. The . entry files re-export from the touched modules by name only (ObjectCalendarBlockConfig from objectql.zod), also unchanged.
  • P3. What a consumer does see is the printed .d.ts TEXT of sixteen existing exports and of AnyComponentSchema: each const's declared type becomes a named interface that extends its own inferred union type and restates options by reference, and AnyComponentSchema's declaration prints an import-type reference per union instead of the union's body. Under the contract-review rule that shipped .d.ts bytes unreachable as a name through exports are not the published accept set, this is a text move, not a widening. The sixteen names are reachable from the entry's type graph only as typeof XSchema, which a consumer could already obtain.
  • P4. Honest scope of the identity claim: the pin's compile-time rows prove Equal on z.input, z.output and _zod against the discriminated union over the same options, plus the Extract row that the named type IS the member AnyComponentSchema carries; the PR's cross-dist check proves MUTUAL ASSIGNABILITY of the schema types. Exact type identity of the schema object itself is not claimed and cannot be (an interface is not identical to the alias it extends), so a downstream pin spelled as identity over typeof LayoutSchema would move. No such pin exists in this repository (Type Check green); for an external consumer that is a .d.ts text change with structural equality, which is what patch describes. RIGHT, and correctly stated by the PR.
  • P5. Naming: the ZodType suffix follows the existing ReportNodeZodType / DrillDownReportZodType precedent in the same directory (the XSchemaType names there are z.infer aliases). RIGHT.
  • P6. public-blocks.zod.ts docblock: the RecordLineItemsBlockSchemaType why-section moves to past tense and points at the pin by file path and symbol, not by line. RIGHT (no cross-file line address introduced; Line Citation Gate green).

The pin (any-component-emit-headroom-11573.test.ts) — RIGHT, with two notes.

  • T1. It is a test beside any-component-union-fanout.test.ts, not a gate: the file list touches no workflow, and the Spec Main Shape Gate job is unchanged, as the triage ruling required.
  • T2. It reads the compiler's own counter through checker.typeToTypeNode's run-time maximumLength parameter under declaration-emit flags, reads the ceiling from ts.noTruncationMaximumTruncationLength rather than restating it, and carries a lit control (a maximum of 100 must truncate; the reading must be strictly inside the open-closed interval between 100 and the ceiling). The tracker answers trackSymbol with true so the builder's per-node cache cannot feed a later probe its earlier verdict. Margin is a tenth of the ceiling; the header states that the reading is taken against the INSTALLED spec and why that covers the gate's main-spec compile (delta measured +12,296 before naming, 0 after).
  • T3. Enumeration: all twenty members in source order, each classified as category union or arm by the presence of options, with the printed name or inline; a second assertion encodes the rule (a category union prints by name; arms may stay inline) with non-vacuity checks both ways. A member that is itself a union but is listed as an arm would be classified as a category union and required to be named, which is the conservative direction. RIGHT.
  • T4. The program build sits at module scope (the clocked tests take tens of milliseconds), the path seed is the gate-recognised dirname(fileURLToPath(import.meta.url)) spelling, typescript is a declared devDependency of @object-ui/types (ten sibling tests already import it), and tsconfig.test.json compiles the file (the dev's reverse verification showed the _Disclosure row going red under it). RIGHT.
  • Note 1 (not a finding): DECLARATION_EMIT_INTERNAL_FLAGS = 8 is a hard-coded internal flag value. The lit control covers the maximumLength parameter, not a renumbering of that internal flag on a TypeScript upgrade; the worst case is a slightly different print, still bounded by the margin test.
  • Note 2 (not a finding): the pin bounds AnyComponentSchema only. validateSchema / safeValidateSchema print their full output union (the dev measured about 45 percent of the ceiling) and are outside the card; see ③ O1.

Card acceptance — met.

  • C1. Measured against objectstack main at claim time: 1a23054, reading 992,625 (headroom 7,375, 0.74 percent), stated first in the PR body as the triage required. main was not over the ceiling.
  • C2. Every category union AnyComponentSchema lists has a named exported type in its own module; the PR states that the printed .d.ts text of existing exports changes while the types stay structurally identical, and proves it.
  • C3. The two inline arms that remain (AppComponentSchema, CloudPlanStatusSchema, about 11.3k together) are inside what the triage allowed; the reading after naming is 12,294 on both specs.

Repo rules on the diff — clean.

  • R1. No governed surface in the file list (Governed Surface Queue Guard green); the review is owed by the claim's Clause-②: yes limb and this record discharges it.
  • R2. Commit trailers on all three commits carry the model-free pair; no model identifier in the PR title, body, changeset, code or this record. English throughout.
  • R3. No packages/spec artifact is touched; no README or guide change is owed for a change that publishes no new name (README Export Check green).

Gate readings on the head (read at 2026-10-04T02:57Z; each conclusion recorded as read; in_progress is an honest reading, not a pass):

  • Spec Main Shape Gate — success. The card's own gate: this head compiled against @objectstack/spec built from objectstack main, the compile that went TS7056 on objectui#11570's first head.
  • Type Check — success (whole-repo tsc, every in-repo consumer of ./zod included).
  • Test (shard 1/8) through Test (shard 8/8) — all eight success (the pin file runs in this set; the dev's local run read 351 files / 9,424 tests in packages/types). Test (dist pins) — success (reads the built dist). Test, the shard rollup — in_progress at reading time; every shard it waits on is green.
  • Build & E2E — success (the head's own declaration build against the installed spec). Build Docs — success. Lint — success.
  • Governed Surface Queue Guard — success. Changeset Declaration — success. Changeset Bump Policy — success. Changeset Fixed Group Check — success. Changeset Claim Re-read — success (report-only, 57 pending bodies name a touched file; nothing blocks). Changeset Overwrite Report — success.
  • Line Citation Gate · README Export Check · Doc Snippet Type Check · Doc Component Type Check · Doc Example Id Check · Doc Fence Language Check · Skill Example Check · Skill Guide Path Check · Skill Eval Token Check · Docs Route Eager Closure Check · Internal Docs Link Check · Control Byte Scan · Shell Escape Residue Scan · Inert vi.mock Specifier Check · Pre-Install Import Graph Check · Action Ref Convention · Bundle Analysis · Live E2E (informational) · label — all success.
  • Skipped by design, not failures: Test (coverage), the coverage shard matrix job (four shards), dependabot.
  • Red: none. Readings: 42 check-runs — 39 success, 3 skipped, 1 in_progress (Test rollup).

② Semver level

Clause-②: no — the diff relaxes no accept set (A1–A3) and widens no public surface (P1–P4). The claim declared yes on the lead's expectation that the new names would be exported from ./zod, and wrote that the review would read the real answer from the diff; the diff answers no, and this record is that reading.

  • The changeset .changeset/11573-any-component-emit-headroom.md declares '@object-ui/types': patch. RIGHT for what the diff publishes: a declaration-text move with structural identity in a released package, no new importable name, no breaking change. skip-changeset would have been wrong (the fixed group's types package publishes a changed .d.ts); minor would have declared a widening that does not exist and bumped the 39-package fixed group for text. No major (objectui's rule; Changeset Bump Policy green). Changeset Declaration (presence) green.
  • The changeset body states what a consumer sees and names the pin by path; it carries no model identifier and no line address, and its summary line is the PR title.
  • The PR body's second line reads Clause-②: yes, copied from the claim, and the body itself says so and explains that the barrel is unchanged. With the level now adjudicated here, the body line and the patch changeset are reconciled by this record; no objectui gate reads a PR-body Clause-②: line, so no edit is required for landing. If the seat edits the body for any other reason, change that line to Clause-②: no so body and changeset agree on their face.

③ Boundary flags

Dev deviations (os-dev-report 5975890312):

  • D1 — shared turbo cache contamination (23 main-spec build entries under /home/user/objectui/.turbo/cache, keyed on objectui 7a7660c and f3d1dce sources; the f3d1dce ones since overwritten by a forced installed-spec rebuild). ESCALATED to the seat, the dispatch's owner and the only actor here with write access to that path: delete the 23 entries (69 files; hashes listed in the report's open question), or hand it to the maintainer. The hazard is live for any worktree whose package inputs hash as at 7a7660c; main having moved to 0c012a7 does not retire it for packages whose inputs did not change. Not a finding against the diff: nothing from the reproduction landed in the tree.
  • D2 — first check:published-dist run timed out outside the verify lock (exit 124 before its check phase). ANSWERED: not a reading; the lock-held re-run (exit 0) is the one that counts.
  • D3 — first check:readme-exports / skill-examples / doc-snippets / doc-examples runs hit PREREQUISITE NOT MET on unbuilt siblings. ANSWERED: not readings; the re-runs on a full build at the head were green, and the head's check-runs above are the gate verdicts.
  • D4 — Spec Main Shape Gate reproduction narrowed to the core/cli/react type-check closure. ANSWERED: that closure includes the @object-ui/types declaration build where TS7056 surfaces, so it answers the card's question; the full gate is the head's Spec Main Shape Gate check-run, recorded above exactly as read.
  • D5 — changeset patch beside the PR body's Clause-②: yes. ANSWERED in ② above: patch stands; the adjudicated declaration is no.
  • D6 — status reply said "pushed through f3d1dce" a few seconds before the push ran. ANSWERED: origin/claude/issue-11573-any-component-emit-headroom reads f3d1dce on fetch; no finding.

Open questions:

  • OQ1 — who clears the 23 cache entries. ESCALATED with D1; option A (delete) is the right one, for the dev's reason. The recipe half goes with it: a future dispatch that suggests the inject-then-type-check reproduction must say to run it with TURBO_FORCE=true AND a --cache-dir inside the scratch worktree, because TURBO_FORCE stops the reads and not the writes. That amendment belongs to the seat as the recipe's owner.

Out-of-scope findings (dev's carriers, confirmed or re-routed):

  • O1 — validateSchema / safeValidateSchema return types print the full output union, about 45 percent of the ceiling, unchanged by the naming. ANSWERED: agreed, no reach today and outside this card; no card is owed, and it stays recorded in the PR's acceptance notes.
  • O2 — three packages/plugin-grid/README.md snippets fail TS2322 against declarations built from objectstack main 1a23054, compiling fine against the installed 17.6.0. ESCALATED to the seat to carry as a one-line note on objectui#11334 (the 17.7.0 adoption), the carrier the dev named; this reviewer writes no second comment.
  • O3 — the inject-then-type-check reproduction poisons the turbo cache shared by every objectui worktree on the box. ESCALATED to the seat as the dispatch recipe's owner (same amendment as OQ1).

Implemented-by: claude/issue-11573-any-component-emit-headroom
Reviewed-by: session_01CPvhwGcirXqBGEdPSb72TZ

VERDICT: PASS


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants