Skip to content

fix(types): content-channel refusals name the parser tier's not-a-container warning (objectui#10928) - #10956

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-10928-not-a-container-clause
Sep 28, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-10928-not-a-container-clause

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #10928
Clause-②: no — message and .describe() text only; every accept or refuse verdict stays byte-identical (auxiliary face).

Implemented by the os-dev dispatched from the domain:spec seat, session https://claude.ai/code/session_012UwY3ahMixEFkfTUxMVkYm (mode:subagent). Base 5c94589f0, head 5833df79f.

What changed

Only the false clause changes. It changes on every refusal of a node whose renderer reads NEITHER content channel (family D and the E3 residual, objectui#9256). "no error, no warning, no element" now reads:

no render-time error or warning and no element; only the parser tier's not-a-container warning (objectui#9910) noticed it

This is the wording the objectui#10907 changesets settled on. Every site is in the past tense, so the clause is too. Every other byte of each string is unchanged; the proof is below.

  • zod mirror. The refusal message and its .describe() text are one string. There are 131 literal sites in the 12 *.zod.ts files, plus the two builders that compose the rest:
    • the shared neitherContentChannelGuidance in zod/tombstone.zod.ts (13 consts, 26 arms);
    • ai.zod.ts's local neitherChannel (6 arms).
    • The shared builder's three source lines were re-wrapped by hand. Its string value changes only in the clause.
  • TypeScript face. 149 body?: never / children?: never docblocks in the 12 matching packages/types/src/*.ts files. They ship in the emitted .d.ts.
  • Only comment and string lines move. Every changed line in the 12 TS files is a JSDoc continuation line. Every changed line in the zod files is a string-literal line. Other changed lines: 0 on each face (git diff -U0, filtered).
  • New pin: packages/types/src/__tests__/content-channel-parser-tier-clause-10928.test.ts.
  • Changeset: .changeset/10928-content-channel-parser-tier-clause.md, @object-ui/types: patch.

The parser tier, measured before the clause was written

Instrument. A one-shot probe, deleted and not committed. It builds the manifest from the live registry the way container-declaration-ratchet.test.tsx does: every KNOWN key, through manifestFromConfigs. components, fields and 18 of the 19 plugin-* packages (all but plugin-designer) are imported. It then runs validateTree once per leg.

Population. Every registry key, bare and namespaced, of the 81 node types whose refusal says "reads NEITHER content channel". That is 177 keys: 164 real registrations and 13 lazy stubs that have no inputs yet. None of the 177 declares a children input.

leg added to the bare node diagnostic it adds, identical on all 177 keys
children: [node] not-a-container (warning)
body: [node] or body: node not-a-container (warning), from checkRetiredBodyDialect over the same containment predicate
body: 'hi' (a scalar) unknown-prop (warning)
neither key nothing

Controls. div, ui:div, span and ui:span declare children. On them, adding children adds nothing, and adding a body list adds unknown-prop.

Reading. The dispatch assumed that a node "with body (not children)" stays silent. It does not: a body child list draws the same not-a-container. So the settled clause is true for both members, which share one string. A scalar body draws unknown-prop instead; see the Acceptance notes.

Census, base 5c94589f0 vs head 5833df79f

The card's 126 and 139 come from a line-bound grep. The same clause also sits on 25 sites where a line break or a '…' + '…' join falls inside it: 15 zod (the shared builder among them) and 10 TS docblocks. So the count below joins those first.

face old clause, base → head new clause, base → head control NEITHER content channel, base / head
zod 141 → 8 0 → 133 137 / 137
TS docblocks 149 → 0 0 → 149 162 / 162
  • The absent-token control qqzz_absent_token_9999 reads 0 in every cell.
  • The 8 old clauses left on the zod face are all in HeaderBarSchema: title, logo, nav, left, center, right, sticky and height.
    • Those keys are not content channels. The parser tier answers each of them with unknown-prop, not not-a-container.
    • So the settled clause would be false there, and they are left untouched; see the Acceptance notes.
  • The in-scope count is therefore 282 = 133 + 149, and each of those sites moved from the old clause to the new one.
  • Emitted dist, after pnpm --filter @object-ui/types build at head, joined the same way:
    • .d.ts: old 0, new 149.
    • JS: the 16 old clauses left are the 8 HeaderBarSchema strings, twice (zod/navigation.zod.js and zod/index.zod.js).

The command, exactly as run. The enumeration and the read both come from REF, and __tests__ is excluded. JSDoc continuations and '…' + '…' joins are removed, and an escaped quote is unescaped.

for REF in 5c94589f0 5833df79f; do for FACE in zod ts; do
  if [ "$FACE" = zod ]; then X='--exclude=*/__tests__/*'; P=packages/types/src/zod; else X='--exclude=*/zod/*'; P=packages/types/src; fi
  printf '%s %-3s ' "$REF" "$FACE"
  git archive "$REF" "$P" | tar -xO --exclude='*/__tests__/*' "$X" | perl -0777 -ne 's/\n[ \t]*\*[ \t]?/ /g; s/\x27[ \t]*\n[ \t]*\+[ \t]*\x27//g; s/\\\x27/\x27/g; my $o = () = /no\s+error,\s+no\s+warning,\s+no\s+element/g; my $n = () = /no\s+render-time\s+error\s+or\s+warning\s+and\s+no\s+element;\s+only\s+the\s+parser\s+tier\x27s\s+`not-a-container`\s+warning\s+\(objectui#9910\)\s+noticed\s+it/g; my $c = () = /NEITHER\s+content\s+channel/g; my $z = () = /qqzz_absent_token_9999/g; print "old=$o new=$n control=$c absent=$z\n"'
done; done

Verdicts unchanged, and every other byte of every message

Instrument. A one-shot probe, deleted and not committed. It records 777 keyed entries, taken once at base and once at head:

  • every exported zod schema (241), each over 6 generic inputs: {}, a children list, a body list, a scalar body, a scalar children, and both keys;
  • the .describe() text of every direct shape member of 196 object schemas;
  • safeValidateSchema over 85 node types, each over 4 legs: none, children, a body list and a scalar body.

Result.

  • Success flags, issue codes and issue paths are identical at base and head (116 successes at both).
  • Messages and describe texts: take the head payload and substitute the old clause back for the new one. The result is byte-identical to the base payload. So each string changed in the clause and nowhere else.
  • The old clause occurs 915 times in the base payload and 8 times in the head payload. Those 8 are the HeaderBarSchema refusals.
  • On the built dist, safeValidateSchema({ type: 'accordion', children: [...] }) answers invalid_type at children, and its message carries the new clause.

Reverse verification (ablation), after the fix was committed

Instrument. objectstack's scripts/ablation-replace.mjs, in WRAP mode. The anchor must hit the declared number of times, and the on-disk blob must move. The restore is proven by blob == HEAD and an empty git diff HEAD. Each leg puts the old clause back in one message source and runs the new pin.

mutation blob mutated to red / 10 red tests
the shared builder, back to its base text 8ff6af3b9 (the base blob) 5 both builder tests, object-grid × 2, the population
accordion's two literal arms 751bf2d6f 3 accordion × 2, the population
ai.zod.ts's neitherChannel, back to its base text 1fd44f9e0 (the base blob) 3 ai-form-assist × 2, the population
  • Each leg restored to its HEAD blob (da56e22f5, f7d88b6c5, a40e67ff6) with an empty git diff HEAD.
  • The types tests import ../zod/* source directly, so no build sits between a mutation and the run.

The pin, and why it holds the prose rather than the parser

@object-ui/types declares no workspace dependency, and its tests import none. So @object-ui/sdui-parser cannot be imported from here without declaring it (check:phantom-deps). The claim's file surface also put the pin under packages/types/src/__tests__/.

What the pin holds:

  • The shared builder. The clause stands exactly where the retired one stood, and the retired one is gone.
  • One arm per message source, each read through its refusal: a literal (accordion), the shared builder (object-grid) and the local builder (ai-form-assist). Each answers code invalid_type at the key's own path, and its message carries the clause.
  • The population. Every exported member whose refusal is the family-D sentence carries the clause, so a later slice that copies an older literal goes red. Non-vacuity is asserted by name over the three sources, not by count.

The parser behaviour the clause describes stays pinned where the parser and the registry live, and the new file's header names those pins:

  • containment-declared-slot-9910.test.ts and body-dialect-6771.test.ts, in packages/sdui-parser;
  • container-declaration-ratchet.test.tsx, in packages/components. It holds declared-slot ⇔ rendered-list over the live registry.

Whether to add a live-registry behavioural pin in packages/components as well is left as an open question for the seat.

Gates, head 5833df79f

The gate list was derived by hand from objectui's root package.json and .github/workflows/, against this diff.

gate result
pnpm --filter @object-ui/types build (tsc, vite, dist completeness) exit 0; "1 package(s) complete (134 emitted files verified)"
pnpm --filter @object-ui/types type-check (tsc, examples, tests) exit 0; tsc -p tsconfig.test.json --listFiles lists the new pin
pnpm --filter @object-ui/types lint exit 0, 0 errors; the warnings are existing no-explicit-any, none on the new file
pnpm exec vitest run packages/types/ 267 files, 5917 tests, all passed
check-changeset-presence · changeset:check · check-changeset-overwrite exit 0 each
check:pending-changeset-literals · check:control-bytes · check:new-line-citations (0 new) exit 0 each
check:handler-key-reads · type-check:coverage · lint:coverage · check:test-path-roots · check:unreferenced-sources · check:phantom-deps · check:published-tsconfig-exclude · check:esm-specifiers · check:spec-symbols · check:component-surface-parity · check:doc-types exit 0 each
check:changeset-claims report-only, exit 0. Each pending changeset it names for a touched file was read; none quotes the replaced clause.
governed guard --test over the diff's paths NOT GOVERNED (lit control AGENTS.md: exit 3)
  • NOT MEASURED: check:readme-exports. It judges README self-imports against every package's dist/*.d.ts. This tree built only @object-ui/types, so the gate printed "could not be judged … run pnpm build first" and exited 1. That is a prerequisite, not a finding. This diff touches no README and no export.
  • NOT MEASURED, left to CI: pnpm test beyond packages/types, the dom / dom-heavy projects, E2E and test:dist.

Serial constraints

  • Open PRs, mapped at hand-back: 13 open, files API, every page. Only objectui#10930 touches a file here: zod/complex.zod.ts, in the GlobalFilterSchema JSDoc.
    • git merge-tree of this head against its head 1daf7edf7 is clean.
    • Its head adds no old-clause site.
  • main moved two commits since the base, to 06a96e948. Neither touches packages/types or packages/sdui-parser, and git merge-tree against it is clean.

Acceptance notes: out of scope, not changed here

  • HeaderBarSchema's nine non-channel refusals (zod/navigation.zod.ts).
    • Eight carry the same "no error, no warning, no element": title, logo, nav, left, center, right, sticky and height. variant says "no error, no warning, no class".
    • The parser tier answers each of the nine keys with unknown-prop (measured, same probe).
    • safeValidateSchema({ type: 'header-bar', title: 'T' }) hands back the "no warning" message (measured on the built dist).
    • Same defect class as this card, but the fix needs a different clause, one naming unknown-prop. So it is reported to the seat, not swept here.
  • The nine objectui#8284 body?: never docblocks on box, span, container, flex, stack, grid, scroll-area, form and toggle (in layout.ts and form.ts).
    • They say a body "rendered an EMPTY element with no error and no warning".
    • Each of those registrations declares children, and a body list on it draws unknown-prop (measured on every bare and ui: key).
    • TypeScript face only. Same class; reported, not changed.
  • A scalar body on a reads-neither node draws unknown-prop, not not-a-container. The clause speaks of an authored content value, which in both channels is a child list or a node. The scalar case is not written into it.

Generated by Claude Code

…tainer warning (objectui#10928)

The family-D / E3 content-channel tombstone messages, their `.describe()`
text, the two builders that compose them (`neitherContentChannelGuidance`
and ai.zod.ts's `neitherChannel`) and the matching TypeScript docblocks said
an authored value rendered NOTHING with "no error, no warning, no element".
The render path is right, but the parser tier's `validateTree` warns
`not-a-container` for a child list in either key on a registration that
declares no `children` input. Only that clause changes, to the wording the
objectui#10907 changesets settled on; every other byte of each string stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UwY3ahMixEFkfTUxMVkYm
…ectui#10928)

The shared builder, one arm per message source (a literal, the shared
`neitherContentChannelGuidance`, ai.zod.ts's `neitherChannel`) read through
its refusal, and the population of family-D refusals all carry the clause
that names the parser tier's `not-a-container` warning. `@object-ui/types`
patch changeset for the published message text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UwY3ahMixEFkfTUxMVkYm
@github-actions

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 85 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/4895-retire-block-schema-family.md

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

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

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

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

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

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

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

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

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

  • 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/6105-tombstone-refusal-message.md

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

    One shared mechanism carries the text into both channels. retirementTombstone() (zod/tombstone.zod.ts) takes the guidance string ONCE and writes it to both z.never({ error }) — the parse-time issue message — and .describe() — the generated JSON-Schema and docs surface, unchanged. One string, so the two cannot drift.

.changeset/6124-handler-keys-json-refusal.md

.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 data-display.ts → packages/types/src/data-display.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.

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

  • 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/6349-types-internal-name-collisions-batch-2.md

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

    MenuItem — renamed to AppMenuItem in app.ts. app.ts declared a flat, all-optional interface (type?: 'item' | 'group' | 'separator', label, icon, path, href, children, badge, hidden) — the @deprecated legacy navigation item that AppComponentSchema.menu, AppAction.items and menuItemToNavigationItem read. overlay.ts declared a discriminated union, MenuCommandItem | MenuDividerItem, whose command arm requires label and whose both arms tombstone type as type?: never (objectui#6523) — precisely the key app.ts declares as a three-value enum. Re-pointing either at the other would have made an authored type: 'separator' legal on one side and a type error on the other, so the two names had to part. @object-ui/types continues to publish overlay's union as MenuItem and app's interface as AppMenuItem, exactly as before.

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

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

  • 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/6397-partial-schema-collapse-pin.md

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

    ⚠️ Dated note, 2026-09-28 — a generic re-spelling of this Omit does not collapse — objectui#9256. Later in this same release objectui#9256 declared a key-remapping OmitDeclared ({ [P in keyof T as P extends K ? never : P]: T[P] }, in packages/types/src/form.ts) and re-spelled the heritage of InputShorthandSchema and UiCalendarSchema with it. Such a mapped type iterates the named members and the index signature separately, so it drops exactly K, keeps every other named member with its modifiers, and keeps the index signature. The same spelling of this alias declares 28 properties at ButtonSchema and 62 at ObjectGridSchema in this release, where the shipped alias declares 1 at each; at this change's own commit, under the same TypeScript, it declares 27 and 61, the source counts in the table above. So "every generic re-spelling collapses" was not true when it was written. The alias is still left as written: re-spelling it would narrow what every instantiation accepts, a contract change and not a pin's business. The PartialSchema docblock and partial-schema-collapse-pin.test.ts carry the correction. The rest of this entry is kept as the reading of this change.

.changeset/6541-sonner-button-variant-enum.md

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

    What was wrong. The same key on the same component shipped as two disagreeing published faces: an open string to anyone validating (@object-ui/types/zod), and a closed six-member union to anyone type-checking (@object-ui/types, SonnerSchema.buttonVariant in feedback.ts). The TS face was already correct — only the mirror is changed here, so this is the mirror being made to agree with a declaration that sat beside it all along.

.changeset/6594-headercolor-mirror-enum.md

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

    The nearest precedent is fc62bb490 (.changeset/5853-tablecolumn-type-canonical-union.md), which narrowed TableColumn.type on the same three-ends pattern and exported a TABLE_COLUMN_TYPES tuple for the zod mirror to build its enum from. That shape is not available here and the difference is structural, not a preference: packages/types/src/views.ts is a type-only module, so a tuple there would add a runtime export to the package barrel (a value export cannot ride the barrel's export type block) and a runtime import edge from the zod entry into views.js. fc62bb490 had a second reason to export — producers needed its normalizeTableColumnType() at their emit seam — and headerColor has no producer that needs a runtime value. The literals are therefore written on each half and the anti-drift guarantee is carried by the pin above, which also covers the third end a shared tuple could not reach: the renderer, in a package @object-ui/types must not depend on.

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

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

  • 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/6687-chatbot-surface-authorable.md

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

    Measured on both declaration faces before the fix, each with a control that had to hit: schema.surface appeared 0 times in renderer.tsx against schema.placeholder at 3 (one per registration) and schema.processVisibility at 1; and ChatbotSchema (packages/types/src/complex.ts) declared 34 keys, not this one. Two faces agreeing is what made the zero a reading rather than a bad query.

.changeset/6881-retire-data-table-toolbar.md

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

    What was measured. The key was declared on both published faces — data-display.ts (toolbar?: SchemaNode[], "Table toolbar actions/content") and the Zod mirror (SchemaNode | SchemaNode[]) — documented, mirrored, and read by NOTHING: data-table.tsx, the registered renderer for type: 'data-table', contains the word only in two prose comments and never reads schema.toolbar. The sibling emptyAction slot on the same interface IS mounted through SchemaRenderer, so the census zero is a reading, not a blind query. An author who wrote a toolbar got a green document and a blank result, with no signal anywhere that said so — the declared-vs-enforced failure mode that is worst for AI-authored metadata, which has nothing but the declaration to go on.

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

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

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

  • 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/6939-overlay-trigger-mirror.md

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

    patch, not minor: the accept set only widens toward what already renders. Every key involved is optional, and children stays legal — it is BaseSchema's own optional key, merely no longer demanded here. No document that validated before this change stops validating; documents the renderers already draw start validating. The TypeScript twins in packages/types/src/overlay.ts move in the same stroke, so the published declaration and the published validator keep saying the same thing.

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

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

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

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

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

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

  • 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/6972-markdown-inert-keys-retired.md

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

    What was measured, on this branch's base. sanitize was declared ?: boolean with @default true on both published faces — data-display.ts and the Zod mirror — documented, and read by NOTHING. Worse than an ordinary inert key, it implied a switch that does not exist: sanitization is unconditional. rehypePlugins in plugin-markdown/src/MarkdownImpl.tsx is a module-level const array whose last link is [rehypeSanitize, sanitizeSchema], handed to ReactMarkdown as-is — no ternary, no if, no runtime assembly. MarkdownRenderer forwards exactly content and className, and MarkdownImplProps accepts only those two. A repo-wide grep for schema.sanitize over packages/ and apps/ returns nothing, against a control of 20 .tsx files reading schema.content in the same query shape, so the zero is a reading, not a blind query. An author writing sanitize: false believed they turned XSS filtering off; one writing sanitize: true believed they turned it on. Neither was true.

.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/7287-resolve-title-field-shared-ladder.md

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

    def.primaryField — a DetailViewSchema key (@object-ui/types views.ts), read off an OBJECT def and ranked ABOVE the canonical nameField ADR-0079 Phase 2 made the pointer (AGENTS.md Commandment #0.1). No producer can put it there: @objectstack/spec's object schema is a strictObject that answers unrecognized_keys: ['primaryField'], and ObjectSchema.create() throws — which is why objectstack#6326 deleted the identical read from two lint rules. A census across both repos found zero object payloads carrying it (the only writers are three test fixtures), and primaryField appears in zero files of the shipped @objectstack/spec@17.2.0 dist against 68 for nameField. Same shape as the undeclared objectDef.titleField read objectui#6531 measured and Five more consumer-side reads of the undeclared object-level titleField survive #6531 — in ObjectView view-config fallbacks and a useRecordSearch memo key #6557 removed. DetailViewSchema.primaryField is untouched and still honoured by DetailView's own header — it is a view key, and on a view it is legitimate.

.changeset/7295-chat-message-avatar-keys.md

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

    packages/plugin-chatbot/src/index.tsx:173–178 reads message.avatar || userAvatarUrl and message.avatarFallback || userAvatarFallback (and the assistant twins), the authoring-to-runtime seam spreads every unlisted key through (chatMessageAdapter.ts, ...passthrough), and the SDUI renderer feeds the authored messages[] straight in — a per-message avatar override renders, is documented, and no authoring-facing type declared it. ChatMessage in packages/types/src/complex.ts has no index signature (objectui#5155, deliberately — none is added here), so an author annotating ChatbotSchema.messages was told a value that renders is an error (TS2353); the zod mirror ChatMessageSchema is a plain strip-mode z.object, so the value parsed green and was silently DROPPED from the parsed output.

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

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

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

  • 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/7586-primary-field-consumer-reads.md

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

    primaryField is a DetailViewSchema key (@object-ui/types views.ts) — a view key, which DetailView.resolveDisplayTitle reads off schema and is welcome to. Read off an object def it is undeclared: @objectstack/spec's object schema is a strictObject answering unrecognized_keys: ['primaryField'], and ObjectSchema.create() throws. primaryField appears in zero files of the shipped @objectstack/spec@17.2.0 dist, against 68 for the canonical nameField. objectstack#6326 removed the identical read from two lint rules; objectui#7287 / PR fix(plugin-detail): resolveTitleField delegates to the shared ADR-0079 ladder #7585 removed it from resolveTitleField. These two survived it — and three of this repo's own changelogs already called the probe "not a spec property — always undefined" while the code kept honouring it.

.changeset/7632-shared-record-source-config.md

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

    That ladder is published contract on both faces — packages/types/src/objectql.ts and its zod mirror both ship .describe() strings naming getDataConfig's order (77cb489b4, maintainer ruling 2026-09-02), pinned by objectql-record-source-refinement-6939.test.ts — and it was hand-copied into five plugin components with no gate holding them together. A change to the ruled order had five edit sites and nothing that noticed a missed one; that is the AGENTS.md #0.1 drift class.

.changeset/7655-chatbot-registration-authoring-faces.md

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

    New published symbol: ChatbotSharedKey, the string-literal union of the twenty keys all three registrations read. It is exported from complex.ts because an exported interface may not extend a Pick over a private name (TS4022), so it is emitted into dist/complex.d.ts and is reachable through the published @object-ui/types/complex subpath (it is not re-exported from the package entry). It is a census, not an authoring face.

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

    The new aliasKeyRefusal() helper (zod/tombstone.zod.ts, internal — not re-exported) reuses retirementTombstone's primitive, z.never({ error }).optional().describe(), deliberately not handlerKeyRefusal's z.custom: measured, z.toJSONSchema throws on a z.custom arm ("Custom types cannot be represented in JSON Schema") and represents a z.never arm as { not: {} } with its description. z.toJSONSchema(ChartDataSeriesSchema) succeeded before this change and still does — it now lists chartType as a refused property carrying the guidance, 11 properties to 12.

.changeset/7697-combobox-option-root-barrel.md

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

    Additive only. ComboboxOption is added to the root barrel's existing named re-export list from ./form.js, next to the sibling option types that were already there (SelectOption, RadioOption). Nothing is removed, retyped or narrowed: the declaration stays in src/form.ts, its three members (value, label, disabled?) are unchanged, and the @object-ui/types/form subpath spelling keeps working exactly as before. Both spellings now resolve to the same declaration.

.changeset/7703-chatbot-dark-keys-retired.md

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

  • 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 form.ts → packages/types/src/form.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 data-display.ts → packages/types/src/data-display.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/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 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/7804-objectql-handler-key-arms.md

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

    The four plain objectql.ts node faces declare the nine handler keys their registered renderers read (the objectql.ts slice): ObjectFormSchema.onCancel / .onError / .onOpenChange / .onStepChange / .onSuccess, ObjectGallerySchema.onCardClick / .onRowClick, ObjectGridSchema.onNavigate and ObjectViewSchema.onNavigate.

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

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

    'runtime-slot' and not 'retired', measured at this key's own channel. 'retired' publishes "no renderer reads this key, so nothing could ever run it" — true of the two siblings already tombstoned on this arm (onSelectChange, onExpandChange) and flatly false here, since the read is live and INVOKED. ⚠️ No in-repo host builds a tree-view node carrying the key: the channel is wired end to end and only the supplier is absent, which is the same shape as ObjectFormSchema.onStepChange in this card's objectql.ts slice and is not evidence of a dead read. The TypeScript declaration is unchanged and still callable, so a programmatic host supplies it exactly as before.

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

.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/7963-alert-dialog-footer-keys-retired.md

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

    Nothing else moves. These spellings are overloaded across the tree and every other owner is a live key on a different declaration — FormSchema.cancelLabel, objectql.ts's confirmLabel, plugin-designer's ConfirmDialog React props, plugin-grid's def.confirmLabel, and plugin-form's ModalForm / DrawerForm, which build a local cancelLabel from schema.cancelText. None is an AlertDialogSchema; none is touched, and a pin asserts it. No fixture, catalog schema, example app or doc fence authored any of the three on an alert-dialog node, so no shipped document is stranded.

.changeset/8072-input-wrapper-class-mirrored.md

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

    packages/components/src/renderers/form/input.tsx reads cn("grid w-full items-center gap-1.5", schema.wrapperClass) onto the wrapper div around the input and its label. The TypeScript face has declared the key all along (form.ts, docblock "Input wrapper CSS class"); the zod mirror never did, so the value rode through .passthrough() and { type: 'input', wrapperClass: 42 } validated GREEN — while the identical document on any of the other eight schema.wrapperClass readers (checkbox, file-upload, filter-builder — objectui#6150 / b74a8598d; switch, textarea, date-picker, select, list — objectui#7722) was refused at the key.

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

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

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

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

.changeset/8415-filter-builder-condition-id.md

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

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

.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/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/8767-object-grid-refuses-string-sort.md

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

    Migration. Write the array: sort: [{ field: 'name', order: 'desc' }]. Both keys are required. SortConfig.order carries no ? in @object-ui/types (packages/types/src/objectql.ts) and no .optional() in its zod mirror, and the protocol's own reusable SortItemSchema requires order as well — measured: that schema refuses [{ field: 'name' }] with invalid_value at 0.order. Do not omit it: this block's array arm interpolates whatever is present, so an omitted order lowers to $orderby: 'name undefined' today. That is pre-existing behaviour on the arm this change does not touch, and it is filed as a successor card rather than widened into here.

.changeset/8800-retire-ai-insights.md

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

    The retirement's tombstones are the comment standing where the interface was in ai.ts and the comment standing where the barrel re-export was in index.ts; the executable half, including the tree-wide census with its controls, is packages/types/src/__tests__/ai-insights-retired-8800.test.ts. The objectui#8178 exclusion pin that deliberately held AIInsightsSchema.objectName out of that retirement was rewritten by this ruling, which is the only thing that was ever allowed to rewrite it.

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

  • names packages/types/src/objectql.ts → packages/types/src/objectql.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/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/8802-8257-8008-kanban-gantt-family-retirement.md

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

    What each retirement was, measured. Three of the four were registration-only: no schema face in @object-ui/types ever declared kanban-ui, kanban-enhanced or gantt as a component node type, so unregistering is the whole retirement. The bare kanban key was the exception — it had a declared arm on both faces (KanbanSchema in complex.ts and its Zod mirror), and a plain deletion there would have been the objectui#7664 failure: BaseSchema is .passthrough(), so a document naming a dropped key validates green and renders nothing. It therefore retires as a named refusal: the Zod union keeps an arm claiming the literal and answers a { "type": "kanban" } document with a message naming object-kanban as the remedy, while the TypeScript half is the absence of the arm from ComplexSchema and of the key from SchemaRegistry, so tsc refuses it at the authoring site.

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

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

    Why ADR-0049 and not a fresh ruling. 12b599219 refused actions on this same node and, by its own comments, ruled on that key ONLY — its ruling is not borrowed here. What reaches this key is the standing enforce-or-remove gate, which this repository applies to this exact face: packages/types/src/zod/tombstone.zod.ts's retirementTombstone is documented as the "ADR-0049 RETIREMENT TOMBSTONE" helper and is internal to these zod modules, 63 changesets cite the ADR, and PageNodeSchema already carried one of its refusal arms one member up. 12b599219 left this key parsing on purpose so that retiring it would be a decision rather than an accident, and wrote a pin saying so; that pin is flipped, not deleted.

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

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

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

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

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

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

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

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

  • 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 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/9092-inline-locale-declared-face.md

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

    • AppComponentSchema.label (app.ts) - ObjectGridSchema.label and .description (objectql.ts) - PageNodeSchema.aria.ariaLabel (layout.ts)
  • names layout.ts → packages/types/src/layout.ts — edited by this change

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

.changeset/9256-list-timeline-content-channels.md

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

    These two were held out of the previous family-D slice for a SERIAL constraint on packages/types/src/data-display.ts and never for a verdict. Readership was re-derived for both rather than inherited: a TypeScript compiler-API sweep files every .body / .children read under the declared type of its receiver and answers zero for ListSchema and TimelineSchema while its live controls fire. timeline's bare-key owner is any-typed, so it was attributed directly as well — packages/plugin-timeline contains no channel read of any kind.

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

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

  • 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/9406-types-root-barrel-two-names.md

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

    form-barrel-mirror-9406.test.ts keeps it closed, and it is DERIVED rather than a pair of presence assertions: it reads form.ts's export list and the root barrel's ./form.js re-export list on every run and names whatever is in the first and not the second. A pin asserting "these two names are present" would pass on the day the next declaration lands in form.ts and is forgotten, which is this class reopening yet again. Names deliberately left off the list get a ledger row carrying the reason instead, and a row goes red once its name reaches the barrel or stops being declared.

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

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

.changeset/9526-types-root-barrel-command-element-types.md

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

    The change is purely additive: the two names join the root barrel's existing named re-export list from ./form.js, beside CommandSchema. Nothing is removed, renamed or narrowed, the declarations stay in form.ts, and the /form subpath keeps working. It is the third instance of one class, repaired by the same route each time: objectui#7697 for ComboboxOption, and objectui#9406 (director decision batch 133, item 2, letter (a)) for InputShorthandSchema and UiCalendarSchema.

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

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

  • 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/9550-object-tree-root-barrel.md

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

    ObjectQLComponentSchema declares the node types an ObjectQL block may be. Every one of its arms was a named export of this package's root barrel except ObjectTreeSchema, which was declared in objectql.ts, applied by the union, and re-exported by the ./zod barrel (objectui#7917) — while no TypeScript consumer could name it. There is no ./objectql subpath to reach around the barrel: the package's exports map is pinned by packages/types/src/__tests__/package-exports-manifest.test.ts, and the root barrel was the only route to this type.

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

  • names objectql.ts → packages/types/src/objectql.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/9628-kanban-column-collapsed-honoured.md

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

    The key was declared on both published faces of the object-kanban arm — the lane element of ObjectKanbanSchema (objectql.ts and its Zod mirror) and the runtime lane KanbanColumn (complex.ts and its mirror) — and read by KanbanEnhanced alone, a module no production source imports. An authored { "id": "todo", "title": "To Do", "collapsed": true } therefore parsed green on both faces and reached a board that did nothing with it: KanbanImpl's only collapse is the SWIMLANE row's, held in viewer state under objectui:kanban-collapsed:ANGLE-BRACKETS(swimlaneField) and never keyed to a lane's declared value. That is the ADR-0049 declared-but-unhonoured shape.

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

    The key was declared on both published faces of the object-kanban arm — the lane element of ObjectKanbanSchema (objectql.ts and its Zod mirror) and the runtime lane KanbanColumn (complex.ts and its mirror) — and read by KanbanEnhanced alone, a module no production source imports. An authored { "id": "todo", "title": "To Do", "collapsed": true } therefore parsed green on both faces and reached a board that did nothing with it: KanbanImpl's only collapse is the SWIMLANE row's, held in viewer state under objectui:kanban-collapsed:ANGLE-BRACKETS(swimlaneField) and never keyed to a lane's declared value. That is the ADR-0049 declared-but-unhonoured shape.

.changeset/9710-overlay-trigger-bare-string.md

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

    ⛔ No published FACE moves — and one published FILE does. The accept set is the one both faces already shipped, and this is the implementation catching up to it — the direction objectui#7105 ruled for node slots, which relax the renderer rather than narrow the declaration. ⛔ No type, no accept set, no export and no runtime behaviour moves in @object-ui/types: every changed line in packages/types/src/overlay.ts is a docblock line. What moves there is documentation that SHIPS. The eight trigger docblocks named renderChildren(schema.trigger) inside each Radix trigger, and the renderers now spell that read renderTriggerSlot(XTrigger, schema.trigger) — so the JSDoc an author's editor shows described a call that no longer exists. Those eight now name the current spelling, which the emitted dist/overlay.d.ts carries and the old spelling no longer appears in; and the line addresses they carried are gone, because packages/components moves lines, nothing re-derived those numbers, and shipped documentation should not rot with them. That correction is why @object-ui/types takes a patch here.

.changeset/calendar-readme-schema-keys-5045.md

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

    README.md's "Schema API / CalendarView" block described a CalendarViewSchema that does not exist. Measured against the interface itself (packages/types/src/complex.ts) and its zod mirror: events — the schema's only required key besides type — was published as events?, so a reader following the README omits it and TypeScript rejects the node; defaultDate was string where the schema says string | Date; and onDateClick was listed as a schema key when it is a CalendarViewProps component prop, sending readers to a different package's surface for a key calendar-view does not have (the schema's key is onDateChange). The block also listed 6 of the schema's 13 keys with nothing saying it was a summary (objectui#5045).

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

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

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

.changeset/table-renderer-declared-column-contract-5350.md

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

    renderers/complex/table.tsx resolved a heading as col.header || col.label and a cell as row[col.accessorKey || col.name]. Neither label nor name is declared on TableColumn, which declares header and accessorKey — both required (packages/types/src/data-display.ts). This was the fourth site of the column-alias family, after data-table, ObjectDataTable and ObjectGrid (objectui#5350).

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

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

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

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

Compared the checked-out tree with 06a96e948 (merge-base with origin/main): 26 file(s) changed outside .changeset/, read against 1686 pending declaration(s) that publish a body (2286 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3102.9 KB 3104.5 KB
Main entry chunk (gzip) 149.4 KB 350 KB
Entry file index-WBBmFoQf.js —
Status PASS —

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


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.58KB 6.17KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
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.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.70KB 2.23KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
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.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 558.56KB 133.87KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.61KB 63.16KB
fields (index.js) 261.01KB 66.28KB
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.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
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.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.96KB 14.83KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 197.72KB 46.92KB
plugin-dashboard (index.js) 137.82KB 36.70KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 233.51KB 61.80KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 161.21KB 41.41KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.15KB 9.14KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 89.15KB 22.35KB
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) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
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) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
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) 4.74KB 2.26KB
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) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.27KB 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: 5833df79f8a392a5ff713e7308ee504509450680
Local-runs: none

Inputs read: card #10928 (body and all three comments: triage 5867971242, claim 5868015311, os-dev-report 5868440277), PR #10956 (body, the 27-file list, and git diff origin/main... of the head against main 06a96e948, merge-base 5c94589f0), the head's check-runs, and read-only git show / git grep / git archive on the fetched refs to check claims against code. Nothing built, run or re-run. main moved two commits since the base (dd0d78f74, 06a96e948); neither touches packages/types or packages/sdui-parser.

① Derived judgments

Accept set: no change. Public surface: wording only. Each item named, right or wrong:

  1. neitherContentChannelGuidance(node, route, renders) in zod/tombstone.zod.ts: signature and every schema combinator unchanged; the returned string differs only in the clause. 13 consts feed it (26 arms), verified by listing the call sites. Right.
  2. ai.zod.ts's module-private neitherChannel (6 arms) and the 131 literal retirementTombstone(...) strings across the 12 *.zod.ts files: the refusal message and its .describe() text are one string, so both faces move together; the issue stays invalid_type at the key's own path. My own git diff -U0 filter over the zod face finds 0 changed lines that are not string-literal lines. Right.
  3. Verdict identity: the PR body's 777-entry probe (base vs head: same success flags, codes and paths; the head payload with the old clause substituted back is byte-identical to base) is what the diff shape in items 1 and 2 predicts. Not re-run here. Right.
  4. Emitted .d.ts docblocks: 149 body?: never / children?: never JSDoc sites in 12 packages/types/src/*.ts files, shipped because the package emits declarations. git diff -U0 over the TS face finds 0 changed lines that are not JSDoc continuation lines. Per the contract-review reference, .d.ts bytes are shipped bytes, not the published accept set, so this does not lift Clause-② to yes. Right.
  5. The new pin packages/types/src/__tests__/content-channel-parser-tier-clause-10928.test.ts sits under a directory the package tsconfig.json excludes (**/__tests__/**), so it is not published; it imports only zod, vitest and ../zod/* sources, so check:phantom-deps holds with no new dependency. Not a public-surface change. Right.
  6. Census, re-derived here with the PR body's joined perl census over git archive of both refs: base zod old=141 new=0, head zod old=8 new=133; base TS old=149 new=0, head TS old=0 new=149; control NEITHER content channel 137/137 and 162/162; absent token 0 in every cell. Matches the PR body cell for cell. The card's 126/139 were a line-bound grep; the joined count is the right one. Right.
  7. Truth of the new clause against the parser on this head: packages/sdui-parser/src/validate.ts pushes not-a-container when node.children?.length && !acceptsChildren(comp); body-dialect.ts's checkRetiredBodyDialect answers a body child list or node under a registration with no children input with not-a-container, and a scalar with unknown-prop. So the clause is true on both members for an authored content value (a child list or a node), which is the sentence's subject. Right, with two residuals for the seat, neither a defect the card named and neither in the prescribed wording's scope: (a) a scalar body draws unknown-prop (the PR's Acceptance notes say so); (b) an EMPTY children: [] draws no parser warning at all, because ?.length is 0, so "only ... noticed it" overstates for that degenerate value.
  8. The 8 old-clause sites left on the head are exactly HeaderBarSchema's non-channel keys title, logo, nav, left, center, right, sticky, height in zod/navigation.zod.ts (with variant's "no class" sibling). The header-bar registration in packages/components/src/renderers/navigation/header-bar.tsx declares inputs crumbs, search, actions, rightContent only, so those keys draw unknown-prop, not not-a-container; the settled clause would have been a second false statement there. Leaving them is right. HeaderBarSchema's own body / children tombstones did take the new clause.
  9. Repo-wide on the head, the old clause survives outside packages/types only in the new changeset (quoting what it replaced) and inside the new pin (naming the retired string). No doc, skill or site page carried it, so nothing in scope was left out. Right.
  10. The pin: the arm tests read each refusal through safeParse and assert code invalid_type at the key's path first and the clause substring second, one arm per message source (literal accordion, shared builder object-grid, local builder ai-form-assist); the population walk asserts non-vacuity by NAME (three schemas by two keys), not by count. Sound. Residual: the walk reaches only z.ZodObject values re-exported by name from zod/index.zod.ts, so a family-D member wrapped in a union or effects, or not re-exported, would be skipped silently; the census in item 6 shows zero such misses today.
  11. Cite-by-content: the pin names the parser and registry pins by file name, and all three exist on the head (containment-declared-slot-9910.test.ts and body-dialect-6771.test.ts in packages/sdui-parser, container-declaration-ratchet.test.tsx in packages/components); no cross-file line address anywhere in the diff. Line Citation Gate is success. Right.

② Semver level

③ Boundary flags

Deviations (6), each answered:

  1. Census target "both counts to zero" not met; 8 HeaderBarSchema non-channel strings kept. Right (①8); routed to objectui#10959.
  2. Dispatch assumption "a body alone stays silent" falsified. Confirmed on the head in body-dialect.ts: a body child list draws not-a-container through the same acceptsChildren predicate. The dev's correction is right, and the clause holds for both members.
  3. Counts 126/139 line-bound, joined 141/149, in scope 133/149. Re-derived identically here (①6). Right.
  4. Pin holds the prose half only. Accepted. @object-ui/types declares no workspace dependency (its deps are @objectstack/spec and zod), so a parser import would be a phantom dependency, and a components-side pin would breach the claimed file surface. The parser half is held by the three named pins, all present on the head (①11).
  5. Rule tension: "prompts, rulings and error wording are not pinned unless a consumer parses the literal" versus triage note 3 "pin the builder sentence plus one arm". The companion rule governs: where the wording itself is the contract, assert the message ON TOP of code and status, never instead. This card is a class-b wording defect, and the arm tests assert code and path first. Resolved right.
  6. Hand re-wrapping of the shared builder's three lines and one form.ts docblock. Verified in the diff: the string value changes only in the clause; 0 non-string and 0 non-JSDoc lines moved (①2, ①4). Right.

Open question: add a live-registry behavioural pin in packages/components (A: follow-up card; B: no)? Answer: B for this landing. The ratchet (declared slot if and only if rendered list) plus the two parser pins already make a reads-neither registration that declares children go red, and the parser behaviour is pinned where the parser lives. A single-file tie is a nice-to-have, not a gap; if the seat wants it, it is a follow-up card, never a blocker on this PR.

Out-of-scope findings (2), filed as one card objectui#10959. Verified: open, label finding, names both sites, the unknown-prop diagnostic, @object-ui/types: patch, Clause-②: no, and the serial constraint after #10956 (it shares navigation.zod.ts, layout.ts, form.ts). Routing right: same family, different clause; a wording naming unknown-prop is needed, so it could not ride this sweep without a new false statement. The card's citations (title to height from objectui#10387, variant from objectui#10286) match the strings on the head. Escalation for the seat: add the two residuals from ①7 (scalar body, empty children) as a note on #10959 or #9256 so they are not lost; not a condition of this verdict.

Check-runs on the head, read at 2026-09-28T11:04Z right before this record was written: 43 total, all completed; 40 success, 3 skipped by design (the two coverage lanes and dependabot), 0 failures, 0 in progress. Type Check, all eight test shards and their Test aggregate, Lint, Build & E2E, Test (dist pins), Spec Main Shape Gate, README Export Check, Governed Surface Queue Guard, Line Citation Gate and every changeset gate are success. These conclusions are the gate verdicts; this record judges the contract.

Implemented-by: claude/issue-10928-not-a-container-clause
Reviewed-by: session_012UwY3ahMixEFkfTUxMVkYm

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 28, 2026 11:08
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 28, 2026
Merged via the queue into main with commit 95a7c8d Sep 28, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10928-not-a-container-clause branch September 28, 2026 11:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants