Skip to content

docs(types): the content-channel tombstones state the BaseSchema premise in the tense it is true (objectui#9933) - #10417

Merged
os-elon-musk merged 3 commits into
mainfrom
claude/issue-9933-stale-baseschema-premise-docblocks
Sep 25, 2026
Merged

os-elon-musk merged 3 commits into
mainfrom
claude/issue-9933-stale-baseschema-premise-docblocks

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #9933
Clause-②: no

Clause-②: no — JSDoc text only: every input's accept/reject verdict is byte-identical before and after, and no published declaration, member or export changes.

Implemented by the os-dev dispatched from domain:ui seat 2, session https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN (mode:subagent). Base a707197, head f30b2c8.

What changed

JSDoc comment text only, in packages/types/src/{ai,complex,data-display,disclosure,feedback,form,layout,navigation,objectql,overlay,reports,views}.ts, plus .changeset/9933-stale-baseschema-premise-docblocks.md ('@object-ui/types': patch). Every changed diff line in the 12 source files is a JSDoc continuation line (non-comment changed lines: 0), so no declaration, member, type or export moves.

The stale premise came in two shapes. Each shape is byte-identical across its sites. Each was rewritten by an exact-match replacement that refused to write unless the per-file match count equalled the census count:

shape sites members docblock header
A — "body and children are inherited-and-optional from {@link BaseSchema}, whose own docblock admits …" 137 67 body?: never, 70 children?: never REFUSED BY NAME (objectui#9256) — reads NEITHER content channel
B — "body is inherited-and-optional from {@link BaseSchema}, whose own docblock admits …", with a line break between "whose own" and "docblock admits" 9 body?: never on box, span, container, flex, stack, grid, scroll-area (layout.ts), form, toggle (form.ts) REFUSED BY NAME (objectui#8284) — reads children

Shape A now reads:

Before objectui#9256 tombstoned them here, body and children were both inherited-and-optional from {@link BaseSchema} — so authoring either here type-checked, parsed green through .passthrough(), and rendered NOTHING: no error, no warning, no element. objectui#6771 has since retired body on BaseSchema itself; BaseSchema still declares children, so this node's own tombstone is what refuses it here. SchemaRenderer strips both keys out of the props bag it spreads, so neither reaches the component by another route either.

Shape B now reads:

Before objectui#8284 tombstoned it here, body was inherited-and-optional from {@link BaseSchema} — so authoring it here type-checked, parsed green through .passthrough(), and rendered an EMPTY element with no error and no warning. Per component, the channel a renderer does not read is now tombstoned on both published faces (maintainer ruling, summon #17 decision batch #2, 2026-09-07), and objectui#6771 has since retired body on BaseSchema itself.

Why this wording:

  • Each statement stays inline on its own member. This follows the reasoning of the objectui#9930 ruling C: no central block to cite, and no extra jump for a declarations reader.
  • The historical tail stays, written as history.
  • The quote of BaseSchema's former docblock is dropped rather than moved into the past tense. BaseSchema.children's own docblock already records it ("This docblock used to read …").
  • Each present-tense clause was checked on the base:
    • BaseSchema.body?: never carries @deprecated Retired spelling of children.
    • BaseSchema.children is still declared as SchemaNode | SchemaNode[].
    • SchemaRenderer's metadata destructuring still strips children and body before it spreads the props.
    • All 137 shape-A headers name objectui#9256, and all 9 shape-B headers name objectui#8284.
    • Those tombstones predate PR objectui#9895 (objectui#6771), merged 2026-09-19T00:59Z, because the card counted them on that PR's head.

Measurements — base a707197 vs head f30b2c8

instrument pathspec base head
the card's line-bound git grep -c "whose own docblock admits" packages/types/src 137 hits, 12 files 0 (exit 1)
comment-joined "whose own docblock admits" (command 1) packages/types/src 146 0
comment-joined present tense "(are or is) inherited-and-optional from {@link BaseSchema}" (command 1) packages/types/src 146 0
comment-joined "whose own docblock admits" (command 2) whole tree 146 0
comment-joined quote "some components use children instead of" (command 2) whole tree 149 3, all out of surface, see Acceptance notes
firing control git grep -c "REFUSED BY NAME" packages/types/src 158 hits, 18 files 158 hits, 18 files
absent-token control qqzz_absent_token_9999 packages/types/src 0 0
new text "has since retired body on BaseSchema itself;" / "… itself." (command 1) packages/types/src 0 / 0 137 / 9
the same new text in emitted packages/types/dist/*.d.ts after the build dist — 137 / 9

⚠️ The card's 137 comes from a line-bound grep. The same premise also sits on 9 more sites (shape B) where "whose own" and "docblock admits" fall on different lines, so a single-line pattern cannot match them. All 9 are in form.ts and layout.ts, which are inside the claimed file surface, so this PR rewrites all 146.

Command 1 (REF = a707197 or f30b2c8). The enumeration and the read come from the same ref:

git archive REF packages/types/src | tar -xO | perl -0777 -ne 's/\n[ \t]*\*[ \t]?/ /g; my $a = () = /whose\s+own\s+docblock\s+admits/g; my $p = () = /\b(?:are|is)\s+inherited-and-optional\s+from\s+\{\@link\s+BaseSchema\}/g; my $c = () = /REFUSED\s+BY\s+NAME/g; my $z = () = /qqzz_absent_token_9999/g; my $na = () = /has\s+since\s+retired\s+`body`\s+on\s+`BaseSchema`\s+itself;/g; my $nb = () = /has\s+since\s+retired\s+`body`\s+on\s+`BaseSchema`\s+itself\./g; print "stale=$a present-tense=$p control=$c absent=$z newA=$na newB=$nb\n"'

Printed at a707197: stale=146 present-tense=146 control=158 absent=0 newA=0 newB=0. Printed at f30b2c8: stale=0 present-tense=0 control=158 absent=0 newA=137 newB=9.

Command 2 (whole tree):

git archive REF | tar -xO 2>/dev/null | perl -0777 -ne 's/\n[ \t]*(?:\*|\/\/|>|#)?[ \t]?/ /g; my $a = () = /whose\s+own\s+docblock\s+admits/g; my $q = () = /some\s+components\s+use\s+[`\x27]children[`\x27]\s+instead\s+of/g; print "stale=$a quoted-sentence=$q\n"'

Emitted declarations, from packages/types/dist/ai.d.ts after pnpm --filter '@object-ui/types^...' --filter @object-ui/types build. The build ran on 54612db, whose source is byte-identical to f30b2c8: the only later change is the changeset.

     * Before objectui#9256 tombstoned them here, `body` and `children` were both
     * inherited-and-optional from {@link BaseSchema} — so authoring either here
     * type-checked, parsed green through `.passthrough()`, and rendered NOTHING:
     * no error, no warning, no element. objectui#6771 has since retired `body` on
     * `BaseSchema` itself; `BaseSchema` still declares `children`, so this node's
     * own tombstone is what refuses it here. `SchemaRenderer` strips both keys
     * out of the props bag it spreads, so neither reaches the component by
     * another route either.

Local verification at f30b2c8

  • pnpm --filter '@object-ui/types^...' --filter @object-ui/types build (under the shared lock): VERDICT command-exit 0, ✓ dist completeness: 1 package(s) complete (130 emitted files verified).
  • pnpm --filter @object-ui/types type-check (tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json), then pnpm exec vitest run --maxWorkers=2 packages/types/ scripts/__tests__/changeset-polarity-census.test.ts scripts/__tests__/check-changeset-no-major.test.ts scripts/__tests__/check-changeset-presence.test.ts from the repo root: Test Files 232 passed (232), Tests 5249 passed (5249), VERDICT command-exit 0.
  • node scripts/check-changeset-presence.mjs: ✅ 12 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/9933-stale-baseschema-premise-docblocks.md. Without the changeset it read ❌ 12 source file(s) of 1 released package(s) changed, and this change adds no changeset. The bump is a patch and not an empty frontmatter, because of the gate header's own section "Comment text in src DOES reach a published .d.ts": the emitted .d.ts text moved, as the dist counts above show.
  • node scripts/check-changeset-claims.mjs: exit 0 (report-only). Self-contradiction reading: ✅ Every package declared across those 1 body(ies) is either not negated in its own prose. The went-false reading lists 56 pending changesets that name a file this change touches. I re-read the ones about content channels (see the Acceptance notes).
  • node scripts/check-changeset-overwrite.mjs: ✅ No pre-existing changeset was modified or deleted.
  • node scripts/check-changeset-no-major.mjs: ✅ No changeset declares a major bump.
  • node scripts/check-control-bytes.mjs: ✅ check-control-bytes: OK (scanned 8472 tracked text file(s); skipped 85 binary). Also self-scanned the changed files for control bytes: grep exit 1, no match.
  • node scripts/check-new-cross-file-line-citations.mjs: VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0.
  • node scripts/check-pending-changeset-literals.mjs: ✅ No test source names a pending changeset.
  • node scripts/check-doc-component-types.mjs: ✅ Every documented component type is registered.
  • node scripts/check-comment-mask-corpus.mjs (on df2d696, same source): 1 disagree … within the residue objectui#7882 is holding open, exit 0.
  • node scripts/check-governed-queue-guard.mjs --test over the changed paths: ✅ NOT GOVERNED — 13 path(s) checked against 5 governed surface(s); none matched.
  • Lint was narrowed to the 12 changed files. This is a measurement, not a skipped run, because of three pieces of evidence:
    1. Population: eslint.config.js applies its **/*.{ts,tsx} blocks to these files. pnpm exec eslint --format json over the 12 files returned 12 result entries and no ignored-file notices.
    2. Result: 12 files, 0 errors, 100 warnings, all @typescript-eslint/no-explicit-any on code lines.
    3. Invariance: eslint.config.js has no projectService, parserOptions.project or *TypeChecked preset (grep count 0), so linting is not type-aware. A comment-only diff therefore cannot move the result for any untouched file.
  • NOT MEASURED: repo-wide pnpm lint, the full pnpm test shards, and E2E. CI runs these.

Acceptance notes

Out-of-surface findings. These were measured but not edited. None is class a, b or c, so no card is filed.

  • The former BaseSchema quote still appears in two places:

    • .changeset/8284-content-channel-per-component.md, as "BaseSchema declares two optional content channels and its own docblock admits that …". This changeset is pending, so it will publish into the CHANGELOG verbatim, in the same release as objectui#6771's changeset.
    • the header of packages/types/src/__tests__/content-channel-per-component-8284.test.ts, under "The defect this pins closed".

    Both are the defect as it stood when those files were written. The third quote hit, in packages/types/src/base.ts, is already in the past tense ("This docblock used to read …"). Carrier: none.

  • .changeset/9256-content-channel-family-d.md, in its AMENDED paragraph, says "The two twins are unchanged and go on inheriting body as the content slot". That became false with objectui#6771. The family-D test file records that each chatbot twin now refuses body with the NEITHER-channel tombstone, pointing at requestBody. The changeset is pending and will publish verbatim. Carrier: none.

  • The runtime refusal messages of shape B's zod twins say "body is inherited from BaseSchema, so an authored body parsed green here and rendered an EMPTY element". There are 2 in packages/types/src/zod/form.zod.ts and 7 in packages/types/src/zod/layout.zod.ts, one per aliasKeyRefusal('body', 'children', …) of the same nine components. They ship in dist/zod/index.zod.js and are shown to authors. The remedy they name (children) is correct; only the premise is out of date. They are not edited because this dispatch excludes packages/types/src/zod/: objectui#7715 is in flight there. Carrier: none — objectui#7715's branch has no diff yet, so it is not known to touch these messages.

Guard: none added. A pin on the absence of the phrase would block only this wording, not the class, which is a premise about another declaration restated per member that goes stale when that declaration changes. After this change the tree holds no copy of the paragraph to paste from. So this PR adds no test pin and no CI gate.

Overlap: draft PR objectui#8941 edits the BulkActionDef.icon docblock in objectql.ts. This PR's objectql.ts hunks sit only in the ObjectGallerySchema and ObjectDataTableSchema content-channel tombstones, so the two PRs do not share a hunk. Nothing under packages/types/src/zod/ is touched.

Labels: this PR writes none. objectui path labels come from labeler.yml.


Generated by Claude Code

…ise in the tense it is true

146 per-component docblocks in packages/types/src justified their `body` /
`children` tombstone with "`body` and `children` are inherited-and-optional
from BaseSchema, whose own docblock admits ..." (137) or its single-key
sibling "`body` is inherited-and-optional from BaseSchema, whose own docblock
admits ..." (9, line-wrapped so a line-bound grep of the phrase misses them).
Since objectui#6771 `BaseSchema.body` is `never` and BaseSchema's docblock no
longer admits the two-spelling ambiguity, so the present-tense opening clause
was false while the rest of each paragraph was true as history.

Each paragraph now dates the inherited-and-optional state to before its own
tombstone card (objectui#9256 / objectui#8284) and states the present: the
base refuses `body` itself, and (for the neither-channel family) still
declares `children`, which this node's tombstone refuses. Comment text only;
no declaration, member, type or export moves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…s a @object-ui/types patch

The rewritten docblocks are on exported members and reach the emitted
.d.ts, so the package ships changed bytes (check-changeset-presence.mjs
header, "Comment text in src DOES reach a published .d.ts").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…s sentence

The first draft quoted the stale clause verbatim, so the tree-wide phrase
instrument used to measure this change kept firing on the changeset that
reports its removal. Paraphrased; the claim is unchanged.

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

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 56 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.

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

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

.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 objectui#5853 (.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. finding(types): TableColumn.type disagrees three ways — interface declares 8 literals, zod mirror accepts any string, renderer's live read set handles values outside the union via an as any cast #5853 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.

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

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

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

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

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

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

.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 (objectui#6939, 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/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.

.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 (objectui#6938), 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 (objectui#6938), FileUploadSchema and FilterBuilderSchema (objectui#6150); these five were left out only because their doc pages never listed it.

.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 (objectui#7804, 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/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 / finding(types): 4 more genuinely-read undeclared keys the #6150 census could not see, plus one declared-but-dead key — all on the same 8 renderers #6938; 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.

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

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

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

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

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

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

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

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

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

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

.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 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 2fc2a2439 (merge-base with origin/main): 12 file(s) changed outside .changeset/, read against 1368 pending declaration(s) that publish a body (1942 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3043.7 KB 3104.5 KB
Main entry chunk (gzip) 148.4 KB 350 KB
Entry file index-Cq2YZNfU.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.68KB 6.20KB
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.18KB 10.59KB
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.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
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) 26.08KB 7.56KB
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) 542.47KB 129.64KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 224.12KB 62.26KB
fields (index.js) 255.56KB 64.73KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
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 (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 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.39KB 3.10KB
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.44KB 14.62KB
plugin-charts (index.js) 72.54KB 20.26KB
plugin-chatbot (index.js) 198.27KB 47.18KB
plugin-dashboard (index.js) 133.45KB 35.33KB
plugin-designer (index.js) 216.12KB 44.37KB
plugin-detail (index.js) 260.94KB 67.93KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 146.34KB 37.47KB
plugin-gantt (index.js) 168.47KB 41.53KB
plugin-grid (index.js) 215.22KB 58.85KB
plugin-kanban (index.js) 49.30KB 15.39KB
plugin-list (index.js) 114.36KB 28.24KB
plugin-map (index.js) 22.05KB 7.14KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.72KB 8.96KB
plugin-tree (index.js) 10.74KB 3.76KB
plugin-view (index.js) 85.86KB 21.38KB
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.66KB 3.50KB
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) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
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) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
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.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
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.25KB
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) 0.20KB 0.18KB
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.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: f30b2c827a89c48188b54dd4c4f30fbf3b87a563

① Derived judgments

Accept/reject sets and published surface — no change; the JSDoc-only claim holds. git diff a707197 refs/review/pr-10417 -- 'packages/types/src/*.ts' has 1159 added and 876 deleted lines; every one of them matches ^[+-][ \t]*\* (JSDoc continuation) — my count of changed lines NOT matching that shape is 0. No declaration, member, type or export line moves. Clause-②: no is verified. The only published-surface change is emitted .d.ts comment bytes (JSDoc on exported interface members projects into declarations; the changeset-presence gate header records this under its objectui#5666 section). I did not build, so the PR's dist counts (137/9) are its reading, not mine.

Site counts, re-derived with my own continuation-aware parser over every packages/types/src/*.ts at base and head (scratch census.py):

  • Base a707197: shape A = 137 (67 on body?: never, 70 on children?: never), every one inside a docblock headed REFUSED BY NAME (objectui#9256; shape B = 9, all body?: never, all headed #8284, on ToggleSchema, FormSchema (form.ts) and BoxSchema, TextSpanSchema, ContainerSchema, FlexSchema, StackSchema, GridSchema, ScrollAreaSchema (layout.ts). Per-file split matches the PR table exactly (ai 6, complex 11, data-display 20, disclosure 6, feedback 16, form 26+2, layout 12+7, navigation 10, objectql 4, overlay 18, reports 2, views 6).
  • Head: newA = 137, newB = 9, same distribution; the card each new "Before objectui#NNNN tombstoned" sentence names equals its own docblock header at all 146 sites (0 mismatches).
  • Card instrument git grep -c "whose own docblock admits" REF -- packages/types/src: base 137 hits / 12 files → head 0. Firing control REFUSED BY NAME: 158 hits / 18 files at both base and head. Absent control qqzz_absent_token_9999: 0 at both.
  • uniq -c over added lines: exactly 8 distinct lines ×137 and 7 distinct lines ×9; over removed lines: 6 ×137 and 6 ×9 — both shapes are byte-identical across sites before and after, and the arithmetic (876 = 146×6; 1159 = 137×8 + 9×7) leaves no room for a stray edit.

Present-tense truth on the head tree (git show refs/review/pr-10417:packages/types/src/base.ts): body?: never sits under a RETIRED (objectui#6771, maintainer ruling 2026-09-01, ADR-0049) header with @deprecated Retired spelling of children — author children.; children?: SchemaNode | SchemaNode[] is still declared, its docblock says "since objectui#6771 the only one" and records the old admission only in the past tense ("This docblock used to read … some components use 'children' instead of 'body'"). packages/react/src/SchemaRenderer.tsx at head destructures children: _children, body: _body out of the metadata before the props spread, so "SchemaRenderer strips both keys out of the props bag it spreads" is true (and is pre-existing text, not new). "objectui#6771 has since retired body on BaseSchema itself" — true (#6771 closed 2026-09-19; PR #9895 merged 2026-09-19T00:59Z). "BaseSchema still declares children, so this node's own tombstone is what refuses it here" — true.

Historical clause verified, not inferred. I fetched the #9895 merge commit 2acd8e1 at depth 2. At its first parent 509f8ed (before the retirement) base.ts reads body?: SchemaNode | SchemaNode[] and children?: SchemaNode | SchemaNode[], and the comment-joined stale count is 149; the merge removed 3 (alert/badge/tooltip children tombstones converted to body?: never) and added 0 → 146; git diff 2acd8e1 a707197 adds 0 and removes 0 → 146 at base. So every one of the 146 tombstones existed while body was inherited-and-optional ⇒ "Before objectui#9256/#8284 tombstoned … was/were inherited-and-optional" is true at every site. ⚠️ The PR body's own justification ("Those tombstones predate PR #9895 … because the card counted them on that PR's head") is not a valid proof — a PR head contains the PR's own additions — but the conclusion is independently true.

Rewrites judged: shape A adds no new false statement; it drops the quotation of BaseSchema's former docblock rather than re-tensing it — that fact is still recorded in past tense on BaseSchema.children and at 3 out-of-surface sites (my whole-tree grep for "some components use" at head: .changeset/8284-content-channel-per-component.md, __tests__/content-channel-per-component-8284.test.ts header, base.ts — exactly the PR's 3). Shape B keeps the "maintainer ruling, summon #17 decision batch #2, 2026-09-07" sentence, which is pre-existing text re-wrapped. Cross-file path:line: 0 added lines match any file.ext:N, "line N" or #LN shape (grep over all + lines); Line Citation Gate success. Whole-tree present-tense (are|is) inherited-and-optional from at head: 1 hit, and it is the changeset describing the old premise. Other stale phrasings in the 12 files (inherited from BaseSchema / two content channels / declares both): only unrelated hits (disabled, bind/className, calendar "declares both keys"). No test or script reads the old paragraph (0 hits for its distinctive phrases in tests/scripts); the family-D pin asserts on zod mirror messages, not the TS docblock; the only bounded-distance docblock regex in types tests targets #7963.

② Semver level

'@object-ui/types': patch is right. No API change; emitted .d.ts bytes move for a published package, and scripts/check-changeset-presence.mjs's header ("If emitted .d.ts text moved, the package ships changed bytes and owes a changeset that says so") makes an empty frontmatter the wrong declaration, so patch is the minimal correct bump. @object-ui/types is in the single fixed group (40 packages) so the group bumps together — normal here. Changeset Declaration, Bump Policy, Fixed Group Check, Claim Re-read, Overwrite Report all success; the only pre-existing-changeset touches are none (my grep of the diff: only the new file). Changeset prose: the stale premise appears only as the thing being described as false; every present-tense claim ("BaseSchema.body is never", "docblock no longer admits the two-spelling ambiguity", "still declares children") verified on head; the shape-B member list matches my census (TextSpanSchema = span). One imprecision, CHANGELOG-bound: "Each paragraph now … states the present: BaseSchema refuses body itself and still declares children" is true of the 137 shape-A paragraphs, but the 9 shape-B paragraphs state only the retirement of body and do not say "still declares children". Overstatement about 9 of 146, not a false statement about the tree; not blocking.

③ Boundary flags

Implemented-by: claude/issue-9933-stale-baseschema-premise-docblocks
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS

@os-elon-musk
os-elon-musk marked this pull request as ready for review September 25, 2026 00:13
@os-elon-musk
os-elon-musk added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit ca5d671 Sep 25, 2026
45 checks passed
@os-elon-musk
os-elon-musk deleted the claude/issue-9933-stale-baseschema-premise-docblocks branch September 25, 2026 02:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants