Skip to content

fix(types): object-master-detail-form, object-metric and pivot validate (objectui#10859, batch 2) - #10927

Merged
objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-10859-validate-registered-types-b2
Sep 28, 2026
Merged

objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-10859-validate-registered-types-b2

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Part of #10859
Clause-②: yes

Batch 2 of the family card: the three (i) keys batch 1 held for file reasons. Draft, head 6ee675b45d (fix round 1, text only, on 72cf9d3123); a dispatched dev's PR, so ready and landing are the seat's.

What changed

  • packages/types/src/zod/data-display.zod.ts. PivotTableSchema, a member for member mirror of the TypeScript PivotTableSchema (packages/types/src/data-display.ts): rowField, columnField, valueField, data required as declared; aggregation the five-value vocabulary; drillDown the shared DrillDownConfigSchema; body / children refused by name (the declaration's ?: never, objectui#9256). drillDown's .describe says the member is inert on a pivot node (fix round 1). DataDisplaySchema lists it. The DrillDownConfigSchema docblock sentence that named this pair unmirrored is amended.
  • packages/types/src/zod/objectql.zod.ts. ObjectMetricBlockSchema and ObjectMasterDetailFormBlockSchema: BaseSchema + the type literal + properties, which IS the block's @objectstack/spec ComponentPropsMap row, by reference through stripImportedDefaults (the objectui#10872 convention). The master-detail arm also refuses onSuccess, onError, onCancel with handlerKeyRefusal(…, 'runtime-slot'). Both reach AnyComponentSchema through a new union, ObjectQLPublicBlockComponentSchema.
  • packages/types/src/zod/index.zod.ts. Four new barrel exports; ObjectQLPublicBlockComponentSchema joins AnyComponentSchema's arm list.
  • packages/types/src/zod/README.md. Data Display gains PivotTableSchema; a new "ObjectQL Public Blocks (2)" group.
  • packages/types/src/data-display.ts. One docblock sentence on ObjectPivotDrillDownConfig ("Neither object-pivot nor PivotTableSchema has a zod mirror") that this change made false. Comment only.
  • Pins: packages/types/src/__tests__/registered-type-arms-10859-b2.test.ts (new) and the ratchet packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts.
  • Ledgers: zod-mirror-parity.test.ts — the data-display.zod.ts#PivotTableSchema pair (EXPECTED_MIRROR_PAIRS 169 to 170) and three EXCLUSIONS rows (the two spec-owned arms, the union); imported-defaults-8317.test.ts — the two rows' IMPORTED entries.
  • Changesets: .changeset/10859-pivot-object-block-zod-arms.md ('@object-ui/types': minor, the Clause-②: yes line, no BREAKING banner: every document naming these types was refused at type before, at the root and at every child slot, so nothing that parsed is refused now). Dated notes, objectui#10533 form, frontmatter byte-identical, on 7352-drill-down-config-mirror.md and 10685-drilldown-per-block.md.

H1 — the declaration of each key

key declaration arm
pivot PivotTableSchema in packages/types/src/data-display.ts, type: 'pivot' hand mirror, member for member
object-metric none on @object-ui/types. @objectstack/spec/ui (17.4.0): ComponentPropsMap['object-metric'] = ObjectMetricPropsSchema, type ObjectMetricProps. objectui's other type, ObjectMetricWidgetProps (plugin-dashboard), is the widget's React props (a translator function, a React-node icon), not a document shape properties = the row
object-master-detail-form none on @object-ui/types. Spec: ComponentPropsMap['object-master-detail-form'] = ObjectMasterDetailFormPropsSchema. @object-ui/plugin-form also exports MasterDetailFormSchema, the type of MasterDetailForm's schema prop (the renderer's post-hoist reading): type optional, three host callbacks, no BaseSchema properties = the row, + three runtime-slot refusals

Both object-* keys have a published declaration (the spec row, which batch 1's (i) criterion counted), so neither is held. Route change, declared: the order's assumption was a TS-face interface mirrored with an Equal pin. For these two the declaration is a ComponentPropsMap row, and the house route for arming from a row is objectui#10872's properties by reference; the shipped documents (H3) are properties-form, which a flat restatement would refuse. Where MasterDetailFormSchema and the row disagree (it requires objectName and details; it types title, submitText, cancelText as string where the row takes I18nLabel), the spec is the contract. Their own union, not ObjectQLComponentSchema: that union mirrors the TS union in objectql.ts member for member, and objectql-union-arms-7363.test.ts pins its length.

H2 — reproduced on main (733fd5ac6)

safeValidateSchema, on source:

timeline (lit control): success=true
pivot: success=false :: invalid_union @ [type] Invalid input
object-metric: success=false :: invalid_union @ [type] Invalid input
object-master-detail-form: success=false :: invalid_union @ [type] Invalid input

At head all three are accepted; objectui validate (the CLI's validate, in-process) on the showcase master-detail document prints ✓ Schema is valid! and Type: object-master-detail-form.

H3 — taught and shipped documents

  • pivot: no JSON document is taught or shipped. The registry tables in packages/plugin-dashboard/README.md and content/docs/plugins/plugin-dashboard.mdx name it only. packages/plugin-dashboard/SKILL.md's type: 'pivot', and the showcase chart-gallery dashboard's, are the dashboard WIDGET type, not this node.
  • object-metric: the objectstack showcase ships it in { type, properties } form (examples/app-showcase/src/ui/pages/index.ts, four KPIs; my-work.page.ts, three) — accepted on both faces; the first index.ts KPI is pinned. command-center.page.ts's kpi() node is accepted by the tolerant face; the strict face refuses its responsiveStyles, an envelope key BaseSchema does not declare on any arm. objectui teaches no JSON document (the data-source.md binding table, registry tables).
  • object-master-detail-form: the showcase's project-workspace.page.ts form, { type, properties } — accepted on both faces, pinned. docs/adr/0001-master-detail-subform.md and apps/console/src/dev/DevMasterDetail.tsx write it FLAT: the tolerant face accepts them (a flat key BaseSchema does not declare is unjudged) and the strict face refuses those keys by name, while a flat key BaseSchema does declare is judged by BaseSchema's own type on both faces — the same boundary objectui#10872 left open for the public blocks.

H4 — runtime readers

  • pivot → PivotTable (registered as pivot): its destructure reads all 11 declared members (title, rowField, columnField, valueField, aggregation, data, showRowTotals, showColumnTotals, format, columnColors, drillDown).
  • object-metric → ObjectMetricBlock → ObjectMetricWidget: its destructure reads all 18 row members; objectName and filter also arrive through the dataSource binding.
  • object-master-detail-form → MasterDetailFormRenderer → MasterDetailForm: every one of the 14 row members is read off schema, and so are onSuccess, onError, onCancel (the runtime slots; check:handler-key-reads now judges them: OK 131 arm(s) … 53 judged).

No declared member without a reader, so nothing is narrowed. Two read-side observations are in Acceptance notes.

Pins, red on base and green on head

Base = a detached worktree at 733fd5ac6 with the two pin files copied in; head = this branch. From the repo root: pnpm exec vitest run packages/types/src/__tests__/registered-type-arms-10859-b2.test.ts packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts.

run result
base, vitest Tests 30 failed / 9 passed (39): every new row red, the ratchet red (refuses 76 registered bare key(s) at type; the pin is 73.); green = the 9 pre-existing ratchet-file rows
base, tsc -p tsconfig.test.json exit 2: TS2305 / TS2724 (the four new exports missing) and TS2344
head, vitest Tests 39 passed (39)
head, pnpm --filter @object-ui/types type-check (all three programs) exit 0; --listFilesOnly shows the pin in the test program

Rows: each key's minimal document on safeValidateSchema and the strict face; the two showcase documents on both faces (each first parsed by the spec's own row); a full pivot document; a page of the three; an undeclared node key refused by name on the strict face per arm (tolerant passes it); an undeclared bag key refused on the TOLERANT face per spec-row arm; one spec-invalid value per arm (aggregation: 'median', properties.colorVariant: 'pink', properties.formType: 'wizzard', each invalid_value at that path); pivot's required data; drillDown judged by the shared mirror; both content channels; the three runtime slots; the bag's key set equal to the row's and answering four probes as the row does. Type level: pivot's key set and member types invariantly equal to the declaration's; each bag's input type equal to the spec's ObjectMetricProps / ObjectMasterDetailFormProps; an instrument-fires row.

Type-level reverse validation (objectstack scripts/ablation-replace.mjs, WRAP mode, anchor x1 to x0, restored to the HEAD blob with git diff HEAD empty): PivotTableSchema.format retyped to z.number() → tsc -p tsconfig.test.json exit 2, TS2344 on the member-types row plus the mirror-parity ledgers naming data-display.zod.ts#PivotTableSchema and format; the object-metric bag restated as a one-member strict object → exit 2, TS2344 on the bag row.

Ablation. ObjectQLPublicBlockComponentSchema dropped from AnyComponentSchema's list and PivotTableSchema from DataDisplaySchema's, two nested WRAP invocations inside a script with its own EXIT/INT/TERM trap: anchors x1 to x0, blobs bdd57555a921 to a431c90b3df6 and bbeac45eafb5 to 1a2f01f85196, both restored to the HEAD blob, git diff HEAD empty. Result Tests 28 failed / 11 passed (39). Observed direction: the order predicted that only the acceptance rows and the ratchet redden; every refusal row reddens too, because with no arm those documents are refused at type instead of by name (batch 1 observed the same). Green: the two rows that read the arms directly and the 9 pre-existing ratchet rows. Vitest aliases @object-ui/types/zod to src/, so no build sits between mutation and reading.

The sweep

git grep over every tracked test file for AnyComponentSchema, StrictAnyComponentSchema, safeValidateSchema, nodeOracles, KNOWN_SCHEMA_TYPES, propsLevelOf, PUBLIC_BLOCKS, unmirroredAlias, collectArms, getPublicConfigs, ComponentPropsMap, check-handler-key-read-sites, block-config-schema-parity-8216, registry-inputs-spec-parity, strict-authoring-face-8345, zod-mirror-parity: 229 files (the order's nine terms alone: 132, all inside the 229). Plus the handler-key census readers outside packages/types (detail-handler-slots-7804, detail-view-handler-slots-9447, handlerKeyDispositionsMeasured-7804, kanban-handler-slots-7664, the two 7077 button-group files) and markdown-test-inputs.test.ts.

  • packages/types/ and packages/cli/ whole, at 72cf9d3123, with the changeset-reading script suites: Test Files 294 passed (294), Tests 6549 passed (6549).
  • The other 113 files: Test Files 113 passed (113), Tests 1945 passed (1945) — among them block-config-schema-parity-8216 (its node oracle now judges object-metric's designer fields at properties), registry-inputs-spec-parity, public-contract, check-handler-key-read-sites.

Gates

Under the verify lock where they build or test. pnpm --filter @object-ui/types type-check exit 0; pnpm --filter @object-ui/cli type-check exit 0 after pnpm --filter '@object-ui/cli^...' build.

exit 0: check-changeset-presence (9 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)), pnpm changeset:check, check-changeset-overwrite (report-only; the two dated notes), check:changeset-claims (report-only; 46 pending changesets name a file touched here, read below), check:pending-changeset-literals, census:changeset-polarity, check:handler-key-reads (OK 131 arm(s)), check:control-bytes, check:new-line-citations (0 new citation(s)), check:test-path-roots, check:spec-symbols, check:doc-types, check:prompt-keys, check:registry-bare-names, check:unreferenced-sources, check:esm-specifiers, check:element-data-source-declaration, check:component-surface-parity, check:designer-field-key-parity, check:side-effects-array, check:self-import, check:phantom-deps, check:installed-pin-claims, check:doc-example-readers, check-type-check-coverage, markdown-test-inputs.mjs --audit (105 candidate test files, all adjudicated).

eslint, a proven narrowing: the eight changed .ts files, --no-inline-config --format json → 8 files, 0 errors, 33 warnings; the same seven pre-existing files at 733fd5ac6 carry the same 33 (all no-explicit-any), the new file 0. Population: @object-ui/types lints ., @object-ui/cli lints src; eslint.config.js sets no projectService / parserOptions, so no rule can move an untouched file's verdict. The full pnpm lint is CI's.

NOT MEASURED: check:readme-exports (PREREQUISITE NOT MET — unbuilt packages, population floor), check:sdui-registration-pins (no console build), check:doc-snippets, check:doc-examples (full package build). No README code fence changed; CI measures them.

Serial

objectui#10908 (objectui#10872 batch 2) shares index.zod.ts, zod/README.md, the ratchet file, zod-mirror-parity.test.ts and imported-defaults-8317.test.ts with this branch; every hunk here sits outside its hunks (the ratchet change is the REFUSED_AT_TYPE line, not the NAMESPACED_ one). git merge-tree --write-tree of this head 72cf9d3123 against its head fe8f6a6aad: exit 0. Against a fresh origin/main 733fd5ac65: exit 0. objectql.zod.ts, data-display.zod.ts and data-display.ts are held by no open PR (the claim's reading).

Fix round 1 (contract review FAIL, text only)

One commit, 6ee675b45d, no behaviour change:

  1. The changeset's flat-key sentence now separates a key BaseSchema does not declare (unjudged on the tolerant face, refused by name on the strict face) from one it does (label, description: judged by BaseSchema's own type on both faces).
  2. PivotTableSchema.drillDown's .describe no longer promises a drill: it names the member inert on a pivot node and points at object-pivot. No test quoted the old string.
  3. The ObjectMasterDetailFormBlockSchema docblock and its EXCLUSIONS reason call MasterDetailFormSchema the type of MasterDetailForm's schema prop (the renderer's post-hoist reading), and "all four keys" became "both keys".
  4. The 7352 dated note names the second "Unchanged" bullet and the opening paragraph's count (two referencing declarations, three for the release). Frontmatter byte-identical; the note is this PR's own and was only amended.

At 6ee675b45d, under the verify lock: pnpm exec vitest run packages/types/ + the ratchet + six changeset / markdown script suites → Test Files 272 passed (272), Tests 6168 passed (6168); the b2 pin, zod-mirror-parity, drill-down-config-mirror-7352, content-channel-family-d-9256 and the ratchet named → Test Files 5 passed (5), Tests 864 passed (864); pnpm --filter @object-ui/types type-check exit 0. exit 0: check:changeset-claims (report-only), check:pending-changeset-literals, check:control-bytes, check:new-line-citations (0 new citation(s)). git merge-tree --write-tree of 6ee675b45d against a fresh origin/main 733fd5ac65: exit 0; against objectui#10908's head fe8f6a6aad: exit 0.

Acceptance notes

  • The ratchet: REFUSED_AT_TYPE 76 to 73. NAMESPACED_REFUSED_AT_TYPE untouched (398 on main).
  • Still held: the designer six (ruling 5861220023's settlement), the 28 (ii) keys (the maintainer's criterion), the 30 proposed (iii) and 9 unclear keys (the seat's ruling).
  • Pending changesets read: the 46 check:changeset-claims names, grepped for claims about these keys, arm counts and category modules. Dated notes where a sentence became false: 7352-drill-down-config-mirror.md ("PivotTableSchema.drillDown has no zod mirror at all") and 10685-drilldown-per-block.md ("neither object-pivot nor PivotTableSchema has a zod mirror"). Read and left: 8344 (no new category module), 8499 and 7363 (their counts are their own change's reading), 9024 (PivotTableSchema still declares no filter).
  • Observations (per ruling 5861341151 each unhonoured member rides this arm only with a filed carrier; the seat filed objectui#10932 for the first and objectui#10935 for the second):
    • pivot's drillDown makes cells interactive only when a host passes onDrillDown; only ObjectPivotTable does, so on the registry path (DashboardGridLayout's static-data pivot, an authored pivot node) it is inert.
    • the spec row types object-master-detail-form's title, submitText, cancelText as I18nLabel; MasterDetailForm renders submitText and cancelText as React children and interpolates title into a toast, so a locale map there is not resolved.
    • the strict face refuses the spec envelope key responsiveStyles on every arm (BaseSchema does not declare it); the strict face has no CLI door.
  • The flat spelling of object-metric / object-master-detail-form (ADR-0001, the dev harness, plugin-form's own composed nodes) is not judged against the spec row: a flat key BaseSchema does not declare is unjudged on the tolerant face and refused by name on the strict face, while one it does declare (label, description) is judged by BaseSchema's type. Route A, confirmed by the contract review; the flat channel stays the question objectui#10872 left open for the whole public-block family (the seat's note on objectui#10872 names these two arms for that batch), and declaring it later is additive.
  • Label: needs:contract-review was applied by the seat after the dev report.

Attribution: the batch-2 dev of domain:ui seat 2, session https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN.


Generated by Claude Code

… AnyComponentSchema (objectui#10859, batch 2)

pivot mirrors PivotTableSchema from data-display.ts member for member.
object-metric and object-master-detail-form are ADR-0080 public blocks
whose published declaration is their @objectstack/spec ComponentPropsMap
row; each arm reads that row as its properties bag, by reference, the
objectui#10872 convention, through a union of their own.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…lower the ratchet to 73 (objectui#10859)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…ock sentence the pivot arm made false (objectui#10859, batch 2)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…false (objectui#10859, batch 2)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
@github-actions github-actions Bot added documentation Improvements or additions to documentation package: types tests package: cli labels Sep 28, 2026
@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 45 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/5903-objectgantt-declared-keys.md

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

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

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

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

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

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

    emptyAction was the one authored-node exception in the tree. The empty-state CTA slot resolved the registry directly — ComponentRegistry.get(node.type) — and mounted the result itself, so the node never passed through SchemaRenderer and its visibleWhen was never evaluated. @objectstack/spec accepts the key (SchemaNodeSchema carries visibleWhen, and data-display.zod.ts types emptyAction as a SchemaNode), so an author wrote a gate, the platform took it, and nothing enforced it — declared-not-enforced, the same class c86185eb5 closed for record:alert, one level down.

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

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

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

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

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

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

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

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

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

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

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

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

.changeset/6349-types-internal-name-collisions-batch-1.md

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

    BreadcrumbItem / BreadcrumbSchema — re-pointed, because one copy was stale. Both were declared in data-display.ts and in navigation.ts. The data-display pair was not a second dialect but a strict SUBSET: no key declared differently on either side, and missing BreadcrumbItem.icon / onClick / siblings and BreadcrumbSchema.maxItems. Everything that reads a breadcrumb was already on the navigation declaration — registry.ts maps the 'breadcrumb' component type to it, src/index.ts re-exports it under the bare names, zod/navigation.zod.ts mirrors it (icon, onClick, siblings, maxItems included), the ui:breadcrumb renderer consumes it, and the component's own documentation page documents icon and maxItems. data-display.ts now re-exports the one authority.

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

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

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

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

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

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

.changeset/6940-rowactions-boolean-mirror.md

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

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

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

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

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

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

    Two published faces, one retirement — and why a tombstone, not a deletion. The TypeScript interface TreeViewSchema (@object-ui/types, data-display.ts) declares data?: never; the Zod mirror TreeViewSchema (@object-ui/types/zod, data-display.zod.ts) declares data as a retirementTombstone(). BaseSchema already declares data?: any (z.any().optional() on the mirror), so DELETING the member would not have refused the key — it would have ADMITTED it, unvalidated, through the base member, and the renderer would have drawn an empty tree. The tombstone on the extended schema shadows the base member on both faces; the pin measures the base accepting the very document the extended schema refuses.

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

    Two published faces, one retirement — and why a tombstone, not a deletion. The TypeScript interface TreeViewSchema (@object-ui/types, data-display.ts) declares data?: never; the Zod mirror TreeViewSchema (@object-ui/types/zod, data-display.zod.ts) declares data as a retirementTombstone(). BaseSchema already declares data?: any (z.any().optional() on the mirror), so DELETING the member would not have refused the key — it would have ADMITTED it, unvalidated, through the base member, and the renderer would have drawn an empty tree. The tombstone on the extended schema shadows the base member on both faces; the pin measures the base accepting the very document the extended schema refuses.

.changeset/6972-markdown-inert-keys-retired.md

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

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

.changeset/7113-chart-data-model.md

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Each of renderers/form/switch.tsx, textarea.tsx, date-picker.tsx, select.tsx and renderers/data-display/list.tsx reads schema.wrapperClass onto its wrapper element, and neither the TypeScript interface (form.ts, data-display.ts) nor the zod mirror (zod/form.zod.ts, zod/data-display.zod.ts) declared the key. The reads compiled through BaseSchema's index signature (objectui#5155) and the values parsed through .passthrough(), admitted unexamined. The same key, on the same class of read, is declared on CheckboxSchema (b74a8598d), FileUploadSchema and FilterBuilderSchema (objectui#6150); these five were left out only because their doc pages never listed it.

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

    Each of renderers/form/switch.tsx, textarea.tsx, date-picker.tsx, select.tsx and renderers/data-display/list.tsx reads schema.wrapperClass onto its wrapper element, and neither the TypeScript interface (form.ts, data-display.ts) nor the zod mirror (zod/form.zod.ts, zod/data-display.zod.ts) declared the key. The reads compiled through BaseSchema's index signature (objectui#5155) and the values parsed through .passthrough(), admitted unexamined. The same key, on the same class of read, is declared on CheckboxSchema (b74a8598d), FileUploadSchema and FilterBuilderSchema (objectui#6150); these five were left out only because their doc pages never listed it.

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

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

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

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

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

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

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

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

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

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

    Behaviour change on a published surface, deliberately. DataTableSchema.emptyAction is declared SchemaNode on both published faces — packages/types/src/data-display.ts and its Zod twin in packages/types/src/zod/data-display.zod.ts — and SchemaNode is BaseSchema | string | number | boolean | null | undefined. The empty-state render path additionally required typeof … === 'object', so an authored emptyAction: 'Create the first record' rendered nothing and reported nothing: declared wider than enforced, failing in the direction that loses the author's content without a diagnostic. A node that renders nothing today therefore starts rendering.

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

    Behaviour change on a published surface, deliberately. DataTableSchema.emptyAction is declared SchemaNode on both published faces — packages/types/src/data-display.ts and its Zod twin in packages/types/src/zod/data-display.zod.ts — and SchemaNode is BaseSchema | string | number | boolean | null | undefined. The empty-state render path additionally required typeof … === 'object', so an authored emptyAction: 'Create the first record' rendered nothing and reported nothing: declared wider than enforced, failing in the direction that loses the author's content without a diagnostic. A node that renders nothing today therefore starts rendering.

.changeset/8338-retire-toast-action.md

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

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

.changeset/8344-node-recursion-point-redirect.md

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Compared the checked-out tree with 1dae95a41 (merge-base with origin/main): 10 file(s) changed outside .changeset/, read against 1672 pending declaration(s) that publish a body (2268 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3096.1 KB 3104.5 KB
Main entry chunk (gzip) 148.6 KB 350 KB
Entry file index-D2YWCFZV.js —
Status PASS —

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


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.58KB 6.17KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.70KB 2.23KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 557.59KB 133.60KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 226.44KB 63.00KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.96KB 14.83KB
plugin-charts (index.js) 83.99KB 22.86KB
plugin-chatbot (index.js) 197.67KB 46.90KB
plugin-dashboard (index.js) 136.93KB 36.48KB
plugin-designer (index.js) 215.03KB 44.21KB
plugin-detail (index.js) 233.48KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 161.21KB 41.41KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.00KB 9.09KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 88.55KB 22.21KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.27KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 72cf9d31233df67259607feb8cb62330b4430c08

① Derived judgments

The route change: sound.

  • Is there a TS declaration? @object-ui/types has no type: 'object-metric' or type: 'object-master-detail-form' spelling in any interface, union or alias.
    • object-metric: nothing declares the node. ObjectMetricWidgetProps has no type. The app-shell BlockType literal is a palette id, not a node declaration.
    • object-master-detail-form: "no published TS declaration" is false. @object-ui/plugin-form publicly exports MasterDetailFormSchema with type?: 'object-master-detail-form'. It is the type of MasterDetailForm's schema prop, which makes it the renderer's post-hoist reading of the node. The dev did find it and set it aside.
  • Why setting it aside was right. Mirroring it would do two wrong things:
    • It requires objectName, but ElementDataSourceGate supplies that from a dataSource binding, and content/docs/guide/data-source.md teaches that binding for this block.
    • It types the labels string. The spec row declares I18nLabel, and spec outranks implementation.
  • Consistency with the precedent. The spec row is the right declaration. It is the same source PR objectui#10888 armed 20 blocks from.
  • Shipped documents. The tolerant face accepts every shipped document:
    • 13 object-metric nodes, not 7: four in index.ts, three in my-work.page.ts, and six from command-center's kpi(). The strict face refuses only the kpi() nodes' responsiveStyles, which is family-wide.
    • The master-detail page.
    • The flat ADR-0001 sketch and the DevMasterDetail harness also pass the tolerant face.
    • No real document that renders today is refused.
  • Option A, ruled.
    • Business need, measured: every shipped node and the page designer write properties. The flat forms are a design sketch, a dev harness, and runtime-composed nodes (ObjectForm, DrawerForm, ModalForm, the dashboard grid) that never meet a validator.
    • Long-term soundness: the row is read by reference, so nothing is restated and nothing drifts. B restates the row and makes the showcase fail the strict face. C creates two dialects.
    • Keeping AI from writing errors: the bag is closed on both faces, and the strict face refuses the undeclared spelling rather than admitting a second one.
    • Startup focus: A adds no surface.
    • Consistency: A is the construction in ruling 5862923926 and PR objectui#10888: BaseSchema, the literal, propsBag(stripImportedDefaults(row)), and handlerKeyRefusal for on* reads. The flat channel is the later objectui#10872 batch that ruling names.
    • Caveat: objectBlockPropsBag is a byte copy of propsBag, and the arms live outside public-blocks.zod.ts. The seat should record on objectui#10872 that its flat-channel batch covers these two arms.

What the renderers read.

  • The adapter. Both renderers read FLAT node keys. The adapter is SchemaRenderer's COMPAT properties hoist, which copies every bag key onto the node except type and id. After it:
    • ObjectMetricBlock spreads the node onto ObjectMetricWidget.
    • MasterDetailFormRenderer hands schema={bound} to MasterDetailForm.
  • object-master-detail-form renders wrong on an accepted document. The row's I18nLabel submitText and cancelText are rendered raw as Button children, so a locale map throws in React. title is interpolated into the edit-save toast and becomes "[object Object] saved".
    • ObjectMetricWidget does resolve its I18nLabel members through pickLocalized, so this is master-detail only.
    • It has the same declared-but-not-honoured shape PR objectui#10908 was held for. The difference: nothing objectui-authored admits it. It arrives by reference, the objectstack gate already passes it, and the remedy is the renderer (no narrowing at the consumer). So it is not a PR defect, but it needs a carrier (see the verdict).
  • Bare nodes. A bare { type: 'object-metric' } validates on both faces and paints "—". It is spec-valid: the row makes objectName optional and has no rule requiring either objectName or dataSource.object. That is spec-row looseness to report upstream, not a blocker here.

pivot, member for member.

  • The 11 declared keys match the TS declaration. Requiredness matches. aggregation equals PivotAggregation. data, columnColors and drillDown are the shared DrillDownConfig, as drillDown?: DrillDownConfig declares.
  • body and children are tombstoned exactly where the TS face has ?: never. It has no handler keys.
  • The drill shape is correct against objectui#10685: ObjectPivotDrillDownConfig belongs to object-pivot, not to pivot.
  • The parity pair is non-vacuous. MIRRORS entries, counted by brace-matched parse, go from 169 to 170, and the census compares key sets. The b2 file adds key-set rows, invariant member-type rows and an instrument-fires row.
  • Defect: the new drillDown .describe promises that clicking opens a list. On every pivot path that is false:
    • PivotTable fires only when it receives onDrillDown.
    • The pivot registration, reached through console's lazy stub too, is bare PivotTable.
    • The only onDrillDown caller is ObjectPivotTable, which renders object-pivot.

Handler slots. Refusing onSuccess / onError / onCancel is right under objectui#6124.

  • MasterDetailForm calls schema.onSuccess?.(parent) and schema.onError?.(err), and wires onClick={schema.onCancel}.
  • ObjectForm places host functions on that schema.

The new union.

  • ObjectQLPublicBlockComponentSchema sits beside PublicBlockComponentSchema in AnyComponentSchema. From there:
    • the strict face derives from it (the test's strictness rows);
    • the derived nodeOracles pick both arms up from the barrel and judge them at properties via propsLevelOf;
    • handler-key-reads is green.
  • objectql-union-arms-7363 (toHaveLength(12) on ObjectQLComponentSchema) is untouched and still true.

Counts.

  • The ratchet is 73, which is 76 minus 3. The pin is an exact toBe, and all three keys are asserted in BARE_KEYS. NAMESPACED stays at 398.
  • EXPECTED_MIRROR_PAIRS is 170. The two block arms and the union are EXCLUSIONS rows.
  • The ablation reddening the refusal rows is expected: without the arms, those documents are refused at type instead.

Changeset, sentence by sentence. All true except the flat-key sentence in the third bullet. On object-metric, a flat label or description is declared by BaseSchema, so it is judged there. The strict face accepts it, and the tolerant face refuses a bad value. The PR's own objectql.zod.ts docblock states the qualifier that the changeset sentence dropped.

Dated notes. Both follow the objectui#10533 form and leave the frontmatter byte-identical.

  • The note on 10685 hits the falsified sentence exactly.
  • The note on 7352 says "the 'Unchanged' bullet", but two bullets begin "Unchanged". It also leaves the opening "both declarations that carry drillDown" line, which is two for this change and three for the release.

data-display.ts edit. True, and comment only.

The objectql.zod.ts docblock. It calls MasterDetailFormSchema "the React props of MasterDetailForm", but those are MasterDetailFormProps. It also says "the row keeps all four keys optional"; the sentence names two required keys. The same "React props" wording is in the EXCLUSIONS reason.

The dev's three out-of-scope notes. All three are real.

  1. The pivot drillDown is inert on the registry path. It bears on landing through the describe fix and a carrier.
  2. The master-detail I18nLabel labels are rendered raw. It bears on landing only through a carrier.
  3. The strict face refuses responsiveStyles. It does not bear on landing: validate.ts uses safeValidateSchema. Its carrier already exists: ruling 5862923926 lists the envelope keys as a later objectui#10872 batch.

Notes 1 and 2 are marked "承接者:无, not filed". Ruling 5861341151 lets an unhonoured member ride a zod arm only when a filed card carries it, so both need one.

Aside for the split: under this PR's criterion object-timeline becomes (i). Spec main has its row; published 17.4.0 does not, so it becomes (i) when the pin moves.

② Semver level

'@object-ui/types': minor is correct. The change only widens: on base, every node of these three types was refused at type, so nothing that validated before can be refused now. No BREAKING banner is right. @object-ui/types does not re-export the zod entry from its root, so the new zod PivotTableSchema cannot collide with the TS PivotTableSchema export.

③ Boundary flags

  • CI on the head: 42 check runs.
    • 38 success. Test shards 1–8, Type Check, Lint, Build & E2E, dist pins, README Export and the changeset family are all final.
    • 3 skipped: the two coverage matrix jobs and dependabot.
    • 1 failure: Spec Main Shape Gate. Its only TypeScript annotation is TS1360 in report-chart-query-spec-parity.test.ts, compiled against spec df3ba164a588. That is the objectui#10916 line, and this PR does not touch that file. The raw log was unreachable: the proxy denied the blob host, so this reading rests on the annotations.
  • merge-tree:
    • Against main 733fd5ac65 (the PR's merge-base): exit 0.
    • Against PR objectui#10908's head fe8f6a6aad: exit 0.
    • Semantically they compose too. objectui#10908 moves only NAMESPACED and adds an EXCLUSIONS row, so the total stays 170. Its REBUILT_CLEAN census covers lazy owners with no defaults, and main's same test already excludes this PR's two rows from that set.
  • Other open PRs on these files: objectui#10908 and objectui#5400 (the release PR consumes 7352).
  • check-governed-merges: NOT governed (0 of 12 paths), 586 lines.
  • PR state: draft yes; assignee os-elon-musk; needs:contract-review present.

Implemented-by: claude/issue-10859-validate-registered-types-b2
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: FAIL

Four text fixes (dev round, text only, no behaviour change) and two carriers (seat, no PR change). Nothing else stops landing.

  1. .changeset/10859-pivot-object-block-zod-arms.md, third bullet.

    • Replace: "As on the public blocks, a prop written flat on an object-metric or object-master-detail-form node instead of inside properties is not judged against the spec row: the tolerant face keeps it, and the strict face refuses it by name."
    • With: "As on the public blocks, a prop written flat on an object-metric or object-master-detail-form node instead of inside properties is not judged against the spec row. A key BaseSchema does not declare passes the tolerant face unjudged and is refused by name on the strict face; a key it does declare (label and description, both also members of the object-metric row) is judged by BaseSchema's own type on both faces."
  2. data-display.zod.ts, the PivotTableSchema.drillDown .describe. Replace the string with: 'Drill-down config (the shared DrillDownConfig). Inert on a pivot node: PivotTable drills only when its host passes onDrillDown, and the pivot registration passes none; object-pivot is the block whose clicks open the records behind a value'

  3. objectql.zod.ts, the ObjectMasterDetailFormBlockSchema docblock.

    • Replace "MasterDetailFormSchema, the React props of MasterDetailForm. It is not restated here: it is a component's props (three callbacks, an optional type) rather than a document shape"
    • With "MasterDetailFormSchema, the type of MasterDetailForm's schema prop: the node as the renderer reads it after the properties hoist. It is not restated here: it carries three host callbacks and an optional type, and it is the renderer's reading rather than the authored document shape".
    • Replace "the row keeps all four keys optional" with "the row keeps both keys optional".
    • In zod-mirror-parity.test.ts's EXCLUSIONS reason, replace "is the component's React props" with "is the type of MasterDetailForm's schema prop, the renderer's post-hoist reading".
  4. .changeset/7352-drill-down-config-mirror.md, the 2026-09-28 note.

    • Replace "so the first half of the "Unchanged" bullet above is this change's reading, not the release's."
    • With "so the opening paragraph's two referencing declarations are three for the release, and the first half of the second "Unchanged" bullet above (PivotTableSchema.drillDown has no zod mirror at all) is this change's reading, not the release's."
  5. Seat, before landing: file carriers, per ruling 5861341151, for:

    • (a) pivot drillDown inert on the registry path;
    • (b) MasterDetailForm rendering the I18nLabel title / submitText / cancelText raw. The remedy is the renderer, not the validator.

    Also record on objectui#10872 that the flat-channel batch covers these two objectql.zod.ts arms.

…sentence, pivot drillDown describe, MasterDetailFormSchema wording, 7352 note (objectui#10859, batch 2)

Text only; no behaviour change.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3096.1 KB 3104.5 KB
Main entry chunk (gzip) 148.6 KB 350 KB
Entry file index-Bg7mbW66.js —
Status PASS —

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


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.58KB 6.17KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.70KB 2.23KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 557.59KB 133.60KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 226.44KB 63.00KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.96KB 14.83KB
plugin-charts (index.js) 83.99KB 22.86KB
plugin-chatbot (index.js) 197.67KB 46.90KB
plugin-dashboard (index.js) 136.93KB 36.48KB
plugin-designer (index.js) 215.03KB 44.21KB
plugin-detail (index.js) 233.48KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 161.21KB 41.41KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.00KB 9.09KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 88.55KB 22.21KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.27KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 6ee675b45d35739a7ba9a6fe6b14015b834c5915

This is an in-seat delta review of fix round 1 (72cf9d3123 to 6ee675b45d, one commit), against record 5866078131 (FAIL, text only: four wording fixes and two carriers).

① Derived judgments

  • Fix 1, the changeset's flat-key sentence: fixed, verbatim. It now separates a key BaseSchema does not declare (unjudged on the tolerant face, refused by name on the strict face) from one it does (label, description, judged by BaseSchema's own type on both faces).
  • Fix 2, PivotTableSchema.drillDown's .describe: fixed, verbatim. It no longer promises a drill: it names the member inert on a pivot node and points at object-pivot. No test quoted the old string.
  • Fix 3, the ObjectMasterDetailFormBlockSchema docblock and the EXCLUSIONS reason: fixed, verbatim. They call MasterDetailFormSchema the type of MasterDetailForm's schema prop, and "all four keys" became "both keys". The dev notes that the prescribed text leaves two consecutive ", and" clauses. It reads correctly and is accepted as written.
  • Fix 4, the 7352 dated note: fixed, verbatim. Frontmatter is byte-identical, and the note is this PR's own, amended in place.
  • Nothing else moved: 5 files, 13 insertions and 12 deletions. Every .ts hunk is a comment, a .describe() string or an EXCLUSIONS reason string, so there is no behaviour change. The dev re-ran packages/types whole (272 files, 6168 tests), the named pins (864 tests) and the @object-ui/types type-check, all green.
  • Carriers (item 5): filed by the seat.
    • objectui#10932: a pivot node's drillDown is inert.
    • objectui#10935: MasterDetailForm renders the I18nLabel labels raw.
    • The seat's note on objectui#10872 names these two objectql.zod.ts arms for its flat-channel batch.
  • The PR body r2 (applied with this record) names both carriers, the objectui#10872 note and the seat's needs:contract-review label, and adds the fix-round section. Line 1 is Part of #10859. No path:line reference.

② Semver level

'@object-ui/types': minor, Clause-②: yes, a pure widening with no BREAKING banner. ⛔ Not major.

③ Boundary flags

  • CI: Spec Main Shape Gate is red on every PR until objectui#10916's fix, PR objectui#10934, lands. The only diagnostic on this PR's run was that line; this PR does not touch that file.
    • The gate reports a lower bound (objectui#9944), so this PR's own reading against spec main is known only on a clean run after objectui#10934.
    • The ACCEPT and the landing wait for every check on the landing head to be final and green, with git merge-tree against current main re-run first.
  • Merge: clean against 733fd5ac65 and against PR objectui#10908's head fe8f6a6aad.
  • NOT GOVERNED.
  • Draft, assigned to os-elon-musk, with needs:contract-review.

Implemented-by: claude/issue-10859-validate-registered-types-b2
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS

…0859-validate-registered-types-b2

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3096.1 KB 3104.5 KB
Main entry chunk (gzip) 148.6 KB 350 KB
Entry file index-Bg7mbW66.js —
Status PASS —

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


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.58KB 6.17KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.70KB 2.23KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 557.59KB 133.60KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 226.44KB 63.00KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.96KB 14.83KB
plugin-charts (index.js) 83.99KB 22.86KB
plugin-chatbot (index.js) 197.67KB 46.90KB
plugin-dashboard (index.js) 136.93KB 36.48KB
plugin-designer (index.js) 215.03KB 44.21KB
plugin-detail (index.js) 233.48KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 161.21KB 41.41KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.00KB 9.09KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 88.55KB 22.21KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.27KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

…on the object schema

The case "keeps that button in client mode" gated only on the view type
(`data-list`), which client mode satisfies on the first commit, then read
the `Total` sort button synchronously. Since the sort-button row reads
the masked stamp (objectui#10728), every column carries it until
`getObjectSchema` resolves, so the button existed only once the schema
commit had landed: a scheduling race (the #7007 trap described in the
file's own docblock). It now awaits `settleObjectSchema(dataSource)`,
the gate its windowed sibling already uses.

Test-only: no retry, no raised timeout, no source change.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
(cherry picked from commit b92ab92)
…(empty frontmatter)

`check-changeset-presence` counts every file under a released package's
`src/`, test files included, so the test-only gate needs an explicit
no-release declaration.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
(cherry picked from commit 3abab7c)
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat note: Test (shard 7/8) failed on objectui#10895's race; its fix is ported here

domain:ui seat 2, session_014mXUNuFomfj24w7s1pZzhN.

  • What failed: at the base-merged head 395c986cd8, Test (shard 7/8) failed. The failing test is packages/plugin-detail/src/__tests__/RelatedList.unmaterializedSort.test.tsx, case "keeps that button in client mode": "Unable to find an accessible element with the role button and name /Total/".

  • Why it is not this PR's: that is objectui#10895's measured race, in which the sort-button row appears only after the object schema commits. This PR touches no plugin-detail file. The same case failed the same way on PR objectui#10912 earlier today.

  • Fix, ported: PR objectui#10923 (objectui#10895, reviewed by the seat, in the merge queue) gates the case on settleObjectSchema. Its two commits are cherry-picked here:

    • the 4-line test gate, 2584884b6b from b92ab92b37;
    • its empty-frontmatter changeset, ed45945b81 from 3abab7ce5a, which check-changeset-presence needs, because it counts test files under src/.

    Both files are blob-identical to PR objectui#10923's head, so they no-op once that PR lands. No other file moved. The PR's own diff is unchanged.

  • Next: this PR lands on a fully green run at ed45945b81.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

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

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


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.58KB 6.17KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.70KB 2.23KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 557.59KB 133.60KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 226.46KB 63.00KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.96KB 14.83KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 197.67KB 46.90KB
plugin-dashboard (index.js) 136.93KB 36.48KB
plugin-designer (index.js) 215.03KB 44.21KB
plugin-detail (index.js) 233.48KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 161.21KB 41.41KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.15KB 9.14KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 88.55KB 22.21KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.27KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 28, 2026 09:21
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 28, 2026
Merged via the queue into main with commit 3b469c8 Sep 28, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10859-validate-registered-types-b2 branch September 28, 2026 09:49
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Oct 7, 2026
…jectstack-ai#10802 and objectstack-ai#10821 made false (objectui#10905) (objectstack-ai#10939)

Fixes objectstack-ai#10905

Clause-②: no — comment and describe text only; no accept set or public
surface moves.

## What changed

Two docblocks in `packages/types/src`, comment text only, plus an
empty-frontmatter changeset. No `.describe()` string, type, value or
accept set moves.

1. **`ObjectChartSchema.series`, the TS interface in `objectql.ts`.**
The sentence "A `type` written on this arm anyway is an excess property
on a literal typed by this interface" is replaced. Since PR objectstack-ai#10802
(objectui#10770), each `series` entry is either the spec's `ChartSeries`
(the `{ name }` arm) or the internal `{ dataKey }` arm. The `{ name }`
arm declares `type`, and excess-property checking on that union accepts
a key that either arm declares, provided the value fits that
declaration. So `{ dataKey: 'amount', type: 'line' }` compiles. The zod
half of the paragraph (a plain `z.object`, which strips `type`, so the
entry parses clean and the key is dropped) is still true and is kept.
"The mirror's `.describe()` says the same" now reads "names the same
omission". The `.describe()` string names the omission and never made
the excess-property claim, so it is not touched.
2. **The route overview headed "WHERE THIS ARM IS INSTALLED", in
`zod/objectql.zod.ts`.** Its second-route paragraph made three claims.
It said a named view's `listViews` "is unmirrored". It said
`generateViewSchema` merges the same two nestings. It named a `custom`
issue at `listViews.KEY.options.kanban.groupBy`. Since PR objectstack-ai#10821
(objectui#7928), `listViews` is
`stripImportedDefaults(SpecViewSchema).shape.listViews`. That record
refuses a named view's `options` bag whole, `generateViewSchema` reads
no `options` bag, and `checkNamedViewKanbanStrayGroupBy` reports at
`listViews.KEY.kanban.groupBy` only. The heading ("TWO NESTINGS EACH")
and the final sentence ("cannot depend on which route or nesting")
described the same pre-objectstack-ai#10821 shape, so they are corrected in the same
docblock. Three nestings now take the one string. A named view's
`options.kanban.groupBy` meets the protocol's refusal of `options`
instead.

Route 1 of the overview (`list-view`: `kanban` plus `options.kanban`)
was re-read against `ListView` and `ListViewSchema.options` and is
unchanged.

## Measurements

The probes are kept outside the tree and were run once. Everything was
measured on `733fd5ac6`.

**Sentence 1, a `tsc` probe.** Three literals typed `ObjectChartSchema`,
run under the package's own tsconfig:
- `series: [{ dataKey: 'amount', type: 'line' }]` produced no
diagnostic, so the sentence under test is false.
- Positive control: `series: [{ dataKey: 'amount', zzzUnknownKey: 1 }]`
produced TS2353 ("Object literal may only specify known properties"), so
a genuinely unknown key IS refused.
- Value control: `series: [{ dataKey: 'amount', type:
'not-a-chart-family' }]` produced TS2322, so the value must fit the
spec's `ChartType`.

**The zod half, a parse probe** (`ObjectChartSchema.safeParse`):
- `{ dataKey: 'amount', type: 'line' }` succeeds with series
`[{"dataKey":"amount"}]`, so the key is stripped and that half stays
true.
- Controls: `{ dataKey, chartType: 'line' }` keeps `chartType`, and `{
name, type: 'line' }` keeps `type`.

**Sentence 2, a parse probe** (`ObjectViewSchema.safeParse`, a named
view carrying `columns` and `kanban.columns`):
- `kanban.groupBy` gives `unrecognized_keys` at `listViews.board.kanban`
naming `groupBy`, plus `custom` at `listViews.board.kanban.groupBy`.
- `options.kanban.groupBy` gives only `unrecognized_keys` at
`listViews.board` naming `options`. There is no `custom` under
`options`.
- Dark control (`groupByField` and `columns` only): success.
- `generateViewSchema` reads no `options` bag: a `.options` read pattern
hits `plugin-view`'s `ObjectView.tsx` zero times, and hits
`plugin-list`'s `ListView.tsx` (the positive control) 48 times.

**Lit control.** I extracted each of the
`checkNamedViewKanbanStrayGroupBy` and `namedViewKindRefusals`
docblocks, from its opening line through the declaration line, before
and after the edit. Both are byte-identical: `cmp` is silent, and the
sha256 prefixes are `aaa003254ab7127a` and `7c8aa1b4f5cd6a3d` on both
sides.

## Changeset

On the docblock commit alone, `node
scripts/check-changeset-presence.mjs` answered exit 1, because both
files are published source of `@object-ui/types`. So a declaration is
owed.

The declaration uses empty frontmatter, not `patch`. Nothing a consumer
acts on moves. Both docblocks reach the build output only as comment
text: after `pnpm --filter @object-ui/types build`, the new `series`
sentence is in `dist/objectql.d.ts` and the route overview is in the zod
module's JS. At the time of writing, both changes that made the
sentences false (PR objectstack-ai#10802 and PR objectstack-ai#10821) are still pending changesets,
so no release has carried the false text.

## Gates

I derived these by hand from the root `package.json` and
`.github/workflows/`, because the objectstack `dispatch-gates.mjs` does
not cover this repository.

At head `950bf6406`:
- Exit 0 for all of these: `check-control-bytes`,
`check-new-cross-file-line-citations` ("0 new citation(s)"),
`check-changeset-presence` (empty-frontmatter pass),
`check-changeset-fixed`, `check-changeset-no-major`,
`check-changeset-overwrite`, `check-changeset-claims`,
`check-pending-changeset-literals`, `check-spec-symbol-derivation`,
`check-handler-key-read-sites` and `check-unreferenced-sources`.
- `pnpm exec vitest run packages/types/`: 264 files and 5854 tests
passed, exit 0.
- `pnpm exec vitest run` on the three script tests that read
`objectql.zod.ts` as text (`check-eager-closure-budget`,
`check-handler-key-read-sites` and `vite-types-zod-lazy`): 3 files and
241 tests passed, exit 0.

At `7449bc6a9`, which differs from the head only in the changeset's
wording (`git diff --stat` shows one file under `.changeset/`):
- `pnpm --filter @object-ui/types build` exit 0, and the dist
completeness check reported complete.
- `pnpm --filter @object-ui/types type-check` exit 0.
- `pnpm --filter @object-ui/types lint` exit 0, with 0 errors.

`node scripts/check-governed-queue-guard.mjs --test` over the three
paths answered NOT GOVERNED.

NOT MEASURED:
- `check:eager-closure`, reason: it reads every package's built dist,
and a comment-only diff moves no import edge. Its script test above ran
green.
- A downstream consumer type-check, reason: the `.d.ts` changes JSDoc
text only, and no type bytes move.

Known repo-wide red, left alone: `Spec Main Shape Gate` (objectui#10916,
owned by another seat).

## Acceptance notes

- Nothing pins either half of the corrected `series` sentence: a `type`
on a `{ dataKey }` entry compiles, and the mirror strips it. The probes
above were run once. This is an observation, not filed. Carrier: none.
- `main` has not moved from `733fd5ac6` on either file. PR objectstack-ai#10927 edits
`objectql.zod.ts` at its imports and its tail, which are disjoint from
this docblock.

Implemented by the dispatched dev run of the `domain:spec` seat, session
`https://claude.ai/code/session_012UwY3ahMixEFkfTUxMVkYm`.

---
_Generated by [Claude
Code](https://claude.ai/code/session_012UwY3ahMixEFkfTUxMVkYm)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants