Skip to content

feat(types): arm the seven registered node types that pass the criterion — page kinds, app-schema-renderer, object-pivot, embeddable-form, detail-section (objectui#11440) - #11493

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-11440-arm-passing-types
Oct 2, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-11440-arm-passing-types

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #11440

Clause-②: yes

The first of objectui#11440's two pull requests. It arms the seven registered node types that the seat ruling 5945530142 on objectui#10859 (as amended by 5945583855) passes: the spec page kinds record, home and utility, and app-schema-renderer, object-pivot, embeddable-form and detail-section. safeValidateSchema, and so objectui validate, refused each one with invalid_union at type. REFUSED_AT_TYPE falls 8 to 1. The eighth key, spec-report, is the fold into report, which is the second pull request (see "What is left" below).

Dispatched run: claim 5954845613 (domain:ui seat 1), session https://claude.ai/code/session_01YLg8XqWGJ785fwQ5v4pH37. Draft, left for the seat.

What changed

key arm shape
record, home, utility PageKindNodeSchema (layout.zod.ts), its own arm of AnyComponentSchema every member is PageNodeSchema's own, by reference (the spec's PageSchema fields among them, through SpecPageFields); type is the spec's PageTypeSchema narrowed with .extract to the three kinds @object-ui/components registers on PageRenderer
app-schema-renderer AppSchemaRendererNodeSchema (app.zod.ts), its own arm basePath and mobileNavMode ('drawer' or 'bottom_nav'), from the registration's inputs; children and body refused by name
object-pivot ObjectPivotBlockSchema (objectql.zod.ts), in ObjectQLPublicBlockComponentSchema a REQUIRED properties bag of the registration's inputs (the cross-tab members are the pivot mirror's own, by reference; drillDown refuses mode; the retired dataProvider is refused); flat props refused by name toward properties.KEY; dataSource binding and responsiveStyles on the node; RECORD_SOURCE_REQUIRED unless dataSource.object names the object
embeddable-form EmbeddableFormBlockSchema (objectql.zod.ts), same union a REQUIRED bag of exactly the registration's inputs (formId required), built as above
detail-section DetailSectionNodeSchema (views.zod.ts), in ViewComponentSchema the ten DetailViewSectionSchema members the registration publishes, .picked by reference, flat; fields required

Also:

  • object-pivot's registration publishes drillDown as an input (@object-ui/plugin-dashboard). ObjectPivotTable reads it, and the pivot node's refusal of drillDown names this block as where a pivot drill is authored.
  • @object-ui/types exports ObjectPivotBlockNode and EmbeddableFormBlockNode. objectui#11468 (landed during this run) derives ObjectQLPublicBlockNode from the union's arms, so the two arms joined it. Its ClosedBag closed only an optional bag, so it closes a required one too now, and no AuthoringNode member carries an open bag.
  • Prose this change made false is amended: three docblocks (ObjectPivotDrillDownConfig, DrillDownConfigSchema, ObjectPivotTable's dataProvider note), and pure-append dated notes on three pending changesets (10685-drilldown-per-block.md, 7352-drill-down-config-mirror.md, 11468-bag-carrier-node-types.md).
  • Changesets: .changeset/11440-arm-passing-types.md (@object-ui/types minor, each new authorable spelling listed, no BREAKING banner: a pure widening) and .changeset/11440-object-pivot-drilldown-input.md (@object-ui/plugin-dashboard minor).

Contract notes

For the at-tier reviewer.

  • Widens. AnyComponentSchema accepts seven type literals it refused at type, on both faces, at the root and at every child slot. @object-ui/types/zod exports five schemas. @object-ui/types exports two TypeScript node types and AuthoringNode gains their members. The object-pivot registration publishes one more input (drillDown).
  • Narrows. Nothing that parsed before is refused now. Every document naming one of the seven was refused before, so the refusals inside each arm (a flat prop, a misspelt mobileNavMode, a missing fields, drillDown.mode) replace a refusal at type with a named one. The ClosedBag change touches only the two new bags: the objectui#11468 pin shows the other members unchanged.
  • Evidence. Base readings below (each key refused at type, both faces); the new pin packages/types/src/__tests__/passing-keys-arms-11440.test.ts holds what each arm accepts and refuses on both faces with a lit control per refusal; packages/cli/src/__tests__/validate-passing-keys-11440.test.ts runs objectui validate end to end; each registration's inputs are pinned against its arm in the package that registers it (four new files); two ablations turn the ratchet and the pins red (below).

Zone 2 readings — the mechanism assumptions, measured

  1. Registrations: held, with one correction. All seven are where the brief says. app is registered on PageRenderer too (with page), not only the three kinds; its literal is the app document's (AppComponentSchema). Each declaration is built from the registration's inputs; where a renderer reads a key the inputs omit, it is named below.
  2. The page kinds: held. At the merged base 6007dd4e4 the card's probe { type: 'home', name, regions } and the spec's own page document were refused at type on both faces, while { type: 'page', pageType: 'home' } passed. After: all pass. The precedent followed is PageNodeSchema itself (the spec fields by reference through specFieldsExcept); the strict face derives from the arm like every other. On objectstack's example pages (objectstack 4e6dc233, read only, the eleven definePage documents of kind home or record in app-crm and app-showcase, run through both faces by an uncommitted probe): base, 11 of 11 refused at type on both faces; after, none is refused at type, and 10 of 11 parse on both faces. The eleventh (showcase_my_work) passes the tolerant face, and the strict face refuses a nested { type: 'grid', properties: { … } } region component as an unrecognized key (properties): the grid arm takes its props flat. That is the grid arm's existing posture, unchanged here: the same component inside a type: 'page' document is refused the same way (measured). app and list today, unchanged here: a spec page of kind app is judged by the app document's arm (the tolerant face passes it unjudged, the strict face refuses regions, template, kind as unrecognized keys); a spec page of kind list meets the list component's arm (refused for having no items, and on the strict face for regions and the other page keys). No new key is registered. ⚠️ A finding on the way: declaring the page node's members a second time inside AnyComponentSchema made tsc refuse the union with TS7056 (inferred type too long to serialize); PageKindNodeSchemaType names the arm's type by reference, so declaration emit names it.
  3. spec-report: not in this pull request. Measured only: the strict face refuses { type: 'report', report } as an unrecognized key (report), and the tolerant face passes it unjudged. The fold needs a file outside the claimed surface (below).
  4. detail-section: armed, not folded. The spec's record:details section names its heading label and takes field NAMES; this node names it title and takes DetailViewField objects. The spec row refuses the README's section with unrecognized_keys on title and invalid_type on each field object (pinned). After arming, the plugin-detail README's detail-view document validates except for one issue, at tabs.1.content.items: its second tab hands record:activity a host feed (items: activityData), which objectui#11321 refuses by name in a JSON document, and whose refusal text names this README as the TSX composition where it stays legal. Measured both ways by objectui validate (the first tab alone: valid; the full document: that one issue). The README is not edited.
  5. app-schema-renderer: the guide's node validates; the schema input is not declared. The governed guide teaches the node's mobileNavMode key, in prose, and { type: 'app-schema-renderer', mobileNavMode: 'bottom_nav' } validates on both faces after. The registration declares three inputs. Measured through the real SchemaRenderer and registry: SchemaRenderer strips the schema key and hands the component the NODE as its schema prop, so a node with its navigation under schema drew no navigation, and the same navigation written on the node drew. A node's children drew nothing either. So the arm declares basePath and mobileNavMode and refuses both content channels; where the app document belongs on this node is an open question for the seat (below). No skills/** edit.
  6. Namespaced twins: none armed; the pin holds at 362. The seven twins are ui:home, ui:record, ui:utility, layout:app-schema-renderer, plugin-dashboard:object-pivot, plugin-form:embeddable-form, plugin-detail:detail-section. Each is its own literal, so the bare arm does not claim it; the ratchet row asserts each twin is still refused at type.
  7. The objectui#8347 ceiling-pin tests: not tripped (types suite green on the head). objectui#11468 and objectui#11478 landed during the run and were merged in; the first needed the two node types above.

Gates

Head ccb1077bc (a merge of main 401611b21 into 34d127803). Exit codes captured to a file before reading. Heavy runs through the shared verify lock; absolute times are shared-box readings.

check result
pnpm --filter @object-ui/types build && … type-check (three legs) exit 0 · "dist completeness: 1 package(s) complete (144 emitted files verified)"; the new pin is in tsconfig.test.json --listFilesOnly
pnpm exec vitest run packages/types/ packages/cli/ exit 0 · Test Files 361 passed (361) · Tests 9266 passed (9266)
type-check @object-ui/plugin-dashboard, plugin-form, plugin-detail, layout, cli (after their closure build) exit 0, each type-check: Done
packages/layout/ plus the plugin-form / plugin-detail tests that read a zod face or name the keys, plus every apps/console / packages/app-shell test that reads a zod face or names a key exit 0 · 101 files, 1190 tests
every other test outside the touched packages that reads a zod face, KNOWN_SCHEMA_TYPES, or names one of the eight keys (examples, scripts, components, core, react, plugin-report and others) exit 0 · 89 files, 2309 tests
packages/plugin-dashboard/ whole suite, at 30614b964 (before the two merges; neither touched this package) exit 0 · 161 files, 1534 passed, 6 skipped; its 20 zod-reading or pivot tests re-run on the head: exit 0
node scripts/regenerate-known-schema-types.mjs --check exit 0 · "matches the registry derivation"
check:doc-snippets (after its --build-filter closure build), check:doc-examples, check:readme-exports exit 0 each · 776 of 776 blocks judged, 0 failed; 122 examples, ledger exact; OK
check:doc-types, check:doc-fences, markdown-test-inputs --audit exit 0 each
check:control-bytes, check:new-line-citations (0 new), check-changeset-presence, check-changeset-no-major, check:pending-changeset-literals exit 0 each
check:changeset-claims (report-only) exit 0 · read the pending changesets that name a touched file; three made false by this change got the dated notes above
check:handler-key-reads, check:element-data-source-declaration, check:registry-bare-names, check:spec-symbols, check:test-path-roots exit 0 each
check-governed-queue-guard --test (the changed paths) NOT GOVERNED
pnpm check (the CLI sweep over the repository) before (types source swapped to main 401611b21 under a trapped restore, proven by blob hash and an empty git diff HEAD) and after: identical, 287 validated, 146 recognised but not validated, 0 did not validate, 47 skipped. No tracked JSON or YAML document names one of the seven types, so the sweep has nothing new to validate. The expectation that more documents validate does not hold
ESLint, narrowed --no-inline-config --format json over the 24 changed lintable files: 1 error, 53 warnings, 0 of them on a line this branch adds (attributed by git diff -U0 against the merge base). The error is the no-restricted-imports report on a @objectstack/spec/ui re-export in packages/types/src/index.ts that an eslint-disable comment covers, which --no-inline-config ignores; that line predates this branch. Population: each touched package's lint script; eslint.config.js sets no type-aware parser project (0 matches for parserOptions, project:, projectService), so no untouched file's verdict can move. Full pnpm lint is CI's

Reverse verification

  • Base, predicted and read. At 6007dd4e4 each of the seven was refused with one invalid_union at type on both faces, and the ratchet read 8.
  • Ablation 1, the page kinds. PageKindNodeSchema removed from the root union by ablation-replace (anchor hit 1 to 0, blob moved), the pin files run, the file restored. Predicted: the ratchet goes red with the three kinds. Read: 3 files red, 14 rows; "objectui validate refuses 4 registered bare key(s) at type; the pin is 1. Refused: home, record, spec-report, utility". Restore proven: blob equals HEAD, git diff HEAD empty.
  • Ablation 2, a declared plugin key. ObjectPivotBlockSchema removed from ObjectQLPublicBlockComponentSchema the same way. Read: 2 files red, 15 rows; "Refused: object-pivot, spec-report". Restore proven the same way.
  • The first attempt at both was a no-op, and is reported as one: the replacement text was a prefix of the anchor, so the tool refused (replacement count did not rise) and restored without running anything. The readings above are the second attempt, with replacement text that was not in the file.
  • Ablation 3, the report wrapper: belongs to the second pull request.

What is left (the second pull request)

The spec-report fold, in the ruled step order (report declares the wrapper shape, DrillDownDrawer moves to report, then the alias goes), needs one file the claim does not name: apps/console/src/register-plugins.ts registers a lazy spec-report stub beside report-viewer, and an unregistered key with a surviving console stub strands that stub. Per the brief, that waits for the seat to amend the claim; it is not started here.

Acceptance notes

  • object-pivot and embeddable-form registrations still declare objectName required: true, while both renderers are gate-wrapped and the arms accept dataSource.object instead. The parser tier's verdict on a binding-only node was not measured here; objectui#10392 is the calendar instance of the same shape.
  • EmbeddableForm reads config keys its registration does not publish (branding, thankYouPage, the anti-spam keys and others). The arm declares the registration's inputs only, so the strict face refuses them as unrecognized keys.
  • PageRenderer derives its layout from pageType, defaulting to record, and not from type, so a kind node rendered outside PageView (which writes both) would draw the record layout. Read from the source, not measured.
  • The plugin-dashboard README and plugin-dashboard.mdx describe object-pivot as an "internal wrapper around ObjectPivotTable"; it is a Tier A public block with an arm now.

Generated by Claude Code

claude added 7 commits October 2, 2026 15:27
…(objectui#11440)

`safeValidateSchema`, and so `objectui validate`, accepts the spec page
kinds `record` / `home` / `utility`, and `app-schema-renderer`,
`object-pivot`, `embeddable-form` and `detail-section`, each refused at
`type` before. `REFUSED_AT_TYPE` falls 8 -> 1; `spec-report` remains
for the fold into `report`.

- `PageKindNodeSchema`: the page node's members by reference under the
  spec's `PageTypeSchema` narrowed to the three registered kinds.
- `AppSchemaRendererNodeSchema`: `basePath` and `mobileNavMode` from the
  registration inputs; `schema` is not declared (never delivered).
- `ObjectPivotBlockSchema` / `EmbeddableFormBlockSchema`: required bags
  of the registration inputs, flat spelling refused by name.
- `DetailSectionNodeSchema`: the ten registered section members, flat.
- `object-pivot` publishes `drillDown` as a registration input.

Claude-Session: https://claude.ai/code/session_01YLg8XqWGJ785fwQ5v4pH37
Co-authored-by: Claude <noreply@anthropic.com>
…ill mirror's referencing declarations (objectui#11440)

The end-to-end rows restate the plugin-detail example instead of reading
its README, so the markdown-input ledger has nothing new to adjudicate.
`.changeset/7352-drill-down-config-mirror.md` gains a pure-append dated
note: the `object-pivot` arm is a third declaration extending the mirror.

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

`LayoutSchema`'s enum-keyed arms are the two html-tag families that
`@object-ui/core`'s html-tier roster pin finds by shape; a third
enum-keyed arm there turned that pin red. The zod README stops naming
the app document's arm by symbol, which the objectui#7469 census reads.

Claude-Session: https://claude.ai/code/session_01YLg8XqWGJ785fwQ5v4pH37
Co-authored-by: Claude <noreply@anthropic.com>
Brings in objectui#11473, objectui#11477 (objectui#11468) and objectui#11481.

Claude-Session: https://claude.ai/code/session_01YLg8XqWGJ785fwQ5v4pH37
Co-authored-by: Claude <noreply@anthropic.com>
…de types, their required bags closed (objectui#11440)

objectui#11468 (landed on main) derives `ObjectQLPublicBlockNode` from the
union's arms, so the two new arms joined it. `ClosedBag` closed only an
optional bag; the two required bags are closed now too, so no
`AuthoringNode` member carries an open bag. `ObjectPivotBlockNode` and
`EmbeddableFormBlockNode` are named and exported beside the other eight,
and the objectui#11468 pin lists them.

Claude-Session: https://claude.ai/code/session_01YLg8XqWGJ785fwQ5v4pH37
Co-authored-by: Claude <noreply@anthropic.com>
…ote the 11468 entry's member count (objectui#11440)

Claude-Session: https://claude.ai/code/session_01YLg8XqWGJ785fwQ5v4pH37
Co-authored-by: Claude <noreply@anthropic.com>
Brings in objectui#11485 (objectui#11465), objectui#11486 (objectui#11478)
and objectui#11482 (objectui#11467).

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

github-actions Bot commented Oct 2, 2026

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

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

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

.changeset/11355-small-p1-sites-r2.md

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

    ObjectChartSchema.isAnimationActive?: boolean is declared on the TypeScript face. Code sets it false for a render with no entrance animation: DashboardRenderer and DashboardGridLayout on the object-chart nodes they build, and DatasetWidget and DatasetReportRenderer on the nodes they hand the chart registration. ChartRenderer honours it. The zod mirror declares no member for it, so it is not an authoring key; zod-mirror-parity.test.ts files it as runtime-only.

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

  • names zod/app.zod.ts → packages/types/src/zod/app.zod.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/5903-objectgantt-declared-keys.md

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

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

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

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

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

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

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

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

    GanttConfig itself gains nine members and is a published type, exported by name from packages/types/src/index.ts: lockField, objectField, summaryExtent, defaultCollapsedDepth, borderColorField, dependencyTypes, timeZone, exportFileName, interactions. The entry file's diff is empty only because the export list already named the type — the widening happened at the declaration.

  • 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/6175-column-state-persistence.md

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

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

.changeset/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/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/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/6940-rowactions-boolean-mirror.md

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

.changeset/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/7735-zod-mirrors-stop-authoring-defaults.md

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

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

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

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

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

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

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

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

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

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

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

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

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

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

.changeset/7918-zod-lazy-getter-identity.md

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

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

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

  • names packages/types/src/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/8344-node-recursion-point-redirect.md

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

.changeset/8735-objectql-mirror-docblocks-not-defaulted.md

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • names zod-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/9549-tree-filter-declared.md

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

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

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

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

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

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

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

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

.changeset/component-meta-converge-and-deprecate-alias-5893.md

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

    ⚠️ Dated note, 2026-09-29 — the one-occurrence reading above has since moved — objectui#10979. "In-repo, PluginComponentMeta has exactly one occurrence — its own export line" above is the reading of the commit this change landed on (575b71c850), where a whole-tree search for the name returned that one line, in packages/types/src/index.ts. This change then added its own pins and this entry, and later in this same release objectui#5674's retirement of the sibling alias PluginComponentInput (PR objectui#10826) added mentions in its test and its entry. Re-measured on main at 2eaf5be27, the name occurred on 19 lines in 7 files. In non-test source it appeared only on the export line and on one doc-comment line in plugin-scope.ts, and the one file that imported it was this change's own component-meta-single-declaration.test.ts. The text above is kept as the reading of this change.

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

Compared the checked-out tree with 9de0b3483 (merge-base with origin/main): 25 file(s) changed outside .changeset/, read against 2028 pending declaration(s) that publish a body (2657 pending in total). · run

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3550.7 KB 3574.6 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-Ba5pn9D2.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.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 569.83KB 136.77KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 230.46KB 63.94KB
fields (index.js) 261.25KB 66.11KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 39.36KB 11.18KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.17KB 15.46KB
plugin-charts (index.js) 83.53KB 22.84KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 141.72KB 38.14KB
plugin-designer (index.js) 231.28KB 48.76KB
plugin-detail (index.js) 245.27KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 172.73KB 44.50KB
plugin-gantt (index.js) 179.14KB 45.06KB
plugin-grid (index.js) 232.66KB 63.77KB
plugin-kanban (index.js) 49.51KB 15.54KB
plugin-list (index.js) 116.60KB 28.96KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 38.80KB 11.71KB
plugin-tree (index.js) 14.51KB 5.15KB
plugin-view (index.js) 90.23KB 22.73KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.32KB 1.64KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.07KB 2.39KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

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

PR objectui#11493, Part of #11440 (PR 1 of 2: the seven arms; the spec-report fold is PR 2 and outside this review). Judged at 2026-10-02T17:51Z on: the card body and every comment on objectui#11440; the seat ruling 5945530142 and its amendment 5945583855 on objectui#10859; the PR body and file list; the net diff of the head against its merge base with main (401611b21: 30 files, +1429 / −56); the registration and derivation sites at the head, read with git show; and the check-runs on the head. The dispatch order and the seat's own ACCEPT (5957931899) were read as claims to test, not as inputs.

Check-runs on the head: 43, every one completed as of 2026-10-02T17:48Z (polled at 60 s intervals; the aggregate Test was the last to appear): 40 success, 3 skipped (dependabot, Test (coverage), Test (coverage shard)), 0 failure, 0 cancelled. Among the successes: Test and its dist pins and shards 1 to 8, Type Check, Lint, Spec Main Shape Gate, Build & E2E, Doc Snippet Type Check, Doc Component Type Check, README Export Check, Governed Surface Queue Guard, Line Citation Gate, and the four changeset gates. Those conclusions are the gate verdicts here; nothing was built, run or re-run locally.

① Derived judgments

Accept set (AnyComponentSchema, both faces: safeValidateSchema, and StrictAnyComponentSchema, which the walker derives from the root union, so it picks the new arms up without a hand edit):

  1. record / home / utility → PageKindNodeSchema, an arm of the root union in its own right. Right. type is the spec's PageTypeSchema by reference, .extracted to the three kinds page.tsx registers on PageRenderer beside page and app (read at the head, lines 801 to 805); every other member is PageNodeSchema's own schema object, by reference (pinned per key), so the page node's four refusals, its body alias refusal and checkPageSourceCompleteness ride along. app stays AppComponentSchema's literal and list the list component's: right, neither is registered under that key on PageRenderer. Observation, not a breach: pageType is a page member, so a kind node may also carry one; both spellings validate, and the ruling leaves the canonical spelling to triage.
  2. app-schema-renderer → AppSchemaRendererNodeSchema. Right, with the escalation in ③. basePath (string) and mobileNavMode (drawer | bottom_nav) are the registration's inputs by type and vocabulary (packages/layout/src/index.ts 170 to 193, pinned both ways). The third input, schema, is left undeclared on a measured reading (the renderer hands the component the node itself; a document nested under schema draws nothing), so the strict face refuses it as unrecognized and the tolerant face leaves it unjudged: declaring it would publish an input that renders nothing. children / body are refused by name: the registration says isContainer: true but declares no children input, and the parser tier reads the input, so the two tiers agree. The amended objectui#11363 pin is right: a misspelt mode is now refused by this arm's own enum (invalid_value), never by the app document's tombstone, and the node was refused at type before, so nothing accepted before is refused now.
  3. object-pivot → ObjectPivotBlockSchema, in ObjectQLPublicBlockComponentSchema. Right. The REQUIRED properties bag is exactly the registration's eleven inputs (read at the head, plugin-dashboard/src/index.tsx 385 to 406): the cross-tab members by reference to the pivot mirror (aggregation is the same five-value enum the registration publishes), filter an array, drillDown the shared drill mirror with mode refused (objectui#10685's shape), plus the dataProvider tombstone the TS prop type already spells; pinned in both directions against the registration. dataSource on the node is right: the registry injects ELEMENT_DATA_SOURCE_INPUT into every gate-wrapped registration (core/src/registry/Registry.ts 452 to 453), which is why the pins find it among the inputs. Flat props are refused by name toward properties.KEY (the batch-10 construct). data and columnColors undeclared: right, the registration publishes neither.
  4. embeddable-form → EmbeddableFormBlockSchema, same union. Right. The required bag is exactly the registration's six inputs (plugin-form/src/index.tsx 394 to 406), formId required as there. fields is declared as the registration's taught vocabulary (bare names); the { name } form the registration calls "tolerated" stays an undeclared runtime affordance, as the headerColor pass-through does elsewhere, and no document naming the key was accepted before, so this is not a narrowing. The config keys the component reads beyond its inputs stay undeclared, so the strict face refuses them: right, the registration is the published surface (flagged in ③).
  5. detail-section → DetailSectionNodeSchema, in ViewComponentSchema. Right. The ten members are .picked by reference from DetailViewSectionSchema and equal the registration's inputs and DETAIL_SECTION_NODE_INPUTS (read at the head: title, description, icon, fields required, collapsible, defaultCollapsed, columns, showBorder, headerColor as the six-token enum, hideEmpty); name / visible keep the base meaning, which is what DetailSection reads. Armed rather than folded: the card allowed the fold only "if measurement shows that is the same shape", and the pin shows the spec's record:details row refusing the README section (label against title, field names against field objects). Right.
  6. spec-report: untouched, still registered and still refused at type. Right for PR 1: the ruling's step order (the wrapper on report, the drawer moves, then the alias goes) belongs to PR 2.
  7. Ratchet: REFUSED_AT_TYPE 8 → 1, with the refused set pinned to exactly ['spec-report']; NAMESPACED_REFUSED_AT_TYPE holds at 362, and each of the seven namespaced twins is asserted still refused. Right: neither rises. The dev's base reading (each of the seven refused with one invalid_union at type on both faces) and the two ablations (14 and 15 rows red, restores proven) are consistent with the diff; none was re-run here.

Public surface:

  • @object-ui/types/zod exports five schemas (PageKindNodeSchema, AppSchemaRendererNodeSchema, ObjectPivotBlockSchema, EmbeddableFormBlockSchema, DetailSectionNodeSchema), and the barrel edits match. Right.
  • @object-ui/types exports ObjectPivotBlockNode and EmbeddableFormBlockNode; ObjectQLPublicBlockNode and AuthoringNode gain the two members. Right. ClosedBag's new branch (close a REQUIRED bag) is applied to the properties member only (ClosedArmShape), and the pre-existing _NoAuthoringNodeBagIsOpen pin was already green under the old ClosedBag, which left required members untouched, so no existing arm had a required open bag: no existing node type narrows. The widened control ('object-chart' | 'flex' | 'object-pivot' | 'embeddable-form') is the right reading.
  • PageKindNodeSchemaType names the arm's type by reference so declaration emit does not re-serialize the page shape (the TS7056 the dev met). Right; Type Check and Test (dist pins) on the head are the judges, both green.
  • @object-ui/plugin-dashboard: the object-pivot registration publishes drillDown (object), which ObjectPivotTable already read. Right: a widening of the manifest, and the arm's member agrees with it.
  • Prose: three docblocks and the zod README (public blocks 8 → 10; a "Shell and Section Nodes" section) are made true; three other cards' pending changesets receive dated notes, and the diff shows only appended lines in each. Right.
  • Nothing narrows: every document naming one of the seven was refused at type on both faces before, so each in-arm refusal replaces a refusal at type. Right.
  • known-schema-types.ts is untouched: right, this PR adds and removes no registration key.

② Semver level

  • .changeset/11440-arm-passing-types.md: @object-ui/types minor. Right. The accept set widens by seven type literals and the package gains five zod exports and two TypeScript types; no previously valid document is refused and no existing TypeScript node type narrows (above). No BREAKING banner is right: the ruling's "minor with a BREAKING (authoring) banner" is for retirements, which are PR 2's. Each new spelling is listed with what it accepts and refuses, and the wording matches the refusal sites read in the diff ("refused by name" is invalid_type at the key's own path on both faces; "as an unrecognized key" is the strict face alone).
  • .changeset/11440-object-pivot-drilldown-input.md: @object-ui/plugin-dashboard minor. Right: one more published input.
  • packages/cli, packages/layout, packages/plugin-detail, packages/plugin-form: tests only, so no changeset is owed. Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check and Changeset Claim Re-read are green on the head.
  • skip-changeset: not used, rightly.
  • Clause-②: yes. Right: the arms widen the accept set at type on a published validator door (objectui validate). Body line 3 carries it; the claim and the changeset say the same.

③ Boundary flags

Dev open_questions:

  1. Where the app document belongs on an app-schema-renderer node (A / B / C). Not answered in this PR, rightly: it is a contract-shape choice. Escalated: objectui#11494 exists (open, class (a), filed by the fleet account, citing the dev's pin row), read independently of the seat's record. This PR declares only what is true under every option.
  2. The spec-report fold needs apps/console/src/register-plugins.ts. Answered A by the seat in 5957931899: claim 5954845613 is amended to add that file and the named tests; PR 2 is cut from origin/main after this PR lands and says Fixes #11440. Nothing of the fold is in this diff. Consistent.

Dev deviations and flags:

  • pnpm install --config.engine-strict=false; the container restart; the two main merges with amended messages (no rebase, no force-push); model-free trailers: mechanics, no contract effect. The head is a merge of main 401611b21.
  • Local suites narrowed for plugin-form / plugin-detail / plugin-dashboard: superseded by the full Test shards on the head.
  • Another card's pin (authoring-nodes-bag-carriers-11468.test.ts) edited: necessary, since objectui#11468 derives the union from the arms; the edit is additive and the invariant pin is unchanged. Three pending changesets of other cards date-noted: pure-append, verified in the diff. Accepted.
  • One PR, not two: body line 1 is Part of #11440. Accepted.
  • The TS7056 finding: closed in the diff by PageKindNodeSchemaType.

Dev out_of_scope_findings and the PR's acceptance notes:

  • class (a), the schema input and the children teaching in content/docs/guide/layout.md: escalated to objectui#11494 (above).
  • objectName: required on the object-pivot and embeddable-form registrations against the arm's dataSource.object waiver, parser-tier verdict unmeasured: the arm follows the gate-wrapped precedent (the gate lands the binding's object on objectName), the zod accept set only widens, and the shape predates this PR on every gate-wrapped block (objectui#10392 is the calendar instance). Flagged, no carrier: the seat holds it as an acceptance note; not a FAIL condition here, but a binding-only node may still meet a missing-required-prop at the parser tier until someone measures it.
  • EmbeddableForm reads unpublished config keys (the strict face refuses them): noted, no carrier; the registration is the published surface.
  • PageRenderer derives its layout from pageType (default record), not from type, so a kind node rendered outside PageView draws the record layout (read, not measured): noted, no carrier; a render-side reading this validator change does not alter.
  • plugin-dashboard README / plugin-dashboard.mdx "internal wrapper" wording: rides in PR 2 per the seat.
  • eslint --no-inline-config note on a pre-existing line: Lint on the head is green.

Serial: main has moved one commit past the merge base (the PR reads behind): objectui#11476 (PR objectui#11487), which touches packages/cli/src/cli.ts, cli-bin.test.ts, content/docs/utilities/cli.mdx and its changeset, none of which this diff touches. objectui#8347 (BaseSchema's index signature) has not landed; per the claim, whichever of the two lands second re-runs the type pass and merges main. A queue-time merge, not a verdict on this head.

Implemented-by: claude/issue-11440-arm-passing-types
Reviewed-by: session_01YLg8XqWGJ785fwQ5v4pH37

VERDICT: PASS

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants