Skip to content

feat(types,react,core,plugin-dashboard)!: a node slot and SchemaRenderer's schema prop take the declared-node union, V1; the dashboard producers name declared nodes and an envelope's object-metric retires (objectui#11466) - #11512

Draft
objectstack-fleet[bot] wants to merge 18 commits into
mainfrom
claude/issue-11466-node-slot-union-v1

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #11466

This PR carries ruling ① (V1) and the card's residue, with main merged in, the plugin-dashboard producers closed, and the maintainer's ruling A on the last inline metric form (comment 5966278275). The census reads 0 lines across all 88 workspace type-check programs, with BaseSchema's index signature present.

Clause-②: yes (narrowing)

Depends on objectui#11543 (governed PR #11558). Skill Example Check stays red on this PR until it lands: three marked fences in skills/objectui/guides pass a BaseSchema to SchemaRenderer, which V1 refuses. At cb687dfad the gate reads 18 of 18 fences judged, 3 failed, and nothing else in CI is red.

What changes

  • @object-ui/types exports DeclaredNode. It is the discriminated union, keyed by the literal type, of:
    • every component schema AnySchema declares, minus its BaseSchema arm (whose type is string) and minus the app-level document, which is left out by { type: 'app' } (AppSchemaRenderer reads that document structurally and ComponentRegistry never dispatches it);
    • every spec-declared AuthoringNode;
    • every entry of the new CustomNodeRegistry interface. An entry joins under its KEY: the arm is the entry intersected with { type: KEY }, so even a loose entry adds no type: string arm.
  • SchemaNode is DeclaredNode | string | number | boolean | null | undefined. It was BaseSchema | string | number | boolean | null | undefined.
  • SchemaRendererProps['schema'] is DeclaredNode | string | null | undefined. It was BaseSchema | AuthoringNode | string | null | undefined. toRenderableSchema follows by reference.
  • @object-ui/core's schema builder takes DeclaredNode where it took BaseSchema: .child() and .children() on the grid and flex builders, and the card builder's .content().
  • @object-ui/components' kind: 'html' page crosses its runtime-parsed tree into DeclaredNode at ONE documented boundary, right after validateTree reports no error (page.tsx, objectui#11479 Q2 A). No other cast to DeclaredNode exists under src/.
  • @object-ui/test-support gains undeclaredNode, the one crossing for deliberately undeclared test input (objectui#11479 Q3 A). It has 25 call sites in 15 test files, and none outside tests.
  • @object-ui/plugin-dashboard:
    • plugin-dashboard:metric is declared through CustomNodeRegistry (objectui#11479 Q1 A). DashboardMetricNodeSchema, a new export, is the node MetricWidget renders under that key, with the keys it reads off the node, typed by MetricWidgetProps. widgetDispatch.ts enters it in the registry beside DASHBOARD_NODE_TYPES, whose values are now literal types (a string-keyed view serves the by-type lookup). The package entry exports the type, so its published typings load the declaration.
    • Both surfaces' node builders (getComponentSchema in DashboardRenderer and DashboardGridLayout) return SchemaRendererProps['schema'], so each node is checked against its declared type where it is built. The metric arms narrow widgetType for the required label; classifyWidgetType answers metric only for a named type, so no verdict moves.
    • DashboardGridLayout's static pivot node states the keys PivotTable draws (its PivotTableSchema keys, and className), read from options, instead of spreading options whole, so it names PivotTableSchema with no cast.
    • The casts before SchemaRenderer are gone: as BaseSchema | string | null | undefined in DashboardGridLayout and as BaseSchema in DashboardRenderer. No new cast.
    • An object-metric node in a widget's legacy component envelope draws LEGACY_RETIRED_WIDGET_SCHEMA on both surfaces (the maintainer's ruling A on objectui#11466, extending ruling C on objectui#11525), under its bare key and under plugin-dashboard:object-metric, and sends no query. One shared detector, isRetiredEnvelopeNode in legacyRetiredWidget.ts, is read by both surfaces before the envelope pass-through, and the placeholder is the same imported object the metric and pivot arms return. object-metric leaves the filter broadcast's FilterableComponentType / FILTERABLE_COMPONENT_TYPES, so the flat-filter write the declared object-metric node refuses is gone. The broadcast now covers object-chart and object-data-table, built by the renderer or written in an envelope.
  • Not here: a type: string arm, an index signature, a transitional BaseSchema arm, BaseSchema's own index signature, and the component slot's BaseSchema fallback (all objectui#8347's). Every zod face and every TypeScript authoring face of object-metric stays as it was, and so does the authored object-metric block on a page. Two runtime moves, both in @object-ui/plugin-dashboard: an option key the static pivot does not state no longer reaches its node, and an envelope's object-metric draws the rebind prompt.

The accept set, before and after (TypeScript face only)

Judged by packages/types/src/__tests__/node-slot-union-11466.test.ts, by the react prop pin, and by the premise probes below.

  • Undeclared type, nested or at the prop, e.g. { type: 'card', children: [{ type: 'txt' }] }. Before: compiles through the BaseSchema arm. After: TS2322.
  • A value typed BaseSchema at the prop or in a slot. Before: compiles. After: refused, because it names no declared type.
  • A misspelled key on a closed node type (an AuthoringNode, or a closed CustomNodeRegistry entry), nested at any depth. Before: compiles through the BaseSchema arm. After: refused, because the slot discriminates on type.
  • A misspelled key on a node type that extends BaseSchema. Before: compiles. After: still compiles, through that type's own index signature, until objectui#8347 removes it. Measured below as the signature-present control leg.
  • A missing REQUIRED key. Before: compiles through the BaseSchema arm. After: refused. Examples: a stored home page without label, a data-table without columns, a static pivot without its axes.
  • A registered custom type. Before: any type compiled. After: it compiles once declared in CustomNodeRegistry; an unregistered one is refused.
  • A plugin-dashboard:metric node. Before: compiled through the BaseSchema arm, like any string. After: in a program that loads @object-ui/plugin-dashboard's typings it is a declared node, checked against DashboardMetricNodeSchema (which extends BaseSchema, so a misspelled key still compiles until objectui#8347); in one that does not, it is refused as undeclared.
  • Narrowing on type. 'app' narrows to PageDocumentNode (the stored page kind) and 'list' to ListSchema. PageDocumentNode excludes the interface-mode list kind, which PageView renders through InterfaceListPage and never hands to SchemaRenderer. SchemaByType reads AnySchema and does not move.

The merge of main

  • Measured first with git merge-tree against main at 160c6c6e: one conflict, packages/types/src/__tests__/zod-mirror-parity.test.ts, as expected. content/docs/api/schema-reference.md, packages/core/src/builder/schema-builder.ts, packages/plugin-detail/README.md and packages/types/src/index.ts auto-merged. The merge is the merge commit c7c37b73; no rebase, no force-push.

  • The resolution is the union, re-derived on today's main. WiderThanDeclared holds main's objectui#11515 entry (app.zod.ts#AppSchemaRendererNodeSchema::schema, one SCHEMA-NODE arm) and this branch's two objectui#11466 entries (complex.zod.ts#DashboardComponentSchema::widgets and complex.zod.ts#DashboardWidgetSchema::component, two CONCRETE arms each). The header bullet reads the union's figures, with both histories in landing order; the file's own pin 'the WIDER ledger's header figures are derived from its ARMS' re-derives them from WIDER_ARMS and the mirrors, and the type-level ratchet confirms each entry against the mirrors (types tsconfig.test.json and vitest, both green). objectui#11514's KnownDrift move (type left complex.zod.ts#DashboardWidgetSchema's entry) merged without conflict.

  • MeasuredArmDrift11466 (node-recursion-point-8344.test.ts) drops detail-section, app-schema-renderer and cloud:plan-status, which objectui#11515 twinned and joined to AnySchema. On the merged tree before that edit the two-way ratchet read red (TS2344 at NodeRecursionPointDeclarationDrift), which is what the ratchet is for; a dated note records the reconciliation.

  • Landing-prep round (director seat, claim 5970151706; dev report 5970915352): two more merge commits, both conflict-free, with pnpm install leaving the lockfile unchanged.

    main brought no new V1 producer.

H1: the cycle

  • Before, with the V1 candidate on the first base (tsc -p packages/types/tsconfig.json), there were 9 errors: TS2456 on FlexBlockNode, AuthoringNode, DeclaredNode and SchemaNode; TS4109 in zod/base.zod.ts; TS7022 on FlexSchema, FLEX_BAG_CHILDREN, FlexPropsBag and FlexBlockSchema.
  • The spelling: FlexBlockNode is an interface that extends the closed arm input of FlexBlockSchema, with no members of its own; zod/base.zod.ts writes the node slot's zod type out through two type aliases (SchemaNodeZodType, NodeSlotZodType). The schema objects do not change.
  • After: 0 errors in tsconfig.json and in tsconfig.examples.json, on the merged tree as well.

H3: premise ②, re-run on the merged tree

Throwaway probe files, never committed: 40 type-level rows in @object-ui/types and 15 in @object-ui/react, each labelled OK (must compile) or REFUSE (must error), each run on two legs at b13093d6.

  • objectui#8347's P1 mutation (BaseSchema's index signature deleted; restore proven by blob c0d9ad44 equal before and after, signature lines back to 2): types 40 of 40 as labelled, react 15 of 15, with no error on an unlabelled probe line. So (a) a nested misspelling is refused at every slot depth and at the prop, and (b) a registered custom type is authorable, its misspelling refused, and its keys do not escape onto a declared type.
  • Signature present (the control, and what lands now): types 29 of 40, react 9 of 15, the same rows as at the first fork. Every row that does not pass is a misspelling on a node type that extends BaseSchema, which is the signature objectui#8347 removes. Undeclared types, wrong-value keys, the tombstone, AuthoringNode bag misspellings and unregistered custom types are refused on both legs.
  • (c) timing, main at 160c6c6e against this branch at b13093d6. Instantiations are deterministic (one value across 3 runs each); seconds are medians of 3 on a shared box, so read them as ratios:
program instantiations (main → V1) check time (median)
types tsconfig.json 1,377,922 → 1,381,453 (+0.26%) 3.21 s → 3.05 s
types tsconfig.examples.json 1,191,109 → 1,195,701 (+0.39%) 2.97 s → 3.58 s
types tsconfig.test.json 3,395,239 → 3,409,600 (+0.42%) 13.24 s → 13.43 s
react tsconfig.json 367,923 → 454,978 (+23.66%) 1.66 s → 2.02 s
react tsconfig.test.json 467,312 → 554,995 (+18.76%) 3.02 s → 3.47 s
components tsconfig.json 853,561 → 913,643 (+7.04%) 5.53 s → 4.80 s
components tsconfig.test.json 1,655,321 → 1,753,308 (+5.92%) 10.56 s → 9.29 s

The first fork read +23.95% (react src), +19.01% (react tests) and +6.48% / +5.83% (components). Memory medians moved between 0.0% and +6.7%.

H2: the census

The instrument is objectui#11479's census, copied: all 88 workspace type-check programs, each run on its own, after a full build. BaseSchema's index signature is present on every reading.

  • On the merged tree, before any other edit (c7c37b73): 9 lines in 3 of 88 programs. plugin-dashboard tsc --noEmit 4 and tsconfig.test.json 4 (the four SchemaRenderer render sites, two per surface, fed by the two casts); types tsconfig.test.json 1 (MeasuredArmDrift11466).
  • At f90ba8d2 (the previous round): 2 lines in 2 of 88 programs, both the return of DashboardRenderer's filter merge, whose object-metric member wrote a flat filter onto an object-metric node. Those programs read a plugin-dashboard dist built with a census-only cast at that line, restored blob-equal before the census.
  • At cb687dfad (this head, after the landing-prep merges): 0 lines across all 88 programs, the same at 5afa0269a. BaseSchema's index signature is present (2 lines in src, 2 in dist), and this round adds no cast.
  • At fab635a2 (the previous head): 0 lines; all 88 programs exit 0. No census-only cast: plugin-dashboard's own build, which fails on a type error in its declaration program, passes.

The corpus, re-judged

  • Type pins restated: SchemaRenderer.propsResolution (the prop equality, and BaseSchema is not an arm); node-recursion-point-8344 (the measured set of zod arms no declared node type admits, now app, record, home, utility, page, dashboard and the retired kanban arm); overlay-trigger-union-7081, overlay-node-slot-doc-types-7082 and page-body-arity-8310; zod-mirror-parity's Wider ledger (the union above); authoring-nodes-11364 (dated note); new node-slot-union-11466.
  • Test fixtures: react suites that register test-only types declare them in CustomNodeRegistry (13 files); deliberately undeclared input crosses undeclaredNode (15 files); stored page fixtures gained the required label; the plugin-gantt and plugin-map bag fixtures are annotated with their declared node types.
  • Docs: the plugin-dashboard README and content/docs/plugins/plugin-dashboard.mdx name the one retired envelope node and what still draws. 14 documents name the declared node type; a registered custom type is shown declared in CustomNodeRegistry (core/schema-renderer.mdx). The two blocks held at the first round (vscode-extension.mdx, the plugin-detail README) now compile, answered by their owner card (objectui#11515).
  • Pending changesets: 13 pre-existing entries carry 15 append-only dated notes (0 lines removed): 11364, 8331, 8908, schema-input-bridge-permanent-4622, 7767, 11479, 8572, 8310 (twice: the prop's annotation, and the root README's example authors children, not body), 11354, 10528-grid-object-table-fetch and 11514-dashboard-slot-entry-types (their "unchanged" / "draws as before" readings of the grid's static pivot), and for the envelope retirement 11525-inline-metric-retired (its envelope object-metric "still receives the dashboard filter bar's values"), 11348-dashboard-widget-reads (the broadcast's guard over "the same three object-* types", now two) and 11514-dashboard-slot-entry-types again (an envelope "draws under its card heading as before" whatever it held). New entries: .changeset/11466-node-slot-union.md (types, react and core minor; components patch), .changeset/11466-dashboard-metric-node.md and .changeset/11466-envelope-object-metric-retired.md (plugin-dashboard minor each, the second stating before, now and the fix: bind a dataset). Each states its breaking meaning in the body, because objectui never declares major.

The envelope's object-metric (the maintainer's ruling A)

The previous round held one line: the filter broadcast kept object-metric in its filterable set for a node in a widget's legacy component envelope (objectui#11525 kept it), and wrote a flat filter that the declared object-metric node (the spec's properties bag, ObjectMetricBlockNode) refuses by name on both faces. The maintainer ruled A (comment 5966278275): that envelope node is the last inline metric form, retired as ruling C retired the rest.

  • Measured basis (from the report): an object-metric inside a component envelope appears in 1 file across objectui and objectstack, the objectui#11525 pin itself, against a control of 3 files holding an envelope of another type. Stored customer dashboards are unmeasured, the gap ruling C accepted.
  • The pin. inlineObjectMetricRetired-11525.test.tsx carries a dated note in its header. Its envelope case flips: on both surfaces, under both keys, the envelope node yields the placeholder object (checked by identity), the prompt is on screen, and no aggregate or find call is made. A new control keeps the broadcast on the envelope's object-chart and object-data-table, which still receive the filter bar's value.
  • Reverse verification, run on the committed fix (20fb2278) with a trap-restored mutation. isRetiredEnvelopeNode was forced false (anchor hit once, marker on disk 1, blob 71c7335c to c2706404). Expected direction: the four retired-envelope cases red and everything else green. Read: "Tests 4 failed | 30 passed (34)", exactly those four (both keys on both surfaces). Restore: blob 71c7335c equals HEAD, git diff HEAD 0 bytes, marker 0.

Gates

HEAD is cb687dfad (landing-prep round, report 5970915352). The readings below were taken at fab635a2 unless the report re-measured them at cb687dfad. Toolchain: the container's Node v22.22.0 (the merged lockfile carries objectui#11366's lowered jsdom floor). Heavy runs went through the shared verify lock.

  • type-check, all 88 workspace programs (the census): 88 exit 0, 0 lines.
  • vitest, run from the worktree root with repo-relative paths:
    • packages/plugin-dashboard/, every reader test of the dashboard README, guide and touched changesets, and the changeset-reading script suites, at fab635a2: 182 files passed (2,325 tests, 6 skipped).
    • packages/types/, packages/react/, packages/core/src/builder/, every reader test of a doc, README or changeset the earlier rounds touched, and the changeset suites, at f90ba8d2, before the last round, whose diff touches plugin-dashboard's sources, its README and guide, and changesets, all re-read by the run above: 542 files and 14,116 tests passed.
  • Doc gates: check:doc-snippets exit 0, "777 of 777 block(s) judged, 0 failed"; check:doc-examples exit 0; check:doc-types exit 0.
  • check-changeset-no-major 0, check-changeset-presence 0 (3 changesets declared), check-changeset-fixed 0, check:pending-changeset-literals 0, check-changeset-overwrite 0 (report-only), check:changeset-claims 0 (report-only), check:new-line-citations 0 (0 new), check:control-bytes 0, check:component-surface-parity 0.
  • eslint, narrowed to each round's changed .ts/.tsx files: 0 errors, and the same warning count per file as before the change (the last round's four files against f90ba8d2; the previous round's six against main and ff7f748e). eslint.config.js sets no parser project, so linting is not type-aware and these diffs cannot move the verdict on an untouched file. The repo-wide lint is CI's.
  • Not measured: check:sdui-registration-pins, which needs a console build (exit 2, prerequisite); no registration input moved.

Acceptance notes

  • Corrected (landing-prep round, report 5970915352): the flex bag's child list is unknown[] | SchemaNode on the TS face. A SINGLE child is checked, and under the objectui#8347 mutation a misspelled key on it is refused (the earlier f03). An ARRAY of children is unknown[] and unchecked (probe f02 compiles under the mutation, at cb687dfad and at fab635a2). That comes from FLEX_BAG_CHILDREN's z.array(z.unknown()) arm, which is on main and untouched here. With the signature present, no reading moves today. The arm is carried by objectui#11564, ordered before objectui#8347's removal PR (seat ruling A on the report's open question).
  • Premise H3, re-measured at cb687dfad: under the P1 mutation, types reads 39/40 (f02, the array arm above) and react 15/15. The control leg reads 29/40 and 9/15.
  • DeclaredNode has same-type pairs that are intended: flex (FlexSchema and FlexBlockNode) and the object-* renderer faces next to their authored bag nodes.
  • The static pivot's runtime move: an option key it does not state (a host prop of PivotTable's such as rowLabels, or a BaseSchema node key other than className such as id, style or hidden) no longer reaches the node. Each stated key draws as before; DashboardGridLayout.objectProviderFetch-10528 and DashboardGridLayout.datasetPath pass.
  • DashboardGridLayout.tsx keeps twelve stale cross-file line addresses, none in a paragraph this PR edits, so they are listed on the card for a later edit of that file rather than repaired here: four in the dataSource prop's docblock (into DashboardRenderer, DatasetWidget, and SchemaRenderer twice) and eight in the render-site comments (five into DashboardRenderer, three into legacyRetiredWidget).

Generated by Claude Code

claude added 10 commits October 2, 2026 19:39
… the declared-node union (objectui#11466)

`SchemaNode`'s object arm is `DeclaredNode`: the discriminated union, keyed
by the literal `type`, of the component schemas `AnySchema` declares (without
its `BaseSchema` arm, whose `type` is `string`), the spec-declared
`AuthoringNode`s and the types an application declares in the new
`CustomNodeRegistry` interface. `SchemaRendererProps.schema` takes the same
type beside a string. No `type: string` arm, no index signature.

The cycle: `SchemaNode` names `AuthoringNode`, whose `FlexBlockNode` is the
input of `FlexBlockSchema`, whose bag names `SchemaNodeSchema`, whose type
names `SchemaNode`. `FlexBlockNode` becomes an interface (members resolved
lazily), and `zod/base.zod.ts` writes the node slot's zod type out through
type aliases. Typing only; no schema object moves.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
…ui#11466)

`AppComponentSchema` (the app.json document, read structurally by
`AppSchemaRenderer`, never dispatched by `ComponentRegistry`) is not a node,
so `DeclaredNode` leaves it out: the registry's `app` key serves the page
kind. `PageDocumentNode` admits every page kind but the interface-mode
`list`, which `PageView` renders through `InterfaceListPage` and never hands
to `SchemaRenderer`; the registry's `list` key is the `list` node's.

Narrowing a `DeclaredNode` on `type === 'app'` now gives `PageDocumentNode`
alone (was `AppComponentSchema | PageDocumentNode`), and on `'list'` gives
`ListSchema` alone (was `ListSchema | PageDocumentNode`). `SchemaByType`
reads `AnySchema` and does not move.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
…fter validateTree; the builder's node slots take DeclaredNode (objectui#11466)

- `kind: 'html'` pages: the runtime-parsed `SchemaElement` tree becomes a
  `DeclaredNode` at ONE documented boundary, reached only when `compile`'s
  `validateTree` reported no error (seat answer Q2 A on objectui#11479).
- `@object-ui/core`'s schema builder: `.child()` / `.children()` (grid,
  flex) and the card's `.content()` take `DeclaredNode` instead of
  `BaseSchema`, so a child passed in is checked against its own type.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
…input crosses one helper, and the old-shape pins state the declared-node union (objectui#11466)

- `@object-ui/test-support` gains `undeclaredNode`, THE one crossing for
  deliberately undeclared node input in a test: unknown or retired types,
  refused spellings (`props` / `visibility` on `element:text`, a node-level
  `params`, a retired `drillDown`), the three runtime-flat action suites,
  a host slot merged into a node, bare `{ type }` smoke nodes.
- Types a test registers with `ComponentRegistry` are declared in
  `CustomNodeRegistry`, as an application would (react: test-widget,
  test-debug-div, crashing-widget / stable-widget, test-component,
  probe-7088, test-4548-div, identity-probe / self-updating-probe,
  test:probe-4795, valid-host / host-wrap, test-ctx-consumer, probe-3955,
  probe-7530, test:schema-input-bridge-8908).
- Fixtures that are declared nodes name their type: plugin-gantt and
  plugin-map's propertiesBag fixtures (bag node and TS twin), the four
  `home` page documents (now with the spec-required `label`).
- Pins: react propsResolution and schema-input.bridgeFidelity state
  `DeclaredNode`; types overlay-trigger-union-7081 likewise;
  node-recursion-point-8344 states the measured exclusion set the
  `BaseSchema` arm used to hide; zod-mirror-parity records the two Wider
  rows the `component` slot's `BaseSchema` fallback produces (objectui#8347's).
- New pin `node-slot-union-11466.test.ts`: a nested misspelling on a
  closed node is refused with the signature present, no `type: string`
  arm, the `app` / `list` arms, registry entries join under their key.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
…is declared in CustomNodeRegistry (objectui#11466)

- Examples that held a node as `BaseSchema` or as an unannotated literal
  name `DeclaredNode` (the schema-rendering, expressions, architecture,
  data-source and runner guides; the root, components, data-objectstack
  and react READMEs; the blocks index).
- `schema-renderer.mdx`'s custom widget declares `MyWidgetSchema` in
  `CustomNodeRegistry` beside its `ComponentRegistry.register` call.
- `data-source.md` teaches a node that reads `objectName`
  (`object-grid`, its props in the bag) instead of `table`.
- `plugin-detail`'s related-list example writes its props in the
  `properties` bag; the types README's `data-table` carries its required
  `columns`.
- The schema reference, the types README and the react README describe
  `SchemaNode`, `DeclaredNode` and `CustomNodeRegistry` as they now are.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
…tries its change falsifies

One changeset (types, react, core minor with the breaking meaning stated;
components patch). Dated, append-only notes on the pending entries whose
standing reading of `SchemaNode`, the prop union, the bridge's types or the
node-recursion pin this change makes false: 11364, 8331, 8908, 4622,
7767, 11479, 8572.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
…e`; two source pins and the schema reference follow (objectui#11466)

- `DeclaredNode` excludes the app-level document as
  `Exclude<AnySchema, { type: 'app' }>` rather than by name, so `base.ts`
  does not name that interface (the `AppComponentSchema.actions[]` reader
  census in handler-keys-string-any-mirrors-7344 keys on the name).
- page-body-arity-8310 reads the root README's typed example
  (`const schema: DeclaredNode = {`); overlay-node-slot-doc-types-7082
  reads `SchemaNode`'s new spelling in `base.ts`.
- The schema reference points at the SchemaRenderer page's registration
  walkthrough instead of a placeholder `my-widget` block.

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

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

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
…op as `BaseSchema` (objectui#11466)

8310-page-node-body-arity and 11354-plugin-form-literals each said, in the
present tense, that `SchemaRenderer`'s `schema` prop is typed `BaseSchema`.
After this change it is `DeclaredNode | string | null | undefined`. Each entry
gets an append-only dated note; no existing line moves.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
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

⚠️ 67 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/10337-widget-input-serializer-key-list.md

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

    Clause 2 of the WidgetInput docblock said sdui-parser's manifest serializer forwards six keys per input and listed them without of. manifestFromConfigs has forwarded of since objectui#8067, so a reader of the published declaration learned that of is not published when it is. The clause now reads "a fixed key list per input, of included since objectui#8067" and lists all of them, the uncounted wording ComponentInput's own docblock in base.ts already uses. The clause's conclusion is unchanged: none of the five tombstoned ComponentInput keys is on that list. Comment only, with no type or runtime change.

.changeset/10859-dashboard-node-keys.md

  • names widgetDispatch.ts → packages/plugin-dashboard/src/widgetDispatch.ts — edited by this change

    • dashboard-grid is unregistered (with plugin-dashboard:dashboard-grid). It was builder chrome published as a node key: objectui validate refused it at type, and nothing authored or emitted it. DashboardGridLayout stays a named export; mount it directly. - metric and metric-card register with skipFallback: true. Both dashboard surfaces (DashboardRenderer, DashboardGridLayout) now hand SchemaRenderer plugin-dashboard:metric / plugin-dashboard:metric-card through one table, DASHBOARD_NODE_TYPES in widgetDispatch.ts. That covers the static metric tile, the metric-card widget-slot entry and a metric / metric-card node in a widget's legacy component envelope. The dashboard WIDGET vocabulary is unchanged: widgets[].type: 'metric' and the 2026-08-14 metric-card slot entry author exactly as before. - dashboardComponents drops dashboard-grid, and its metric / metric-card entries are keyed plugin-dashboard:metric / plugin-dashboard:metric-card, the type each now serves, so iterating the map cannot re-create the retired bare keys. - A rendered KPI tile's data-obj-type reads plugin-dashboard:metric (or plugin-dashboard:metric-card) instead of the bare spelling.

.changeset/11068-grid-declared-keys.md

  • names schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    What was wrong. ObjectGridSchema declared eight keys the grid never read. An author who wrote description, emptyState, name, placeholder, rowSpecActions or bulkSpecActions on an object-grid got no type error, no validator refusal and no effect. The reference example in schema-reference.md taught description and showFilters as if they worked. It now authors only keys the grid reads.

.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/11483-metric-card-needs-value.md

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

    • What changed. DashboardWidgetTypeSchema is now the spec's families plus list and custom, so the slot reads a metric-card through its component arm alone. The TypeScript twin, DashboardWidgetTypeName, drops DashboardComponentWidgetType the same way. DASHBOARD_COMPONENT_WIDGET_TYPES still lists metric-card: it is the component arm's type, and only that. - What now refuses that did not. Directly in widgets[], both faces refuse a metric-card with no value. That holds whatever else the card carries: title, description, id and layout, a dataset with values, or its figure under options (options: { value }), which the tolerant face accepted. On its own, DashboardWidgetSchema (zod) refuses type: 'metric-card' at type. On the TypeScript face, 'metric-card' is no longer a DashboardWidgetTypeName, so assigning it to that type is a compile error. - Why no widget key stands in for value. This was measured through the real DashboardRenderer and DashboardGridLayout. A dataset makes the dashboard draw DatasetWidget in the card's place, by a rule that does not read the type; the card is never mounted. options is spread onto the node as literal props: a second spelling of value, not a binding. No other widget key reached the figure. - What does not move. On the TypeScript face, DashboardWidgetSchema['type'] still includes 'metric-card'. That interface is also the read type of every widgets[] entry, and the component arm stays assignable to it. So a metric-card literal with no value still compiles directly in widgets[], while the validator refuses it on both faces. A metric-card in a widget's legacy component envelope is judged as before: there, the tolerant face's BaseSchema fallback still accepts a card that fails the arm. Every metric-card in the schema catalog, the READMEs and the docs fences carries its value. The one docs example that did not, under "TypeScript Support" in content/docs/plugins/plugin-dashboard.mdx, is rewritten.

.changeset/11515-vscode-export-react-annotation.md

  • names SchemaRenderer.tsx → packages/react/src/SchemaRenderer.tsx — edited by this change

    The compile pin gains a leg against the real prop type, read from SchemaRenderer.tsx, with a positive control. The two documented copies of the preamble (DESIGN.md and the docs page) follow it. The docs page's sample keeps its h1 node: h1 is a declared type (HtmlElementSchema), and the annotated sample compiles both today and with objectui#11466 applied.

.changeset/3917-retire-action-condition-branch.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    • ActionCondition is removed from @object-ui/types (and from the barrel export). - ActionSchema.condition is retyped to the predicate the runtime actually honours: boolean | string | { dialect?: string; source: string } — the same three arms ActionRunner's own ActionDef.condition carries, and the same vocabulary visible and disabled use. - ActionConditionSchema is removed from @object-ui/types/zod (and from the zod barrel); the condition key now validates against that predicate union. - The two teaching sites (content/docs/core/enhanced-actions.mdx Conditional Execution, content/docs/api/schema-reference.md ActionSchema table) are rewritten to the live vocabulary: condition is a gate; a branch is expressed as separate actions with mutually exclusive conditions.

.changeset/5674-retire-plugin-component-input.md

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

    Migration. Replace PluginComponentInput with ComponentInput — they have named the identical declaration since objectui#4972 converged the plugin-scoped interface onto base.ts, so this is a rename with no shape change:

.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/5905-componentinput-inputtype-tombstone.md

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

    Stale wording corrected in the same pass, because this change falsifies it: base.ts and zod/base.zod.ts both said the fork was "recorded for a ruling; until then this stays a live, writable key", and widget.ts called it "the open fork". All three now record the ruling. A reader who greps the source instead of the card thread was meeting an open fork that no longer existed.

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

    Stale wording corrected in the same pass, because this change falsifies it: base.ts and zod/base.zod.ts both said the fork was "recorded for a ruling; until then this stays a live, writable key", and widget.ts called it "the open fork". All three now record the ruling. A reader who greps the source instead of the card thread was meeting an open fork that no longer existed.

.changeset/5905-componentinput-retire-constraint-keys.md

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

    The retirement kit: ?: never on the interface (packages/types/src/base.ts), so authoring one is a tsc error at the registration site; retirementTombstone() on the Zod mirror (packages/types/src/zod/base.zod.ts), so an authored value is REFUSED at parse time with code: 'invalid_type', the key named in the issue path, and the migration note as the message. Deleting the members outright was the option NOT taken: ComponentInputSchema is a non-strict z.object, which strips an undeclared key silently — one silent no-op traded for another. Pinned in packages/types/src/__tests__/component-input-retired-constraint-keys.test.ts.

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

    The retirement kit: ?: never on the interface (packages/types/src/base.ts), so authoring one is a tsc error at the registration site; retirementTombstone() on the Zod mirror (packages/types/src/zod/base.zod.ts), so an authored value is REFUSED at parse time with code: 'invalid_type', the key named in the issue path, and the migration note as the message. Deleting the members outright was the option NOT taken: ComponentInputSchema is a non-strict z.object, which strips an undeclared key silently — one silent no-op traded for another. Pinned in packages/types/src/__tests__/component-input-retired-constraint-keys.test.ts.

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

  • names packages/react/src/SchemaRenderer.tsx → packages/react/src/SchemaRenderer.tsx — edited by this change

    visibleWhen is not a per-block concern in this platform. It is enforced once, generically, in packages/react/src/SchemaRenderer.tsx: shouldHide tests visibleWhen ahead of the hoisted visible (objectui#5454), sets _hidden, and the _hidden early return fires before the registry dispatches. A block renderer cannot ignore the gate, because it never sees the node.

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

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

    Why the name had to move. The const declares exactly two keys — className and style, the CSS passthrough attributes a node exposes. The TypeScript StyleProps (base.ts) is the Tailwind-SCALE vocabulary: padding, margin, gap, backgroundColor, textColor, borderWidth, borderColor, borderRadius. Measured on this branch's base with an AST read of both files: 2 keys against 8, sharing ZERO keys. In this package the …Schema suffix otherwise means "runtime mirror of the like-named declaration", so the shared name asserted a mirror relationship that does not exist — and building objectui#5684's parity registry by name pairing duly put the two together and reported drift on a pair that has no counterpart at all.

  • 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/6067-component-meta-derive-from-canonical.md

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

    packages/core/src/registry/Registry.ts declared its own ComponentMeta: thirteen keys, of which nine were restated from @object-ui/types' base.ts, four were registry-only (tier, namespace, skipFallback, labelling), and tags / description were absent — although both are declared on the canonical type and on the ComponentMetaSchema zod mirror. Two of the three authorities agreed and the registration surface did not, so those two keys were unwritable at exactly the declaration most component registrations import. That is the same two-key delta objectui#5893 had just closed inside @object-ui/types, arriving a third time on a third declaration, and objectui#5671 had already made the identical move for the sibling type ComponentInput in this very file.

.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/6170-retire-timeline-dead-keys.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Also in this change: the in-repo example packages/types/examples/data-display-examples.json (its timeline node authored all three) is migrated to items / variant; the two content/docs/api/schema-reference.md snippets that authored events: [] now author items: []; and the plugin-timeline docs callout says retired rather than deprecated.

.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/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/6896-retire-chart-inline-data.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    ⚠️ One correction to the record the ruling rests on. The ruling states zero authorship of a populated series[].data outside tests across packages/ / apps/ / examples/. The re-measurement finds one such site inside those roots — packages/types/examples/data-display-examples.json (2 series) — plus four outside them, in documentation: content/docs/api/schema-reference.md (3) and content/docs/core/report-schema.mdx (1).

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

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — 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/6951-tree-view-data-retired.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    What was measured, on this branch's base. TreeViewSchema declared two spellings for its one inline-nodes slot — nodes (read second) and data (read third: boundData || schema.nodes || schema.data || [] at renderers/data-display/tree-view.tsx:105), both declared by objectui#6150. data had been REQUIRED until 777e5c6f4 (PR fix(types): tree-view mirror stops requiring the limb it reads third (group 2 of objectui#6939) #7533) made it optional, so this retirement starts from a declared-and-optional member on both faces. The in-repo corpus at the retirement: seven tree-view nodes under examples/schema-catalog and packages/types/examples plus one content/docs fence — six on nodes, two on data (packages/types/examples/data-display-examples.json and content/docs/api/schema-reference.md), both rewritten; no package source authored either spelling.

.changeset/7087-disabled-twin-symmetry.md

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

    Those 18 redeclarations are removed, on both faces. The interfaces inherit BaseSchema.disabled the way they always inherited visible; the zod mirrors inherit base.zod.ts's z.union([z.boolean(), z.string()]) through .extend()'s merged .shape, so there is no second spelling of the union to drift from — the route ChatbotSchema took in objectui#6169.

.changeset/7088-hidden-jsdoc-single-hide-path.md

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

    Why a comment was worth a changeset: the JSDoc is the authority a later docs correction is measured against, and this one nearly propagated. While splitting the schema-reference hidden row, a reader checked it against base.ts and almost "corrected" the table's "Inverse of visible" — the half that describes shipped behaviour — toward the declaration. That row is unchanged and stays. SchemaRenderer.hiddenVisibleSynonymy.test.tsx now pins the claim the comment makes: the two keys produce the same rendered output, and hidden: true leaves no node that could carry a visibility style.

  • names SchemaRenderer.hiddenVisibleSynonymy.test.tsx → packages/react/src/__tests__/SchemaRenderer.hiddenVisibleSynonymy.test.tsx — edited by this change

    Why a comment was worth a changeset: the JSDoc is the authority a later docs correction is measured against, and this one nearly propagated. While splitting the schema-reference hidden row, a reader checked it against base.ts and almost "corrected" the table's "Inverse of visible" — the half that describes shipped behaviour — toward the declaration. That row is unchanged and stays. SchemaRenderer.hiddenVisibleSynonymy.test.tsx now pins the claim the comment makes: the two keys produce the same rendered output, and hidden: true leaves no node that could carry a visibility style.

.changeset/7319-dead-schemarenderer-retired.md

  • names packages/react/src/SchemaRenderer.tsx → packages/react/src/SchemaRenderer.tsx — edited by this change

    packages/components/src/SchemaRenderer.tsx was a 28-line component carrying the same export name as the real renderer in packages/react/src/SchemaRenderer.tsx. Nothing reached it: it is absent from the package barrel, no file in the repo imports it by any form, and the package's exports map has no subpath that resolves to it.

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

.changeset/7415-action-type-input-rename.md

  • names SchemaRenderer.tsx → packages/react/src/SchemaRenderer.tsx — edited by this change

    Census of the authored corpus, measured before the rename, not assumed. Across 4,797 files in examples/, apps/console/, content/docs/ and packages/: zero authored nodes set the input. 517 JSON files (433 of them the schema catalog) contain 2,410 type-bearing nodes and no action:button / action:icon node at all — control: 127 plain button nodes on the same walk. 207 parsed fenced JSON blocks in md/mdx carry 5 action:button nodes, all of them { type, label, icon?, action } — the discriminator plus the action channel, none setting an execution type — control: 28 plain button nodes, and the count independently matches the corpus census already recorded in SchemaRenderer.tsx ("action:button (5 nodes)"). No docs page documents the input; no catalog entry uses the components. So the rename breaks no authored document in this repo.

.changeset/7493-componentinput-retire-label-defaultvalue-advanced.md

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

    The retirement kit: ?: never on ComponentInput (packages/types/src/base.ts), so authoring one is a tsc error at the registration site; retirementTombstone() on ComponentInputSchema (packages/types/src/zod/base.zod.ts), so an authored value is REFUSED at parse time with code: 'invalid_type', the key named in the issue path, and the migration note as the message (one string, both channels). Pinned in packages/types/src/__tests__/component-input-retired-keys-7493.test.ts, which also holds a tree-scoped absence census over every inputs: array under packages/** and apps/**.

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

    The retirement kit: ?: never on ComponentInput (packages/types/src/base.ts), so authoring one is a tsc error at the registration site; retirementTombstone() on ComponentInputSchema (packages/types/src/zod/base.zod.ts), so an authored value is REFUSED at parse time with code: 'invalid_type', the key named in the issue path, and the migration note as the message (one string, both channels). Pinned in packages/types/src/__tests__/component-input-retired-keys-7493.test.ts, which also holds a tree-scoped absence census over every inputs: array under packages/** and apps/**.

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

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

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

.changeset/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/7658-toc-code-span-anchor-parity.md

  • names content/docs/utilities/runner.mdx → content/docs/utilities/runner.mdx — edited by this change

    Seven live headings in this repo's own docs were affected (content/docs/utilities/cli.mdx, content/docs/utilities/runner.mdx, packages/cli/README.md). Pinned against the real render pipeline rather than a second derivation of the slug rules: the new test renders each heading through MarkdownImpl and compares extractToc's id to the id attribute rehype-slug actually emitted.

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

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Migration. Author boards in the plugin dialect — objectName + groupBy for an object-bound board, or columns[].cards[] with badges for a static one. Replace DeclarativeKanbanSchema imports with KanbanSchema (from @object-ui/types, or the Zod KanbanSchema from @object-ui/types/zod; @object-ui/plugin-kanban re-exports the same KanbanSchema type). Delete draggable (drag-and-drop is always on) and column color (style a lane through className). content/docs/api/schema-reference.md's kanban section now documents this dialect.

.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/7912-schema-renderer-datasource-contract.md

  • names packages/react/README.md → packages/react/README.md — edited by this change

    Five as any reads of this context in @object-ui/fields are gone — they were redundant the moment the seam became honest — and LookupField's local re-declaration of the imported context as a Context of any, which laundered its dataSource read while looking typed, is gone with them. Both directions of the contract are pinned against the real compiler in SchemaRendererContext.dataSourceType.pin.test.ts, and the card's planted documentation probe (a bare string in packages/react/README.md's provider example) now fails pnpm check:doc-snippets, where it used to exit 0 with zero diagnostics.

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

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

    The card that found this did not claim the ten were wrong. It asked whether the spelling was buying a temporal-dead-zone dodge, and that check is what shipped. Each of the ten was rewritten in place to const inner = ANGLE-BRACKETS(body); z.lazy(() => inner), the package rebuilt, and the built barrel imported in a fresh process. Eight refuse to load. Seven name the very const being declared (children: z.array(TreeNodeSchema) sits inside TreeNodeSchema's own initialiser); SchemaNodeSchema names BaseSchemaCore, which base.zod.ts declares below it. For those eight the z.lazy is load-bearing, so ActionSchema, AppMenuItemSchema, FilterGroupSchema, MenuItemSchema, NavLinkSchema, NavigationMenuItemSchema, SchemaNodeSchema and TreeNodeSchema keep the spelling they have. The two that loaded clean are memoised: FilterBuilderConditionSchema is not recursive at all, and NavigationItemSchema already defers its self-reference through an inner z.lazy(() => NavigationItemSchema) on children.

.changeset/7926-page-node-refuses-actions.md

  • names packages/components/src/renderers/layout/page.tsx → packages/components/src/renderers/layout/page.tsx — edited by this change

    Accept-set change, deliberately. A page document carrying actions used to parse GREEN and render nothing. PageNodeSchema never declared the key and PageRenderer never read it — git grep -ni action on packages/components/src/renderers/layout/page.tsx returns only the PageVariableActionBridge import and its render — so the array survived purely through BaseSchema's .passthrough(). Measured through the real SchemaRenderer: a page node with actions: [{type:'button',label:'Add Product'}, …] drew 0 buttons and the label appeared nowhere in the DOM, while the SAME two buttons in the page's child list drew 2. Until objectui#7933 the array also reached the wrapper element as actions="[object Object],[object Object]".

.changeset/7997-detail-view-related-retired.md

  • names packages/plugin-detail/README.md → packages/plugin-detail/README.md — edited by this change

    Documentation. packages/plugin-detail/README.md and content/docs/api/schema-reference.md stop teaching the retired array and gain a migration block each.

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Documentation. packages/plugin-detail/README.md and content/docs/api/schema-reference.md stop teaching the retired array and gain a migration block each.

.changeset/8114-detail-tab-activity-timeline.md

  • names plugin-dashboard/README.md → packages/plugin-dashboard/README.md — edited by this change

    README.md ships in this package's files, so the example went out in every tarball. DetailTabs renders a tab's content through ANGLE-BRACKETS(SchemaRenderer schema={toRenderableSchema(tab.content)} /), which makes content.type an SDUI node position judged by the component registry — so a reader copying the tab got the registry's Unknown component type panel (OBJUI-001) where the timeline should be. Same shape as the line-chart widget in plugin-dashboard/README.md (objectui#7896's census; fixed by objectui#7951) and the fourth known instance.

.changeset/8268-testid-emitted-as-data-testid.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    The other direction — retiring the promise — was considered and declined. It is what c1fe272ad did for BaseSchema.hidden, but that key had a working behaviour to describe and zero named consumers, and the ruling's decline turned on exactly that. This promise already has carriers outside the type declaration: content/docs/api/schema-reference.md states it as a table row and authors testId in that page's own base-schema example, @object-ui/cli's OBJECTUI_STRUCTURAL_KEYS identifies a file as an ObjectUI schema node by this key, at this change ObjectGridSlotKey / ObjectFormSlotKey pin it, SchemaBuilder.testId() writes it, and ADR-0054 C4 — shipped — reads "the renderer emits data-testid … derived from metadata".

.changeset/8284-content-channel-per-component.md

  • names packages/react/README.md → packages/react/README.md — edited by this change

    Migration. Nothing that renders today stops rendering: a document authoring the channel its renderer reads is unchanged, and a document authoring the other one rendered an empty element before and is now refused instead. The repo-wide census found five documents in this state — packages/react/README.md, content/docs/guide/expressions.md, two blocks in content/docs/guide/schema-rendering.md and packages/components/TESTING.md — every one of them a form or container authoring body; all five are corrected in this change. If your own metadata authors the refused channel on one of these twelve node types, the component was already drawing nothing there; rename the key to the one in the table.

  • names content/docs/guide/expressions.md → content/docs/guide/expressions.md — edited by this change

    Migration. Nothing that renders today stops rendering: a document authoring the channel its renderer reads is unchanged, and a document authoring the other one rendered an empty element before and is now refused instead. The repo-wide census found five documents in this state — packages/react/README.md, content/docs/guide/expressions.md, two blocks in content/docs/guide/schema-rendering.md and packages/components/TESTING.md — every one of them a form or container authoring body; all five are corrected in this change. If your own metadata authors the refused channel on one of these twelve node types, the component was already drawing nothing there; rename the key to the one in the table.

  • names content/docs/guide/schema-rendering.md → content/docs/guide/schema-rendering.md — edited by this change

    Migration. Nothing that renders today stops rendering: a document authoring the channel its renderer reads is unchanged, and a document authoring the other one rendered an empty element before and is now refused instead. The repo-wide census found five documents in this state — packages/react/README.md, content/docs/guide/expressions.md, two blocks in content/docs/guide/schema-rendering.md and packages/components/TESTING.md — every one of them a form or container authoring body; all five are corrected in this change. If your own metadata authors the refused channel on one of these twelve node types, the component was already drawing nothing there; rename the key to the one in the table.

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

    ⛔ It is NOT closed here, and the reason is measured rather than argued. The route that closes it by binding the union inside SchemaNodeSchema's z.lazy getter was implemented and pushed, and CI refused it: Build Docs failed with ReferenceError: Cannot access 'BaseSchema' before initialization out of packages/types/dist/zod/app.zod.js, because that import makes base.zod.ts depend on the barrel and a bundler is free to evaluate the resulting cycle category-module-first. Reproduced locally in one line — importing dist/zod/app.zod.js throws with the binding in place and loads clean without it. The other three candidates were measured too: a narrowed sideEffects array is not a legal declaration for this package (one gate requires every entry form to be named, another refuses a named entry with no load-time effect, and this package's entry forms are pure), a bare top-level call is dropped by the same flag, and dropping the flag costs 16,078 gzipped bytes on the console framework chunk and moves a workspace census a guard pins.

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

  • names content/docs/utilities/runner.mdx → content/docs/utilities/runner.mdx — edited by this change

    The defect, and the direction it ran. Nine type spellings sat at DECLARED node slots in this repository's own corpora and resolved in no arm of the component union. Eight were registered renderers with fixtures proving they draw; the ninth (my-component) is the reader's own plugin component and carries a written exemption in scripts/check-doc-component-types.mjs. A reader following content/docs/utilities/runner.mdx's own instruction — "copy one, wrap it in a page document … and save it as src/app-data/pages/index.json" — got a document that renders correctly in the browser and is refused by objectui validate. That is the expensive direction: the likely reaction is to stop trusting the validator.

.changeset/8568-retire-dollar-dialect-lowercase-aliases.md

  • names packages/data-objectstack/README.md → packages/data-objectstack/README.md — edited by this change

    packages/data-objectstack/README.md's operator tables follow the implementation, as does the reconciliation test that holds them to it.

.changeset/8653-listview-title-retired-rowactiondefs-pinned.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    title — retired. ListView resolved its export filename through schema.label || (schema as any).title. @objectstack/spec/ui's ListViewSchema refuses title by name (unrecognized_keys: ['title']) while ObjectGridPropsSchema accepts it; packages/types mirrors the platform contract rather than ruling over it, so declaring title on ListViewSchema would have made this repo accept what the platform save gate rejects. That asymmetry is also why objectui#6639 could take the declare branch for ObjectGridSchema.title one package over and this site could not. A parse-based census of apps/ examples/ content/ and packages/ found zero list-view nodes authoring title, so the retirement costs no author a filename. Over that same corpus the instrument reports three object-grid nodes carrying the key: two authored ones, both in content/docs/api/schema-reference.md, plus one that is not authored at all — packages/plugin-view/src/ObjectView.tsx composes title: schema.table?.title onto a grid node it builds, so it is a producer writing the key rather than an author declaring it. ObjectGrid's own title reads are untouched — they remain declared, ruled and read.

.changeset/8760-unfulfilled-chart-stubs.md

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

    • At render. SchemaRenderer's lazy branch re-checks hasLazy(type) on every pass and returns the Loading ANGLE-BRACKETS(type)… placeholder. Registry.register() deletes a lazy entry only for keys the loaded module actually registers, so for an unfulfilled key the entry SURVIVES the load and every later pass takes the same branch. Measured on b775500af through the real chain: { "type": "line-chart" } painted role="status" / data-lazy-loading="line-chart" / Loading line-chart…, permanently. Not the OBJUI-001 panel the card expected — no alert, no error, no console warning. A skeleton that never resolves reads to a user as a slow network. - At authoring. A stub is enough to put a key into getKnownTypes(), so check:doc-types and the CLI's generated KNOWN_SCHEMA_TYPES snapshot both blessed all three. content/docs/plugins/plugin-dashboard.mdx taught "type": "line-chart" inside a card body, and every gate was green on it.

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

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — 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 packages/types/src/base.ts → packages/types/src/base.ts — edited by this change

    Accept-set change, deliberately. A page document carrying breadcrumbs used to parse GREEN and render nothing. PageNodeSchema never declared the key and no renderer ever read it, so the array survived purely through BaseSchema's .passthrough(). On the TypeScript face, tsc previously accepted it too, through BaseSchema's own [key: string]: any index signature (the [key: string]: any member of BaseSchema in packages/types/src/base.ts) — the same open door the zod mirror walked through at runtime.

.changeset/9162-node-slot-guard-class-closure.md

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

    What leaked. {schema.footer && ANGLE-BRACKETS(CardFooter)…ANGLE-BRACKETS(/CardFooter)} does not evaluate to false when the slot is falsy — it evaluates to the slot, and React renders numbers. A node slot's published zod face carries a z.number() arm (nodeUnionOptions, packages/types/src/zod/base.zod.ts), so footer: 0 is legal authored input, and it painted a stray 0 into the DOM. NaN painted three characters. The && also short-circuits, so renderChildren's own if (!children) return null first leg was never reached — which is why the objectui#8908 bridge repair could not cover any of these sites.

.changeset/9187-record-highlights-layout-two-values.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    ⚠️ The census behind this narrowing covers this repository only, and it found no in-repo authoring to migrate: every in-tree layout: 'grid' belongs to a different component (detail-view in content/docs/api/schema-reference.md and phase2-schemas.test.ts, ai-recommendations in packages/plugin-ai/README.md), and the one in-repo consumer of this interface that writes a layout (p1-spec-alignment.test.ts) writes 'horizontal'. So no document in this repository stops type-checking. A TypeScript consumer outside this repo that wrote grid is not observable from here and gets a compile error (TS2322) naming the key — which is why the FROM/TO is spelled out above.

.changeset/9308-data-root-unbound-from-adapter.md

  • names content/docs/guide/schema-rendering.md → content/docs/guide/schema-rendering.md — edited by this change

    The Data Context passages of content/docs/guide/schema-rendering.md and packages/react/README.md teach the scope channel accordingly.

  • names packages/react/README.md → packages/react/README.md — edited by this change

    The Data Context passages of content/docs/guide/schema-rendering.md and packages/react/README.md teach the scope channel accordingly.

.changeset/9365-bridgelistview-present-tense.md

.changeset/9375-base-i18n-vocabulary-docblock.md

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

    base.ts said of the INLINE locale map (label / description) and the KEYED bundle reference (ariaLabel) that they "each accept the other's shape vacuously", and the label docblock said the same thing one property over ("a keyed ref typed into this slot is accepted only vacuously"). That was true when objectui#4580 Q2-B wrote it; the installed pin has since closed it, and the sentence now reads as live in published .d.ts bytes while the tree says the opposite.

.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/9659-node-recursion-point-inert-clause.md

.changeset/9959-refused-data-prop-warn-reset.md

  • names SchemaRenderer.tsx → packages/react/src/SchemaRenderer.tsx — edited by this change

    The dedupe Set behind the objectui#9571 / objectui#9758 diagnostic — "the authored data key was NOT passed to the component as a React prop" — lived in SchemaRenderer.tsx with nothing able to clear it, while every other dev diagnostic in this package's utils/ directory pairs its Set with an exported test-only reset. propsBagDiagnostic.ts states the consequence in its own reset's docblock: the second test to assert the same warning reads the first test's dedupe entry and sees silence, "a green run that checked nothing". The absence direction is the dangerous one — a test asserting this warning did NOT fire for a type#id an earlier test already warned for is green whatever the renderer does, and it reports that green as coverage.

.changeset/calendar-view-schema-converge.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Runtime renderer behaviour is unchanged. @object-ui/plugin-calendar's README and content/docs/api/schema-reference.md are repaired to the converged surface in the same change, so no copy of the old contradiction survives.

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

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

    @object-ui/types published ComponentMeta twice, from two different declarations: base.ts and plugin-scope.ts (the latter published as PluginComponentMeta). They were structural copies, not an alias pair. plugin-scope.ts' ComponentMeta is now export type { ComponentMeta } from './base.js' — the disposition objectui#4580 ruled for the identical shape, a structural copy would reproduce the defect the moment either side moved, and the same move objectui#5671 made for the sibling type ComponentInput in the same file.

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

    Either side had already moved. base.ts declared eleven keys; the plugin-scoped copy declared nine — the same nine, minus tags and description. So a plugin author typing against the plugin-facing declaration could not write two keys the main surface advertises, and which the runtime validator already accepted: ComponentMetaSchema in zod/base.zod.ts declares all eleven, so two of the three authorities agreed and the plugin-facing one did not. resizeConstraints' six members were identical in both, so the delta was exactly those two keys.

  • 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/componentinput-reexport-4972.md

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

    @object-ui/core's ComponentInput (registry/Registry.ts) and @object-ui/types' plugin-scoped ComponentInput (plugin-scope.ts, published as PluginComponentInput) were structural copies of the interface in @object-ui/types' base.ts. Both are now re-exports of that one declaration, which is the disposition objectui#4580 ruled for the identical shape — a structural copy would reproduce the defect the moment either side moved — and the way core/src/types/index.ts already handles SchemaNode.

.changeset/default-children-retired-5051.md

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

    If you author plugins against the published register-meta table, drop the key. It is gone from skills/objectui/guides/plugin-development.md, which had been teaching it. A meta that still declares it stays valid: ComponentMetaSchema is a plain z.object, and measured on zod 4.4.3 that STRIPS unknown keys rather than rejecting them — so the key is silently dropped from the parse output instead of failing validation. TypeScript authors get the loud signal instead: all three ComponentMeta declarations (@object-ui/types base.ts and plugin-scope.ts, @object-ui/core Registry.ts) no longer offer it, so re-declaring it is now a compile error.

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

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

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

  • names api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Authoring crud is now REFUSED BY NAME rather than passed or silently ignored. validateSchema returns an error with code: 'RETIRED_TYPE' on schema.type — at any depth, since it is what validateChildren recurses with — so assertValidSchema throws and isValidSchema answers false. The message names the migration: object-grid for the record table with its toolbar, filters, pagination and row/batch actions, object-form for the create/edit form, and detail for the record view. api/schema-reference.md is rewritten around those shapes.

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

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    listViews stays unmirrored, on the ruling's own fallback clause. The declaration's value is the local NamedListView — 64 declared top-level members (⚠️ re-taken at objectui#8980, which added the seventeen the protocol declares on this surface to the 47 this entry first measured), of which the renderer reads 21 off a named view. data is one of the 21 now: it used to reach the renderer through an as any cast on the named-view config in packages/plugin-view/src/ObjectView.tsx and be declared nowhere, and the objectui#8980 ruling declared it by name — objectui#7928's open half, answered. The spec's ViewSchema.listViews is a record of the STRICT ObjectListViewSchema, which requires columns and refuses options, ObjectQL tuple filters and default — that is, it refused the named views this package's own README and content/docs/api/schema-reference.md taught when this entry was written ({ label: 'All Users' } fails at columns; filter: [["owner", "=", "..."]] fails at filter.0), and objectui#8255 has since rewritten them in the spec shape. Mirroring the spec value would have lost documented behaviour; mirroring the local value would enforce 43 unread members (64 declared, minus the 21 that are both declared and read) into the contract — the very thing ruling B refused for the six local keys. The key therefore stays in the parity ledger with that measurement, pinned, until the maintainer decides its value type. It is not papered over with z.any().

  • 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/page-source-tailwind-prose-retraction-5469.md

  • names src/renderers/layout/page.tsx → packages/components/src/renderers/layout/page.tsx — edited by this change

    • @object-ui/react-runtime: README.md is published to npm (npm includes README.md in the tarball regardless of files). Its "no sandbox" callout is the paragraph that routes untrusted-author work to the html tier, and it carried the retracted framing line-wrapped across :17-18. It also gains the §Styling section it was missing — the absence is why the framing survived here. - @object-ui/sdui-parser: the corrected header of src/types.ts projects verbatim into the published dist/types.d.ts. - @object-ui/components: the corrected header of src/renderers/basic/html-elements.tsx projects verbatim into the published dist/renderers/basic/html-elements.d.ts. The kind === 'html' dispatch-arm comment in src/renderers/layout/page.tsx does not project (it is inside a function body) and is included here only because the same package already owes an entry.

.changeset/plugin-component-input-deprecate-5674.md

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

    @object-ui/types' entry point publishes ComponentInput as PluginComponentInput. Until objectui#4972 that alias pointed at a genuinely different declaration: plugin-scope.ts restated its own nine-key ComponentInput. objectui#5671 converged that declaration onto base.ts, so the alias became a second published name for the same type, carrying no information the first does not. Two published names for one type is a shape that costs readers a step and gives AI-authored code a coin-flip between spellings.

.changeset/plugin-detail-8937-parent-scope-residue.md

  • names packages/plugin-detail/README.md → packages/plugin-detail/README.md — edited by this change

    • packages/plugin-detail/README.md (it is in files[], so it ships). It said the node's filter is AND-combined with { [relationshipField]: parentId }, full stop. Since objectui#7299 the parent condition is compiled to match the relationship field's arity, so a multi-valued relationship gets { [relationshipField]: { $contains: parentId } } instead. The paragraph now states both spellings and names the arbiter (@objectstack/spec/data's isMultiValueField). - The claim that the SQL driver decides arity on that same predicate. It does not: driver-sql gates the equality family on its own storage question, which reads multiple as truthy on ANY type. The two rules therefore disagree for a type outside MULTI_CAPABLE_TYPES carrying multiple: true. objectui#9184 moved the arity compiler into @object-ui/core's parent-scope seam and carried the claim with it, so the correction is recorded there — the seam now states the driver's measured rule, records the divergence as a divergence, and points at the upstream card that owns which of the two rules is right (objectstack#17469). RelatedList.tsx's pointer comment and the objectui#7299 test header carried the same sentence and are corrected to match.

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

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    What makes this site different from its three siblings is that the alias was not merely tolerated, it was published. content/docs/api/schema-reference.md §TableSchema shipped a copyable { "name": "id", "label": "#" } example and a property row reading "Column definitions with name, label, …", while packages/types declared the opposite pair. Docs and type disagreed about one type they both call TableColumn, each internally consistent. Retiring the alias without correcting the page would have turned a documented, working example into a silently broken one, so both halves land together: the page now authors accessorKey/header. The same row also advertised a render property that TableColumn has never declared — the renderer's hook is cell — and that claim is dropped rather than re-spelled.

.changeset/zod-base-schema-mirror-parity-4605.md

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

    The new pin is DERIVED rather than a hand-written key list: it reads the mirror's own .shape and compares each key against the declaration, so the next widening of base.ts that forgets this file turns it red with no list to maintain. It reads .shape and not keyof z.inputANGLE-BRACKETS(…) because that spelling was measured vacuous — .passthrough() collapses the inferred key union to bare string, and a pin written over it resolved never while five keys were demonstrably narrow. Two guards pin the derivation against both degenerations (never and string).

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 806f32726 (merge-base with origin/main): 72 file(s) changed outside .changeset/, read against 2075 pending declaration(s) that publish a body (2708 pending in total). · run

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Console Performance Budget — not measured

This run did not produce a console bundle to measure, so there is no pass/fail verdict for the performance budget.

This is not a budget violation. Nothing was measured — the numbers a real violation would carry are simply absent.

Step Outcome
Build packages failure
Check console performance budget skipped

See the workflow run for details.

No package size report: it is only generated from a complete package build, so a partial one is never shown.

This was referenced Oct 2, 2026
…ctui#11466)

One textual conflict, packages/types/src/__tests__/zod-mirror-parity.test.ts,
resolved as the union of both sides' WiderThanDeclared rows: main's
objectui#11515 entry (app.zod.ts#AppSchemaRendererNodeSchema::schema,
one SCHEMA-NODE arm) and this branch's two objectui#11466 entries
(complex.zod.ts#DashboardComponentSchema::widgets and
complex.zod.ts#DashboardWidgetSchema::component, two CONCRETE arms each).
The header bullet reads the union's figures, with both histories in
landing order; the file's own header-figures pin re-derives them.

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

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Console Performance Budget — not measured

This run did not produce a console bundle to measure, so there is no pass/fail verdict for the performance budget.

This is not a budget violation. Nothing was measured — the numbers a real violation would carry are simply absent.

Step Outcome
Build packages failure
Check console performance budget skipped

See the workflow run for details.

No package size report: it is only generated from a complete package build, so a partial one is never shown.

…5 twinned (objectui#11466)

objectui#11515 gave detail-section, app-schema-renderer and
cloud:plan-status their TypeScript twins and joined them to AnySchema,
so their zod output is assignable to SchemaNode and the two-way ratchet
went red on the merged tree (TS2344 at NodeRecursionPointDeclarationDrift).
The three names leave the measured set; a dated note records why, and
the first measurement's bullet is kept as that change's reading.

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

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Console Performance Budget — not measured

This run did not produce a console bundle to measure, so there is no pass/fail verdict for the performance budget.

This is not a budget violation. Nothing was measured — the numbers a real violation would carry are simply absent.

Step Outcome
Build packages failure
Check console performance budget skipped

See the workflow run for details.

No package size report: it is only generated from a complete package build, so a partial one is never shown.

…NodeRegistry, the static pivot names PivotTableSchema, and the SchemaRenderer casts go (objectui#11466)

- widgetDispatch.ts: DASHBOARD_NODE_TYPES keeps literal values (a
  string-keyed view serves the by-type lookup), and
  DashboardMetricNodeSchema, typed by MetricWidget's props, is entered in
  @object-ui/types' CustomNodeRegistry under plugin-dashboard:metric
  (objectui#11479 Q1 A). The entry exports it so the published typings
  load the declaration.
- Both surfaces' getComponentSchema return SchemaRendererProps['schema'],
  so each node is checked against its declared type where it is built;
  the metric arms narrow widgetType for the required label.
- DashboardGridLayout's static pivot states the keys PivotTableSchema
  declares and PivotTable draws, read from options, instead of spreading
  options whole.
- The `as BaseSchema | string | null | undefined` cast in
  DashboardGridLayout and the `as BaseSchema` cast in DashboardRenderer
  are gone. No new cast.
- index.tsx: ObjectMetricBlock's docblock no longer names the dashboard
  grid path as a host (objectui#11525 retired it).

One line remains in DashboardRenderer: the filter broadcast's
object-metric member writes a flat `filter` onto an object-metric node,
which the declared node (the spec's bag form) refuses by name. Held for a
decision, reported on objectui#11466.

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

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Console Performance Budget — not measured

This run did not produce a console bundle to measure, so there is no pass/fail verdict for the performance budget.

This is not a budget violation. Nothing was measured — the numbers a real violation would carry are simply absent.

Step Outcome
Build packages failure
Check console performance budget skipped

See the workflow run for details.

No package size report: it is only generated from a complete package build, so a partial one is never shown.

…d dated notes on three pending entries (objectui#11466)

- New .changeset/11466-dashboard-metric-node.md (@object-ui/plugin-dashboard
  minor): DashboardMetricNodeSchema, the static pivot's stated keys and
  what no longer rides along, and the casts gone.
- Dated, append-only notes:
  - 8310-page-node-body-arity.md: the root README's Basic Usage example
    authors `children`, not `body` (already so at this branch's base);
  - 10528-grid-object-table-fetch.md and 11514-dashboard-slot-entry-types.md:
    their "unchanged" / "draws as before" readings for the grid's static
    pivot, whose node now states its keys.
- DashboardGridLayout.tsx: the pivot comment names the keys it states.

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

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Console Performance Budget — not measured

This run did not produce a console bundle to measure, so there is no pass/fail verdict for the performance budget.

This is not a budget violation. Nothing was measured — the numbers a real violation would carry are simply absent.

Step Outcome
Build packages failure
Check console performance budget skipped

See the workflow run for details.

No package size report: it is only generated from a complete package build, so a partial one is never shown.

claude added 2 commits October 3, 2026 06:20
…ope draws the retired-format placeholder, and leaves the filter broadcast (objectui#11466)

The maintainer's ruling A on objectui#11466 (comment 5966278275), which
extends ruling C on objectui#11525 to the last inline metric form.

- legacyRetiredWidget.ts: isRetiredEnvelopeNode, shared by both surfaces,
  answers an object-metric node (bare key or the registration's full
  name) read from a widget's legacy `component` envelope.
- DashboardRenderer and DashboardGridLayout return the imported
  LEGACY_RETIRED_WIDGET_SCHEMA for it, before the envelope pass-through.
- DashboardRenderer: object-metric leaves FilterableComponentType /
  FILTERABLE_COMPONENT_TYPES, so the flat-filter write the declared
  object-metric node refuses is gone with no cast. The docblock now names
  what the broadcast covers.
- inlineObjectMetricRetired-11525: the envelope case flips to the
  placeholder on both surfaces (dated note in the header), and a control
  keeps the envelope's object-chart and object-data-table receiving the
  filter bar's value.

Untouched: the envelope's other node types, the authored object-metric
block on pages, and every zod and TypeScript authoring face.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
…e README and guide, its changeset, and dated notes on three pending entries (objectui#11466)

- New .changeset/11466-envelope-object-metric-retired.md
  (@object-ui/plugin-dashboard minor): before, now, the fix (bind a
  dataset) and what does not move.
- README.md and content/docs/plugins/plugin-dashboard.mdx: the envelope
  paragraph names the one retired node and what still draws.
- Dated, append-only notes: 11525-inline-metric-retired.md (the envelope
  object-metric no longer receives the filter bar's values),
  11348-dashboard-widget-reads.md (the broadcast narrows over two types,
  not three), 11514-dashboard-slot-entry-types.md (an envelope's
  object-metric draws the placeholder).

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

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3551.1 KB 3574.6 KB
Main entry chunk (gzip) 150.3 KB 350 KB
Entry file index-CBl8n4YA.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) 571.09KB 136.86KB
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.51KB 66.22KB
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.47KB 11.25KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.17KB 15.46KB
plugin-charts (index.js) 83.60KB 22.89KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 142.83KB 38.55KB
plugin-designer (index.js) 231.28KB 48.76KB
plugin-detail (index.js) 245.43KB 64.53KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 172.73KB 44.50KB
plugin-gantt (index.js) 179.16KB 45.06KB
plugin-grid (index.js) 233.13KB 63.89KB
plugin-kanban (index.js) 49.59KB 15.58KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 38.80KB 11.71KB
plugin-tree (index.js) 14.51KB 5.15KB
plugin-view (index.js) 90.23KB 22.73KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.16KB 1.96KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.07KB 2.39KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet objectstack-fleet Bot changed the title feat(types,react)!: a node slot and SchemaRenderer's schema prop take the declared-node union, V1 (objectui#11466) feat(types,react,core,plugin-dashboard)!: a node slot and SchemaRenderer's schema prop take the declared-node union, V1; the dashboard producers name declared nodes and an envelope's object-metric retires (objectui#11466) Oct 3, 2026
…ctui#11466)

Brings in objectui#11533, #11531 (@objectstack/spec 17.6.0), #11547,
#11539, #11542, #11344 and #11068. No textual conflict.

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

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3308.4 KB 3330.4 KB
Main entry chunk (gzip) 150.3 KB 350 KB
Entry file index-BrOf2VzY.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) 572.97KB 137.34KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 231.08KB 64.07KB
fields (index.js) 261.62KB 66.20KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 35.66KB 9.49KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 39.47KB 11.25KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.17KB 15.46KB
plugin-charts (index.js) 83.60KB 22.89KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 142.83KB 38.55KB
plugin-designer (index.js) 231.28KB 48.76KB
plugin-detail (index.js) 245.43KB 64.53KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 172.96KB 44.52KB
plugin-gantt (index.js) 179.16KB 45.06KB
plugin-grid (index.js) 235.05KB 64.60KB
plugin-kanban (index.js) 49.59KB 15.58KB
plugin-list (index.js) 116.46KB 28.93KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 38.80KB 11.71KB
plugin-tree (index.js) 14.51KB 5.15KB
plugin-view (index.js) 90.23KB 22.73KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.16KB 1.96KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.07KB 2.39KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

…ctui#11466)

Brings in objectui#10813 (PR #11555), which landed after the previous
merge. No textual conflict.

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

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3308.7 KB 3330.4 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-Me_URvki.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) 572.97KB 137.34KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 232.57KB 64.51KB
fields (index.js) 261.62KB 66.20KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 35.66KB 9.49KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 39.47KB 11.25KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.17KB 15.46KB
plugin-charts (index.js) 83.60KB 22.89KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 142.83KB 38.55KB
plugin-designer (index.js) 231.28KB 48.76KB
plugin-detail (index.js) 245.59KB 64.56KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 172.96KB 44.52KB
plugin-gantt (index.js) 179.16KB 45.06KB
plugin-grid (index.js) 235.03KB 64.66KB
plugin-kanban (index.js) 49.59KB 15.58KB
plugin-list (index.js) 116.46KB 28.93KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 38.80KB 11.71KB
plugin-tree (index.js) 14.51KB 5.15KB
plugin-view (index.js) 90.23KB 22.73KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.16KB 1.96KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.07KB 2.39KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

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

What was read: card objectui#11466 (its body and all 19 comments), PR #11512 (its body, its 88-file list, and its net diff against the merge-base d0c0c7fe9: +1276 / -299, the same file headers as the REST diff), the head's 43 check-runs and the Skill Example Check job log, objectui#11525's ruling C (5964914121), objectui#11543 / PR #11558, objectui#11564, and the body and comments of objectui#8347. Nothing was built, run or re-run.

① Derived judgments

1. The narrowing (TypeScript face only). SchemaNode's object arm and the object member of SchemaRendererProps['schema'] are now DeclaredNode. It is the union of AnySchema's literal-typed arms (without the type: 'app' document), AuthoringNode, and one arm per CustomNodeRegistry entry. It has no type: string arm and no index signature. BaseSchema's own signature is untouched: base.ts still declares it, and the diff adds none.

  • Refused now, compiled before:
    • an undeclared type, nested or at the prop;
    • a value typed BaseSchema. The same holds for a value typed AnySchema, which carries the BaseSchema arm, and for an unannotated const whose type widens to string (71 of the first census's 155 test lines);
    • a missing required key of a declared type (a home page's label, a data-table's columns, a pivot's axes);
    • a wrong value type or a tombstoned key on a declared type. These are refused even with the signature present, because the signature does not override declared members (the first fork's control leg);
    • a misspelled key on a closed arm (an AuthoringNode, a closed registry entry), at any depth;
    • the app-level document at the prop, and a PageDocumentNode under type: 'list';
    • a type registered at runtime with ComponentRegistry but not declared in CustomNodeRegistry;
    • BaseSchema arguments to core's grid and flex .child() / .children(), to the card builder's .content(), and to toRenderableSchema.
  • Still accepted:
    • a string, null and undefined at the prop (number and boolean stay in SchemaNode only, as before);
    • every declared literal type, and every AuthoringNode;
    • a registered custom type once it is declared in CustomNodeRegistry;
    • a misspelled key on a type that extends BaseSchema, until objectui#8347 removes the signature.
  • Judged: this matches ruling ① exactly. It is pinned by node-slot-union-11466 (type-level; each directive holds both ways through TS2578) and by propsResolution assertions 1 and 1b. CI Type Check is green.

2. New public types and exports.

  • @object-ui/types gains DeclaredNode. Necessary: the object arm needs a name that consumers can annotate with (the READMEs, the migration), and that core's builder and page.tsx's boundary can use.

  • @object-ui/types gains CustomNodeRegistry. Necessary: it is premise ②(b)'s mechanism and the only route for a custom type. Each entry is intersected with { type: KEY }, so even a loose BaseSchema entry adds no type: string arm (pinned by _LooseEntryJoinsUnderItsKey). The helpers LiteralTypedArm and RegisteredCustomNode are not exported.

  • @object-ui/react and @object-ui/core: no new export. The prop and the builder parameters are retyped, and toRenderableSchema follows by reference.

  • @object-ui/plugin-dashboard gains DashboardMetricNodeSchema, plus a declare module '@object-ui/types' augmentation for plugin-dashboard:metric. Necessary:

    • the node both surfaces build must be declared before their casts can go (objectui#11479 Q1 A);
    • the type-only entry export is what loads the augmentation into consumer programs.

    It extends BaseSchema, so it keeps the signature until objectui#8347. Every program that imports the package gains this arm, which matches what the registry mounts.

  • @object-ui/test-support gains undeclaredNode. The package is private: true, so this is not public surface. It has 25 call sites in 15 test files and none under src/.

  • Two published types change shape, and the changeset states both. FlexBlockNode becomes an empty interface with the same members, and PageDocumentNode excludes list. Not named: an interface gets no implicit index signature, so a FlexBlockNode value no longer assigns to a string-keyed record type. The census shows no in-repo reader that this breaks.

3. The plugin-dashboard producers.

  • plugin-dashboard:metric is declared through CustomNodeRegistry. Both getComponentSchema builders return SchemaRendererProps['schema'], and both casts are gone. Under src/, the only cast to DeclaredNode is page.tsx's documented boundary. That boundary is reached only after the errors early return, and it renders exactly what it rendered before. The pre-existing as any / as never crossings at schema= props in src/ number the same at the base and the head.
  • The added widgetType !== undefined test moves no verdict. classifyWidgetType returns passthrough for a falsy type, so the metric family implies a non-empty string; I read this in widgetDispatch.ts.
  • Ruling A's envelope object-metric: isRetiredEnvelopeNode runs before the envelope pass-through on both surfaces and returns the same imported LEGACY_RETIRED_WIDGET_SCHEMA.
    • Both keys are retired, the bare key and plugin-dashboard:object-metric. That is the same registration under its full name, so this is within the ruling, and the changeset, README and guide state it.
    • The broadcast's FilterableComponentType, FILTERABLE_COMPONENT_TYPES and FilterableComponentSchema lose object-metric. All three are internal, not exported.
    • The ruling's three ⛔ hold. The new control asserts that an envelope object-chart and an envelope object-data-table still receive the filter. ObjectMetricBlock's registration is not edited. There is no cast.

4. The runtime move: the static pivot.

  • Where it applies. Only in DashboardGridLayout, the editable grid. DashboardRenderer draws the retired placeholder for every non-dataset pivot.
  • What changed. The node used to spread options whole. It now states ten keys plus data, under satisfies PivotTableSchema.
  • Can a user see it? Yes, in the editor, for a stored static-data pivot whose options carry a key outside the stated set:
    • a BaseSchema node key such as hidden, style or id (an options.hidden: true tile used to be hidden and is not now);
    • a PivotTable host prop such as rowLabels or columnLabels.
  • Is it stated? Yes: in 11466-dashboard-metric-node (before and now, with the keys named), in dated notes on the 10528 and 11514 changesets, and in the PR body.
  • What is missing. It is not labelled breaking, and it is not measured against stored dashboards. The diff does not show that the TypeScript face required dropping the spread; stating the required axes beside it was possible. It is a choice in the contract's direction, inside the claimed surface.
  • A stale sentence remains. objectui#10528's sentence a few lines above, in the same arm, still reads "the static-data pivot under this branch is unchanged". Non-blocking.

5. The ledgers.

  • WiderThanDeclared is the union of main's objectui#11515 entry and this branch's two (complex.zod.ts#DashboardComponentSchema::widgets and #DashboardWidgetSchema::component, two CONCRETE arms each).
    • Read at the head: 6 entries, 6 keys and 8 arms, split 3 SCHEMA-NODE, 3 CONCRETE, 0 MIXED and 2 unions. That equals the header. main read 4 / 4 / 4, split 3 / 1 / 0 / 0.
    • The growth honestly records a TypeScript face that narrowed while the mirror's BaseSchema fallback stays objectui#8347's, and each entry names its exit. Accepted.
  • MeasuredArmDrift11466: NodeRecursionPointDeclarationDrift went from never to an exact two-way list. never was vacuous while BaseSchema absorbed every arm. The list is app, record, home, utility, page, dashboard, plus the retired kanban wide arm.
    • The three arms objectui#11515 twinned left the list, with a dated note. This strengthens the pin.
    • The zod runtime does not move. SchemaNodeSchema's static output does narrow, because its type is the SchemaNode alias, and for those six arms that type now overstates what parse returns. So the changeset's "every zod face does not move" holds for the runtime accept set only.

6. The flex bag's array child list.

  • The correction is true. FLEX_BAG_CHILDREN is a union of z.array(z.unknown()) and SchemaNodeSchema (zod/layout.zod.ts, which this PR does not change). So FlexBlockNode's properties.children is unknown[] | SchemaNode: a single child is checked, and an array is not.
  • It is not a V1 regression. Without V1 the arm is unknown[] too, and with the signature present no reading moves.
  • Seat ruling A (5970941895) is within the seat's authority. It orders technical tasks: land V1, then close the arm in objectui#11564 before objectui#8347. It widens nothing and adds no exemption.
  • Premise ②(a) still holds. The list is not a slot typed SchemaNode, and premise ②(a) names "a nested slot and the SchemaRenderer prop". So the fork rule ③ is not triggered.
  • The gaps are in ③.

7. The tests.

  • Nothing is skipped or suppressed. No .skip, .only, .todo, xit, @ts-ignore, @ts-nocheck, as any or as never is added.
  • Seven @ts-expect-error are added, all in node-slot-union-11466, and none is removed.
  • Two expect lines are removed, both in the objectui#11525 envelope case that ruling A flips. The new assertions are stronger: the placeholder by identity, no node of that type, no aggregate or find call, and a two-type control.
  • The old pins are restated, not loosened. propsResolution keeps an exact Equal and adds 1b. 7081, 7082 and 8310 are re-anchored. authoring-nodes-11364 keeps PostRemovalSchema as a dated model, and the live prop is pinned in 11466.
  • The census claim is consistent with the diff and with CI. The claim is 0 lines across 88 programs at 5afa0269a and cb687dfad, with the signature present and no cast. The diff adds one cast under src/ (the boundary). On the head, Type Check, all 8 test shards, Test (dist pins), Spec Main Shape Gate and Doc Snippet Type Check are green. The dev measured the census; it was not re-run here.

8. The check-runs. 39 success, 3 skipped (two coverage jobs and dependabot) and 1 failure, Skill Example Check.

  • From its log: the build and the self-test pass. The semantic phase reads 18 of 18 fences judged, 3 failed. All three are TS2322, 'BaseSchema' is not assignable to 'string | DeclaredNode | null | undefined', in skills/objectui/guides/auth-permissions.md, data-integration.md and schema-expressions.md. JSON, bare any and shadowed types each read 0.
  • PR docs(skills): type three marked fences' schema as SchemaRendererProps['schema'] (objectui#11543) #11558 edits exactly those three fences to SchemaRendererProps['schema'], and its own head reads 36 success and 3 skipped.
  • So the failure is that and nothing else.

② Semver level

  • The rule. objectui's version policy (AGENTS.md): no changeset declares major. objectui's own breaking changes are also marked minor, with the breaking meaning written in the body. The fixed group moves together.
  • 11466-node-slot-union: types, react and core minor; components patch. It has a "BREAKING (TypeScript authoring face only)" heading, what moves, what does not, and a migration that includes CustomNodeRegistry. patch is correct for components: only the internal page.tsx boundary and the README change, and no published type moves.
  • 11466-dashboard-metric-node: plugin-dashboard minor. The new export is additive, and the pivot move is stated as before and now. Its "No runtime change" is scoped to the cast bullet, where it is true. Advisory: the pivot move carries no breaking label.
  • 11466-envelope-object-metric-retired: plugin-dashboard minor. It says "Breaking for stored dashboards that still carry this form; minor because objectui never declares major", gives before, now and the fix, and says what does not move. This meets the rule.
  • Pre-existing changesets: 13 pending entries carry 15 append-only dated notes, with 0 lines removed. The bump-policy, declaration, fixed-group, claim re-read and overwrite checks are green.
  • Clause-②: yes (narrowing) is the correct arm. The net change narrows the TypeScript face (item 1) and adds one declaration surface (CustomNodeRegistry). It also makes two runtime narrowings in plugin-dashboard, both stated in the changesets and the PR body.
  • The BREAKING statement for TS consumers who pass a BaseSchema to the prop is present and adequate: "So is a value typed BaseSchema, which names no declared type", with a migration. Advisory:
    • The census's most common break, an unannotated const whose type widens to string, and a value typed AnySchema are covered only by implication.
    • No published narrowing exists for untyped server JSON (objectui#11479 Q2 B was deferred until such a producer is measured), so for that input the advice amounts to a validated cast.

③ Boundary flags

Implemented-by: claude/issue-11466-node-slot-union-v1
Reviewed-by: session_016tKoy8NJa35Yih1FdzrVmn

VERDICT: PASS

This branch has not been deployed

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