Skip to content

feat(types,components)!: retire data-table selectionStyle and chatbot floatingConfig on both faces, file data-table's inline-edit flags by name (objectui#6152, round 5) - #11320

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-6152-unmirrored-round5
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-6152-unmirrored-round5

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Refs #6152
Clause-②: yes (narrowing). selectionStyle and the base-face floatingConfig retire on both faces, and editable / singleClickEdit leave the authored docs, under the startup no-gradualism rule. It is priced in the changeset.

Round 5 of objectui#6152, dispatched under claim 5923137359 (domain:devx seat 2, session session_01TdiauJaVCHuj45EzZGUxHh, on the maintainer's direct order). The rulings are the seat review 5921390451: its three answers to round 4's open questions. ⛔ This is a draft from a dispatched dev: do not mark it ready and do not enable auto-merge. The card stays open: ObjectGridSchema's keys and the spec-derived pairs (objectui#2231) are not this round's.

What changed

  1. DataTableSchema.editable / singleClickEdit: answer 1, B. Both keys are filed by name on RuntimeOnlyNamedAllowList, each with its reason, under round 4's class rule: a key a host produces in code and no document authors (the showAddRow precedent). No declaration moved. content/docs/components/complex/data-table.mdx no longer teaches them on an authored node. The interface block drops them, and the editable: true example becomes an "Inline editing" section. That section says the capability is host-paired: the table only stages an edit, and object-grid sets both keys on the table it builds and supplies the save path. It points a document author at object-grid. The two members' doc comments say the same.
  2. DataTableSchema.selectionStyle: answer 2, B. It is retired on both faces: a ?: never tombstone on data-display.ts, and a retirementTombstone() on zod/data-display.zod.ts whose message says to delete the key and that selectable alone turns selection on. The hover-only checkbox branch in packages/components/src/renderers/complex/data-table.tsx is dropped, so the 'always' behaviour is the only one.
  3. Base-face ChatbotSchema.floatingConfig: answer 3, A. It is retired on the chatbot face only: ?: never on complex.ts and a retirementTombstone() on zod/complex.zod.ts, whose message names type: "chatbot-floating".
    • Mechanism, from the measurement: a base tombstone only, with no re-declaration. ChatbotFloatingSchema does NOT inherit the base member. Its TS face is BaseSchema plus Pick of ChatbotSchema over ChatbotSharedKey and maxToolRoundtrips, plus its own floatingConfig member. Its zod twin is BaseSchema.extend with the shared pick spread and its own floatingConfig arm. So the floating registration's declaration, mirrored in round 4, was already separate on both faces and is untouched.
    • It is a tombstone and not a deletion because ChatbotSchema is a BaseSchema carrier, where a deleted member reads as any through the index signature. The discriminator is cited, not restated, so tombstone-discriminator-agreement-9684.test.ts still counts four statements.
    • The chatbot half of floating-chatbot-trigger-icon-retired.test.ts is flipped, not deleted. On a chatbot node it now asserts the refusal of the whole floatingConfig key on both faces, a live config included, with a no-floatingConfig control. chatbot-registration-authoring-faces-7655.test.ts's type pins follow.
  4. Ledger. zod-mirror-parity.test.ts: the UnmirroredDeclared, RuntimeOnlyDeclared and RuntimeOnlyNamedAllowList blocks, and every prose total reconciled to the parser count. One non-vacuity leg was reshaped; see the acceptance notes.
  5. Docs. content/docs/plugins/plugin-chatbot.mdx no longer says floatingConfig type-checks and parses on a chatbot node.
  6. Changeset. .changeset/6152-unmirrored-round5.md bumps @object-ui/types and @object-ui/components to minor, with the break stated. Three pending entries that this round makes false get dated, append-only **Correction, 2026-10-01** notes, with frontmatter and every prior line intact: 6152-unmirrored-round4.md, 7654-floating-chatbot-trigger-icon-tombstone.md and 7655-chatbot-registration-authoring-faces.md.

Per key

key node ruling route TS face zod face renderer docs
editable data-table B filed by name, runtime-only (host-paired) unchanged (boolean), doc comment notes the pairing unchanged (not mirrored) unchanged removed from the authored interface block; section rewritten
singleClickEdit data-table B filed by name, runtime-only (host-paired) unchanged (boolean), doc comment notes the pairing unchanged (not mirrored) unchanged as above
selectionStyle data-table B retired (ADR-0049) ?: never retirementTombstone() hover branch dropped none taught it
floatingConfig chatbot A retired (ADR-0049) ?: never retirementTombstone() none (never read) plugin-chatbot.mdx corrected
floatingConfig chatbot-floating (kept) none unchanged unchanged (round 4's arm) unchanged unchanged

Ledger totals, by TypeScript-parser count of zod-mirror-parity.test.ts

ledger (entries / keys) before, base 3d09948 after, head c1b5767
KnownDrift 49 / 86 49 / 86
UnmirroredDeclared 6 / 20 (3 / 14 spec-derived, 3 / 6 local) 5 / 16 (3 / 14 spec-derived, 2 / 2 local)
RuntimeOnlyDeclared 4 / 27 4 / 29
RuntimeOnlyNamedAllowList 3 / 20 3 / 22
WiderThanDeclared 3 / 3 3 / 3
MirroredUndeclared 2 / 2 2 / 2
shared entries / union of the two unmirrored ledgers 1 of 4 / 9 (6 + 3) 0 of 4 / 9 (5 + 4)

⚠️ The local half is 2 / 2, not the 0 / 0 the dispatch predicted (hypothesis H1 falsified). The two keys left are displayMode on ChatbotSchema and on ChatbotFloatingSchema. Under objectui#7654 maintainer ruling B, kept by round 4's Q3 B, their refusal stays TypeScript-only: stored designer documents carry displayMode: 'floating'. So they stay in UnmirroredDeclared by decision, not as mirroring debt. The ledger prose says so, and the non-vacuity pin "the LOCAL half read as EMPTY" still holds. After this round, UnmirroredDeclared holds only the spec-derived 3 / 14 (objectui#2231) and those two decided entries.

What was measured, before editing (base 3d09948)

  • Type-checker read and producer census. The round-4 instrument: the TS compiler API over every packages/*/src and apps/*/src, 5585 files.
    • ChatbotSchema.floatingConfig: 0 reads, 0 producers, 1 test producer (the tripwire).
    • ChatbotFloatingSchema.floatingConfig: 1 read (the floating registration).
    • DataTableSchema.selectionStyle: 1 read (the data-table destructure) and 0 producers.
    • editable / singleClickEdit: 1 read each (the destructure) and 1 producer each (ObjectGrid).
    • Lit controls: ChatbotSchema.messages 3 reads, maxHeight 2, DataTableSchema.columns 2 reads and 2 producers.
  • Authored census in this repository. 493 tracked JSON files, 354 Markdown JSON fences and 3202 TS/TSX files with a literal type.
    • data-table: selectionStyle 0, and editable / singleClickEdit 1 each, both ObjectGrid in code with no document.
    • chatbot: floatingConfig 1, a test.
    • Lit controls: data-table.columns 71, selectable 8, chatbot-floating.floatingConfig 4.
  • objectstack census. git grep -i on a self-named ref of objectstack origin/main (9b0de7de7): selectionStyle, selection_style and floatingConfig 0 hits. The lit control on the same ref was singleClickEdit, with 6 hits, all on the spec's ObjectGridProps, not data-table. There is no named user of hover-style selection, so the stop condition did not fire.
  • Runtime probe, a one-off. The real SchemaRenderer and registry, varying one key. Both files were deleted after each run, leaving git status and git diff HEAD empty.
    • At base, a selectable data-table with selectionStyle: 'hover' differed from unset: 6651 against 6481 characters. The checkbox cell gained relative, and each checkbox was wrapped in a transition-opacity opacity-0 group-hover/row:opacity-100 div. 'always' was byte-equal to unset, and the showRowNumbers control moved the DOM.
    • At head, 'hover' is byte-equal to unset (6481 = 6481), with no hover wrapper.
    • chatbot with against without a full floatingConfig: byte-equal at base and at head (2600 = 2600). The placeholder control moved the DOM, and the same config on chatbot-floating drew its title.

Verification on head c1b5767

Every heavy run went through bash /home/user/objectstack/scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-6152, NODE_OPTIONS=--max-old-space-size=4096 and a memory watchdog; the lowest available memory was 11244 MB.

  • Builds. pnpm exec turbo run build over check-doc-snippet-types --build-filter with --concurrency=2: "Tasks: 35 successful, 35 total". @object-ui/types reports "dist completeness: 1 package(s) complete (138 emitted files verified)".
  • Type-checks. pnpm --filter X run type-check exits 0 with 0 TS errors for @object-ui/types (tsc --noEmit, -p tsconfig.examples.json, -p tsconfig.test.json), components, plugin-chatbot and plugin-grid. The last three are the packages the type-checker census named.
  • Tests, all with pnpm exec vitest run --maxWorkers=4:
    • packages/types/: 306 passed (306), 7701 tests passed;
    • packages/components/ in two runs: 192 passed and 1 skipped (193), with 1920 tests passed; then 156 passed (156), with 1560 tests passed;
    • the consumer set: 309 passed (309), 4939 tests passed. It covers plugin-chatbot, plugin-grid, examples/schema-catalog, packages/cli, the three app-shell files importing @object-ui/types/zod and app-shell/.../metadata-admin/previews/__tests__/.
  • Gates, each exit 0.
    • Changesets: check:changeset-claims, report-only, with 47 pending entries naming a touched file; three were re-read and corrected as above. Also check:pending-changeset-literals, check-changeset-presence ("9 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)"), changeset:check and check-changeset-overwrite, report-only, whose findings are the 3 dated appends with frontmatter unchanged.
    • Docs: check:doc-fences, check:doc-snippets ("698 of 698 block(s) judged, 0 failed"), docs:check-links ("Links are valid across 17 scan roots"), check:doc-examples, check:doc-types, check:doc-example-ids, check:doc-example-readers and check:skill-examples.
    • Others: check:control-bytes, check:new-line-citations ("0 new citation(s)"), check:handler-key-reads, check:component-surface-parity (report-only), check:designer-field-key-parity, check:element-data-source-declaration, check:prompt-keys, check:readme-exports, check:spec-symbols, check:test-path-roots, check:unreferenced-sources and check-type-check-coverage.
    • check-governed-queue-guard --test over the 15 paths: NOT GOVERNED.
  • Narrowed lint against the merge base cd5b19a. The 9 lintable changed files ran through eslint --format json.
    • With inline config: 0 errors and 75 warnings.
    • With --no-inline-config: 1 error and 77 warnings. The one error is the react-hooks/static-components line in data-table.tsx's row-action menu, inline-disabled at an untouched line, and it is identical at base. Per-file error and warning counts are equal at base and head; the new test file reads 0 / 0.
    • The output judged 9 files and ignored 0.
    • Invariance: eslint.config.js declares no parserOptions (so no type-aware project), and no rule in eslint-rules/ reads the disk, so the diff cannot move an untouched file's verdict. The repo-wide lint is CI's.

Ablations, predictions written first, each restored to blob == HEAD with git diff HEAD empty

Each ran through ablation-replace.mjs, with its anchor count proven 1 → 0 on disk and restored.

  • A1 deleted the selectionStyle zod arm. The round-5 pin file read "5 failed | 9 passed (14)", exactly the five predicted selectionStyle rows. tsc -p tsconfig.test.json exited 2 with one error, at assertionUnmirroredMatchesLedger.
  • A2 restored ChatbotSchema.floatingConfig to FloatingChatbotConfig. vitest stayed green on the three chatbot pin files (43 passed), since the change is type-level. tsc exited 2 with exactly the four predicted errors: TS2578 in the round-5 pin file and in the trigger-icon tripwire, TS2344 in the 7655 pin file, and one at assertionDriftMatchesLedger.
  • A3 deleted the editable allow-list row. tsc exited 2 with exactly one TS2344, at assertionRuntimeOnlyIsCallbackShapedOrNamed, and the parity file's vitest stayed 37 passed, as predicted.

Acceptance notes

  • One non-vacuity leg was reshaped, not dropped. The leg asserted that the two unmirrored ledgers share at least one entry. This round made them disjoint for real: DataTableSchema, the last pair in both, left UnmirroredDeclared. Keeping the leg would have forced a key back onto a ledger. It is replaced by a lit control on the same reader: KnownDrift shares a pair with each unmirrored ledger, so the header's 0 is a reading, not a name-normalisation artefact. The reasoning is in the test's own comment.
  • Merged origin/main twice, never rebased: de4459a, then c1b5767. The second merge followed PR feat(types): the dashboard widget's TypeScript chartConfig refuses the retired aria, and both faces are pinned (objectui#4044) #11310, which edits complex.ts. The ledger counts did not move with either merge.
  • PR fix(types): a gate-wrapped arm waives objectName when dataSource.object binds the node (objectui#11117) #11312 (open) adds a WiderThanDeclared entry to the same parity file. The two diffs touch disjoint blocks. Whichever lands second merges main and re-derives the totals by parser count.
  • The optional with-item-count catalog id rename was not done, because this round does not touch that file.
  • Commit trailers are the model-free pair AGENTS.md requires.
  • NOT MEASURED:
    • the full pnpm test farm and the repo-wide lint (CI's);
    • apps/console tests, because no file it owns changed;
    • an at-tier contract record, which the seat owes on the landing head.

Generated by Claude Code

claude added 4 commits October 1, 2026 02:11
… floatingConfig, file editable/singleClickEdit by name (objectui#6152, round 5)

Round 5 of objectui#6152, on the seat's three answers to round 4's open
questions (seat review 5921390451):

- DataTableSchema.editable / singleClickEdit (B): filed by name on
  RuntimeOnlyNamedAllowList as host-paired flags ObjectGrid sets in code;
  data-table.mdx stops teaching them on an authored node.
- DataTableSchema.selectionStyle (B): retired on both faces (a `?: never`
  tombstone and a retirementTombstone arm); the hover-only checkbox branch in
  the data-table renderer is dropped.
- ChatbotSchema.floatingConfig (A): retired on the base chatbot face on both
  faces. ChatbotFloatingSchema never inherited it (it picks the shared keys and
  declares its own member), so no re-declaration was needed.

UnmirroredDeclared 6 / 20 -> 5 / 16 (3 / 14 spec-derived, 2 / 2 local: the two
displayMode entries kept by decision under objectui#7654 ruling B).

Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh
Co-authored-by: Claude <noreply@anthropic.com>
…e two unmirrored ledgers are disjoint (objectui#6152, round 5)

Round 5 emptied DataTableSchema's UnmirroredDeclared entry, the last pair in
both unmirrored ledgers, so their intersection is now genuinely empty and the
header's shared figure reads 0. The leg that asserted a non-empty intersection
would have forced a key back onto a ledger. It is replaced by a lit control on
the same reader: KnownDrift shares a pair with each unmirrored ledger, so a 0
intersection is a reading and not a name-normalisation artefact.

Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation package: types package: components tests labels Oct 1, 2026
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 47 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/5453-retire-grid-column-wrap-forward.md

  • names data-table.tsx → packages/components/src/renderers/complex/data-table.tsx — edited by this change

    generateColumns() copied a per-column wrap onto every column object handed to data-table, and packages/components' data-table.tsx never read it. Measured on the current ref rather than inherited from the card, with comments stripped so prose mentions cannot be counted as reads: a column-level wrap scores 0, against accessorKey 34, align 5, header 4, className 4, width 8 and fitContent 2 in the same query shape. Those sibling counts are the positive control — the search style does find the keys that are genuinely consumed, so the zero is a measurement and not a mis-aimed grep. The raw string wrap does occur in that file; every occurrence is flex-wrap, whitespace-nowrap, or a variable named wrapper.

.changeset/5853-tablecolumn-type-canonical-union.md

  • names data-table.tsx → packages/components/src/renderers/complex/data-table.tsx — edited by this change

    @object-ui/types exports TABLE_COLUMN_TYPES (the canonical tuple — the single declaration the zod mirror builds its enum from, so the two cannot drift), the TableColumnType union, and normalizeTableColumnType() for producers. The as any cast in data-table.tsx is deleted and the read is typed, so re-introducing an undeclared spelling is a tsc error rather than a silent widening.

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

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

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

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

  • names data-table.tsx → packages/components/src/renderers/complex/data-table.tsx — edited by this change

    The fix is a route, not a new check: no visibleWhen test was added to data-table.tsx. A local check on this slot would have been a fourth evaluator, which is the drift page:tabs' item-level predicate already records on this card. The slot now mounts its authored node exactly the way the empty renderer's action slot always has.

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

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

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

.changeset/5993-button-shared-icon-resolver.md

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

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

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

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

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

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

.changeset/6169-chatbot-authoring-face-type.md

  • names content/docs/plugins/plugin-chatbot.mdx → content/docs/plugins/plugin-chatbot.mdx — edited by this change

    Out of scope, deliberately: the chatbot-enhanced and chatbot-floating registrations' own anonymous intersections (different key sets, a decision for a separate card in the same family), and the surface row on content/docs/plugins/plugin-chatbot.mdx's Properties table, which names a key no registration in this package currently reads (filed separately).

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

  • names data-table.tsx → packages/components/src/renderers/complex/data-table.tsx — edited by this change

    • @object-ui/components — DataTableSchema has declared onColumnResize?: (columnKey, width) => void all along, and data-table.tsx invoked it nowhere: the resize drag updated the table's local columnWidths state and stopped there. It now reports the settled width once, at mouseup. Once, deliberately — the host turns this callback into a write to shared view config, so a per-mousemove callback would be a write storm. - @object-ui/plugin-grid — ObjectGrid emitted onColumnReorder (singular) while the renderer invokes the near-duplicate onColumnsReorder (with the s), a different declared key with a different signature. The producer now emits the spelling the renderer actually invokes, mapping the reported TableColumn[] to the accessorKey order columnState stores.
  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

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

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

.changeset/6373-datatable-emit-boundary.md

  • names data-table.tsx → packages/components/src/renderers/complex/data-table.tsx — edited by this change

    The measured read set of the consumer (data-table.tsx, comments stripped) contains none of the six, so all six retire from the emit rather than being declared — declaring a key nothing reads is the same declared != enforced defect facing the other way. Rendering is unchanged because none of those keys was the live path for its own value: the FieldMeta the cell closure captures is what this widget's type-aware rendering has always read, and it is untouched. Authored spellings still pass through, so a column the author wrote as { format: '$0,0' } keeps its format exactly as before.

.changeset/6424-tablecolumn-declares-headericon.md

  • names data-table.tsx → packages/components/src/renderers/complex/data-table.tsx — edited by this change

    Scope of the enforcement claim, measured: the declaration, the parse road, and the renderer's behaviour now agree; the renderer's internal column reads remain any-mediated (the col: any normalization in data-table.tsx) — a standing instrument gap, not closed here.

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

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

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

.changeset/6687-chatbot-surface-authorable.md

  • names content/docs/plugins/plugin-chatbot.mdx → content/docs/plugins/plugin-chatbot.mdx — edited by this change

    content/docs/plugins/plugin-chatbot.mdx's Properties table listed surface ('card' | 'plain', "bordered panel or a frameless full-page workspace"), but the key had zero read points: none of the three ComponentRegistry.register('chatbot*', ...) sites in renderer.tsx forwarded it, and ChatbotSchema did not declare it. surface was real only as a prop of the React component — ChatbotEnhanced.tsx defines ChatbotSurface, defaults it to 'card', and branches six layout decisions off isPlainSurface — so it was reachable by a hand-written React host and by nobody writing metadata. An author who wrote surface: 'plain' got the 'card' default, with no error and no signal.

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

  • names data-table.tsx → packages/components/src/renderers/complex/data-table.tsx — 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/6882-datatable-declare-render-cell-editor-and-cell-classname.md

  • names data-table.tsx → packages/components/src/renderers/complex/data-table.tsx — edited by this change

    data-table has read both keys on its production path all along — renderCellEditor through a (schema as any) cast, cellClassName by destructuring it into the class of its three utility cells (the selection checkbox, the row number, the row actions). Neither was declared, so authoring either one was unchecked: a misspelling produced no error and no widget, and no editor completion offered them. DataTableSchema now declares both, and the cast in data-table.tsx is gone rather than replaced.

  • names content/docs/components/complex/data-table.mdx → content/docs/components/complex/data-table.mdx — edited by this change

    ⚠️ What the schema-level cellClassName actually styles. It is NOT the table-level twin of the per-column key: the two reach disjoint cells. Measured on the render, the schema-level key is folded into the utility cells only — the selection-checkbox cell, the row-number cell and the row-actions cell — while every data cell folds TableColumn.cellClassName and nothing else. Row density is therefore a pair of settings (ObjectGrid sets both), and the schema-level key alone leaves data cells at the primitive's default p-4. The docblock, the zod describe and content/docs/components/complex/data-table.mdx all say this now.

.changeset/6909-fieldeditwidget-dom-pass-through.md

  • names data-table.tsx → packages/components/src/renderers/complex/data-table.tsx — edited by this change

    Also corrects a comment in @object-ui/components' data-table.tsx that this change falsifies. It justified the injected editor's document-level pointerdown listener partly with "FieldEditWidget forwards autoFocus and nothing else out of the DOM block, so a host handler could not reach the control through it even if one were passed" — no longer true. The listener is still load-bearing for the other half of that reason, which is untouched: the renderCellEditor context object has nowhere to put an onBlur in the first place. Comment only; no behaviour change in that package.

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

.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/7113-chart-data-model.md

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

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

  • names content/docs/plugins/plugin-chatbot.mdx → content/docs/plugins/plugin-chatbot.mdx — edited by this change

    Same precedent as CheckboxSchema.wrapperClass (b74a8598d) and the objectui#6150 batch. RuntimeOnlyMessageKeys in plugin-chatbot is untouched; SeamChatMessage inherits the two keys through its ChatMessage half. The three example blocks on content/docs/plugins/plugin-chatbot.mdx that PR docs(guides,plugins,tooling): clear three .mdx pages off the doc-snippet ledger (#5174 batch 5) #7294 left unannotated (supportChat, salesBot, multiAgentChat) are annotated ChatbotSchema again.

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

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

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

  • names content/docs/plugins/plugin-chatbot.mdx → content/docs/plugins/plugin-chatbot.mdx — edited by this change

    content/docs/plugins/plugin-chatbot.mdx gains the restatement of the six removed keys with their replacements (5f4514f7b: a control is restated, never deleted into a vacuum), and its three present-tense claims that the chatbot-floating props spread is still unfiltered are corrected to what PR fix(plugin-chatbot): fence chatbot-floating's raw props spread #8077 actually left behind.

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

  • 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/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/7804-data-table-handler-key-arms.md

  • names renderers/complex/data-table.tsx → packages/components/src/renderers/complex/data-table.tsx — edited by this change

    BaseSchema is .passthrough(), so a key no arm declares is not refused — it stops being judged and the value is KEPT. All seven were in that state while renderers/complex/data-table.tsx read and INVOKED each one, so an authored { "type": "data-table", "onRowClick": { "action": "toast" } } parsed GREEN and that action object was handed to a call site expecting a function. Each key is now a named refusal on the mirror (handlerKeyRefusal, the objectui#6124 shape), and the arm's message says why JSON cannot author it and what to write instead.

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

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

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

.changeset/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 zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

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

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

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

    The remaining 6 addresses (zod/complex.zod.ts) stayed out of scope for this PR and returned to the queue rather than riding this PR's scope — the card did not close here.

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

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

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

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

  • 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/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/9511-record-id-is-a-string.md

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

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

.changeset/9628-kanban-column-collapsed-honoured.md

  • 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/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/dashboard-stable-empty-rows-4629.md

  • names data-table.tsx → packages/components/src/renderers/complex/data-table.tsx — edited by this change

    Nothing rendered wrong before or after; this is wasted work in the empty window, plus the live react-hooks/exhaustive-deps warning the conditional raised. It is the same module-scope frozen empty data-table.tsx adopted for its own EMPTY_ROWS (objectui#4618), applied to the provider: 'object' siblings.

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

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

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

.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 0c6f9bbd7 (merge-base with origin/main): 11 file(s) changed outside .changeset/, read against 1882 pending declaration(s) that publish a body (2495 pending in total). · run

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

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

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


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 571.24KB 136.81KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
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.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.95KB 11.48KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.90KB 15.35KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.45KB 44.60KB
plugin-detail (index.js) 244.79KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.70KB 44.63KB
plugin-gantt (index.js) 173.03KB 43.07KB
plugin-grid (index.js) 231.61KB 63.61KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.54KB 7.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.05KB 9.67KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

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

Labels

documentation Improvements or additions to documentation package: components package: types tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants