Skip to content

feat(types)!: an authored object-chart takes its props in the properties bag; the flat spelling is refused by name (objectui#11276) - #11328

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-11276-object-chart-properties-bag
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-11276-object-chart-properties-bag

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Part of #11276
Clause-②: yes (narrowing)

Clause-②: yes (narrowing), copied from the PM claim 5924110462: @object-ui/types' authoring faces widen (a bag object-chart node that is refused today parses) and narrow (a flat object-chart node that parses today is refused). This is objectui's narrowing-as-minor rule, with a BREAKING (authoring) banner in the changeset.

The object-chart batch of objectui#11276. It executes triage's routing call A (5921136804) as released by the retriage answer (5922517855, Q2: "the next claim's scope is object-chart only"), with the construct of objectui#10859 batches 4 and 5 (PRs objectui#11291 and objectui#11309). The card stays open: object-grid is the next batch, and flex is with the maintainer as objectui#11300.

What changed

  • packages/types/src/zod/objectql.zod.ts: new ObjectChartBlockSchema, the batch 4/5 construct: BaseSchema + the object-chart literal + NODE_ENVELOPE + properties through the shared propsBag helper, beside:
    • one aliasKeyRefusal per bag member, keyed by the bag's own key set (read off the bag, not transcribed), so each prop written flat is refused on both faces with Did you mean KEY → properties.KEY? and one prescription that shows the whole bag document;
    • the three list-view spellings objectui#10608 retired (xAxisField, yAxisFields, aggregation) keep the flat mirror's own retirement tombstones when written flat (the same objects), so their remedy stays the spec spelling rather than a pointer at a bag member that is itself retired;
    • body / children: the flat mirror's objectui#9256 refusals, by reference;
    • the chart-family floor (objectui#10770), read in the bag: requireObjectChartFamilyInBag refuses a node naming neither properties.chartType nor properties.specType, at properties.chartType.
  • ⛔ No spec row, and none invented. Installed @objectstack/spec 17.5.0's ComponentPropsMap has no object-chart row, so the spec's page component takes ANY bag on this type. The bag (ObjectChartPropsBag) is therefore the flat ObjectChartSchema mirror's OWN members by reference: its shape less the node-level keys (BaseSchema's and NODE_ENVELOPE's, both read off their declarations), each the same schema object the mirror holds. It is built from the mirror's shape, not with .omit() on the mirror, because zod refuses .pick() / .omit() on an object carrying a refinement. The bag keeps the mirror's posture (.passthrough()), so an undeclared bag key is judged exactly as it was on the flat node: unjudged by the tolerant face, refused by name by the strict face. The code docblock and the bag's .describe() say this.
  • packages/types/src/zod/public-blocks.zod.ts: propsBag gains an optional description argument for the one bag that is not a spec row, so the arm does not describe itself as ComponentPropsMap['object-chart']. Every other caller omits it and its description is byte-identical.
  • The arm joins ObjectQLPublicBlockComponentSchema (object-chart is in PUBLIC_BLOCKS). ObjectChartSchema (the flat mirror) leaves ObjectQLComponentSchema (10 to 9 arms), stays exported, and stays paired with its TypeScript twin in the parity census.
  • packages/types/src/objectql.ts: the TypeScript ObjectChartSchema is re-declared, not retired. Members unchanged; its docblock now says it is the node as ObjectChart reads it after SchemaRenderer hoists properties, and as code composes it. The TypeScript union's docblock names the zod union's new count.
  • packages/types/src/zod/index.zod.ts (one barrel export) and packages/types/src/zod/README.md (the ObjectQL public-block group lists the arm, 6).
  • New pin packages/types/src/__tests__/object-chart-properties-bag-11276.test.ts (58 rows plus a type-level block).
  • One changeset, .changeset/11276-object-chart-properties-bag.md: @object-ui/types minor, BREAKING (authoring) banner. No other released package's source changes, so there is no second changeset.

Files beyond the claimed surface, each forced:

  • census rows the new export forces: zod-mirror-parity.test.ts (one EXCLUSIONS row; EXPECTED_MIRROR_PAIRS untouched), objectql-union-arms-7363.test.ts (9 arms, object-chart not among them), registered-type-arms-10859-b2.test.ts (the exact public-block literal list gains object-chart), public-block-responsive-styles-10872.test.ts (object-chart gets the smallest family bag in RECORD_SOURCE, because the arm keeps the floor), flat-arm-responsive-styles-10872.test.ts (the batch-9 flat population is flex and object-grid; one row pins that the flat mirror keeps the shared member as the post-hoist reading);
  • fixture triage where a face now judges the bag (mirror rows keep the post-hoist spelling; only door rows move): content-channel-e3-residual-9256.test.ts (AUTHORED_REQUIRED gains the chart), object-chart-axis-config-10518.test.ts (5 door rows and the showcase lit control write the bag), object-chart-legacy-axis-keys-retired-10608.test.ts (the door refuses the retired key in the bag AND written flat; the remedy rows write the bag), object-chart-react-tier-node-10770.test.ts (the door row reads the wrapper's props in the bag), strict-face-read-keys-11070.test.ts (the dataSource row writes its chart props in the bag; the row's verdict is unchanged).

Zone 1, measured before building

  1. The reason holds for this type. Installed @objectstack/spec 17.5.0: ComponentPropsMap has no own object-chart property (controls: object-map and object-grid have one). PageComponentSchema refuses the flat node { type: 'object-chart', chartType, objectName, aggregate } with unrecognized_keys naming the three ("Unrecognized key(s) on this page component … ADR-0089 D3a") and accepts the same props in properties. Control: a flat action:button actionType is refused the same way.
  2. Census, with the stop line. At base 0c6f9bbd7, git grep -l object-chart gives 57 non-test files: 16 CHANGELOG.md and 16 pending changesets (records, not producers) and 25 others. Seat 1's census (5922247963) re-verified on this base:
class files action
authored page-node producers (any spelling) none nothing to rewrite
runtime composers (build a flat node, render through SchemaRenderer, never meet a zod face) app-shell views/ObjectView.tsx (2 sites), plugin-view ObjectView.tsx (2), plugin-list ListView.tsx (2), plugin-dashboard DashboardGridLayout.tsx (1) and DashboardRenderer.tsx (1); the react-page wrapper (react-page.tsx, a comment hit; it composes by tag) kept flat (they read the TypeScript twin)
the declarations objectql.ts, objectql.zod.ts, public-blocks.zod.ts (comment + propsBag) this change
names the type, no node shape register-plugins.ts, known-schema-types.ts, core public-blocks.ts, chart-category-key.ts, plugin-charts ObjectChart.tsx / index.tsx / normalizeChartSchema.ts, plugin-dashboard ObjectMetricWidget.tsx / index.tsx, sdui-parser parse.ts, types data-display.ts, scripts/regenerate-known-schema-types.mjs, ROADMAP.md, content/docs/guide/data-source.md (a table row) none
governed skills/** skills/objectui/guides/data-integration.md, skills/objectui/guides/schema-expressions.md: each names object-chart in a prose list of useDataScope callers, no node listed, not rewritten (no producer)

Stop line: not tripped. The zod ObjectChartSchema object has one reader in objectui source, the ObjectQLComponentSchema union (plus the barrel re-export); no schema is built from it (unlike object-view's table slot from ObjectGridSchema, the shape the object-grid batch meets). Every non-page context that keeps the flat spelling depends on the TypeScript twin (the composers, ObjectChartProps.schema, ObjectMetricWidget's aggregate type), which is unchanged.
3. The arm, as above. The TypeScript twin is re-declared, not retired; nothing had to retire, so the needs_decision stop did not fire.
4. Producers: zero objectui-authored flat object-chart page-node producers to rewrite; zero governed producers (two governed prose hits, listed above).

Zone 2, re-measured

  1. Held, by symbol: ObjectChartSchema is in objectql.zod.ts, its twin in objectql.ts, ObjectMapBlockSchema (batch 5) in objectql.zod.ts. The new arm sits right after the flat mirror it reads, which also keeps it out of PR objectui#11312's hunks.
  2. Held: 25 non-test files outside the CHANGELOG and changeset records.
  3. PR objectui#11312 (objectui#11117) and PR objectui#11320 merged into main at 2026-10-01T04:02Z, after this branch's commits and before this PR opened. Together with be5211522, main has moved by three commits since the base, and the last two touch objectql.zod.ts, objectql.ts, public-blocks.zod.ts and zod-mirror-parity.test.ts. Their hunks are disjoint from this diff's: GitHub reports the PR mergeable, and the merge queue re-tests the merged tree.

Tests (heavy runs through os-verify-lock.sh, slot issue-11276)

  • Red first at the arm commit a13229954 (before triage): pnpm exec vitest run packages/types/src/ gave Tests 29 failed | 7663 passed (7692) in 10 files: exactly the census rows and door fixtures listed above.
  • Green, union at the final head 03b6e0d98: pnpm exec vitest run packages/types/ packages/cli/ gave Test Files 330 passed (330), Tests 8079 passed (8079), VERDICT command-exit 0.
  • Consumer sweep at f1cd6ab70 (the head before the README + changeset commit): 234 tracked test files outside packages/types and packages/cli that name object-chart, plugin-charts, ObjectChartSchema, ObjectChartBlockSchema, AnyComponentSchema, StrictAnyComponentSchema, safeValidateSchema, ObjectQLComponentSchema, ObjectQLPublicBlockComponentSchema, PUBLIC_BLOCKS, getPublicConfigs, ComponentPropsMap, KNOWN_SCHEMA_TYPES, nodeOracles or the parity / strict-face / handler-key ledgers, in three chunks: 79 passed / 2499 tests, 73 passed / 787 tests, 82 passed / 1576 tests, each VERDICT 0. The later commit changes only README.md and the changeset; the tests that read the zod README are in packages/types and scripts/__tests__, both re-run after it.
  • scripts/__tests__/ (a markdown file changed) at 03b6e0d98: Test Files 177 passed | 2 skipped (179), Tests 5371 passed | 2 skipped (5373), VERDICT 0. node scripts/markdown-test-inputs.mjs --audit: "107 candidate test files, all adjudicated; 78 declared entries, all present".
  • Type-check: pnpm --filter @object-ui/types type-check (tsc --noEmit, examples, tsconfig.test.json, which includes src/**/*.test.ts), VERDICT 0. @object-ui/types has no workspace dependency to build first.
  • Runtime ablation (objectstack scripts/ablation-replace.mjs, two nested WRAP legs plus an EXIT/INT/TERM trap, absolute paths): the flat arm restored, i.e. ObjectChartSchema back into ObjectQLComponentSchema (anchor x1 to x0, blob ec019f61 to 0bc58a72) and ObjectChartBlockSchema out of the public-block union (anchor x1 to x0, blob to fb03dd31); on disk, ObjectChartSchema, x1 in the ObjectQL union and ObjectChartBlockSchema, x0 in the public one. Over 14 files (the new pin, the 10 triaged and census files, and three untouched chart and bag pins as controls): Tests 66 failed | 651 passed (717). Red: 43 rows of the new pin and 23 triaged or census rows (10608 12, 10518 3, 9256 2, 10872-batch-9 2, and one each in 10770, 7363, b2 and 11070), which is exactly the set this diff added or changed. Green: the untouched pins (object-chart-undeclared-keys-8885, widget-schema-anchors-7946, object-map-properties-bag-10859-b5), the census files whose subject is the export itself, and the pin's 15 rows that do not depend on which arm the union selects (the spec lit controls, the bag's key set and member identity, its description, and the refusals the flat mirror shares). Direction red, as expected. Restored: blob == HEAD ec019f61, git diff HEAD empty. Vitest imports the zod sources relatively, so no build sat between the mutation and the run.
  • Type-level reverse validation (same tool): the bag's .omit(OBJECT_CHART_NODE_LEVEL_KEYS) widened to also drop dataset made tsc -p tsconfig.test.json exit 2 with exactly one error, TS2344: Type 'false' does not satisfy the constraint 'true' on the pin's keyof Bag row. Restored, blob == HEAD.
  • Render identity (a one-time probe through the real SchemaRenderer and registry, not committed): the bag node { type: 'object-chart', properties: { objectName, chartType, aggregate, filter, title } } makes the same data-source calls as the flat node with the same props and draws an identical DOM; control: a bag naming another object makes different calls. Tests 1 passed (1).

Gates (exit codes captured before any pipe)

  • exit 0: node scripts/check-changeset-presence.mjs ("16 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)"), changeset:check ("No changeset declares a major bump."), check:control-bytes ("OK (scanned 9793 tracked text file(s); skipped 85 binary)"), check:new-line-citations ("VERDICT new-cross-file-line-citations: 0 new citation(s)"), check:handler-key-reads ("OK 140 arm(s), 212 registration(s) (138 keyed onto an arm), 56 reachable handler read(s), 56 judged"), check:spec-symbols, check:element-data-source-declaration, check:component-surface-parity, check:doc-types, check:prompt-keys, check:test-path-roots, check:installed-pin-claims, check:vi-mock-specifiers, check:pending-changeset-literals, check:changeset-claims (report-only), docs:check-links, check:governed-queue-guard, and check-governed-queue-guard.mjs --test over the 17 changed paths ("NOT GOVERNED").
  • pnpm exec eslint on the 15 changed source and test files: 0 errors, 37 warnings, all no-explicit-any on lines this diff does not touch.
  • NOT MEASURED (PREREQUISITE NOT MET: the packages they import are not built in this worktree, so each refused to run, exit 2 or a collapsed population): check:readme-exports, check:doc-snippets, check:doc-examples, check:skill-examples, check:sdui-registration-pins. This diff changes no fenced block in any README, doc or skill. Left to CI, which builds first.
  • Not run locally: the repo-wide pnpm lint and the full pnpm test farm (CI's).

Acceptance notes

  • data stays a node-level BaseSchema key on this arm, as on every arm. ObjectChart reads schema.data (inline rows) and the registration's inputs list data, but neither the mirror nor the TypeScript twin declares it as a chart member, so it is not in the bag: a flat data passes both objectui faces (the spec's page component refuses it at node level), and properties.data passes the tolerant face unjudged and is refused by the strict face. Whether data is a chart member is a declaration decision this card did not take. Noted, not filed; no carrier.
  • dataSource stays undeclared on object-chart, on the bag arm as on the flat mirror, as objectui#11070 left it ("refused by name until it is declared"). The registration is gate-wrapped and reads the binding.
  • The mirror's INTERNAL members ride into the bag with it (xAxisKey, the { dataKey } series arm, specType): the bag is the mirror's members, so they are judged in the bag exactly as they were judged flat. No member's verdict changed; only its layer.
  • The same release's pending changesets 10518, 10608, 10770 and 10872-flat-arm-responsive-styles describe validator readings on a flat object-chart; this PR's changeset says those readings now happen in the bag on an authored node and still hold on the flat mirror (the batch-5 precedent). They are not edited.
  • No render pin is committed: the renderer half is SchemaRenderer's generic hoist, already pinned for the earlier bag arms; the one-time probe above measured it for this type.

Overlap

PR objectui#11312 (merged first) edited other regions of objectql.zod.ts, objectql.ts, public-blocks.zod.ts and zod-mirror-parity.test.ts. The hunks are disjoint, and this PR lands through the merge queue, which tests the merged tree.

Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec (the dispatching seat's id; this run is its subagent on branch claude/issue-11276-object-chart-properties-bag).


Generated by Claude Code

claude added 3 commits October 1, 2026 03:36
…ies bag (objectui#11276)

`ObjectChartBlockSchema` arms the authored `object-chart` node: `BaseSchema`
+ the literal + `NODE_ENVELOPE` + `properties` through `propsBag`, whose
members are the flat `ObjectChartSchema` mirror's own members by reference
(the spec has no `ComponentPropsMap['object-chart']` row, and none is
invented). Each bag member written flat is refused by name with
`Did you mean KEY → properties.KEY?`; the chart-family floor is read in the
bag. The flat mirror leaves `ObjectQLComponentSchema` and stays published as
the post-hoist reading; its TypeScript twin is re-declared, not retired.

Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
Co-authored-by: Claude <noreply@anthropic.com>
…e move forces (objectui#11276)

New pin `object-chart-properties-bag-11276.test.ts`: the showcase's bag nodes
pass both faces, the bag is the mirror's own members by reference, each flat
member is refused by name with the bag prescription, the family floor is read
in the bag, and the flat mirror stays the post-hoist reading.

Census rows and fixture triage: the parity census gains one EXCLUSIONS row;
the union-arm counts move (ObjectQL union 10 to 9, public-block union gains
`object-chart`); door rows that judged a flat authored chart now write the
bag, while the mirror rows keep the post-hoist spelling.

Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
Co-authored-by: Claude <noreply@anthropic.com>
…hangeset declares the authoring break (objectui#11276)

`@object-ui/types` minor with a BREAKING (authoring) banner: a flat authored
`object-chart` is refused by name; the same props in `properties` parse.

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

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 47 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/10608-object-chart-legacy-axis-keys-retired.md

  • names packages/types/src/__tests__/object-chart-legacy-axis-keys-retired-10608.test.ts → packages/types/src/__tests__/object-chart-legacy-axis-keys-retired-10608.test.ts — edited by this change

    Pinned in packages/types/src/__tests__/object-chart-legacy-axis-keys-retired-10608.test.ts.

.changeset/10872-container-children-channel.md

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

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

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

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

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

  • 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/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/6152-object-form-unmirrored-members.md

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

    ObjectFormSchema in @object-ui/types (objectql.ts) declared a set of members that its zod mirror in @object-ui/types/zod had never heard of. Every one of them is read by the object-form renderer (ObjectForm in @object-ui/plugin-form). The two published faces answered differently:

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

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

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

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

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

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

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

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

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

  • 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/7352-drill-down-config-mirror.md

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

    DrillDownConfigSchema is the zod mirror of DrillDownConfig, and both declarations that carry drillDown reference it — ChartSchema (zod/data-display.zod.ts) and ObjectDataTableSchema (zod/objectql.zod.ts) — so the published validator under @object-ui/types/zod reads the key for the first time (objectui#7352).

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

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

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

  • 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/7632-shared-record-source-config.md

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

    That ladder is published contract on both faces — packages/types/src/objectql.ts and its zod mirror both ship .describe() strings naming getDataConfig's order (77cb489b4, maintainer ruling 2026-09-02), pinned by objectql-record-source-refinement-6939.test.ts — and it was hand-copied into five plugin components with no gate holding them together. A change to the ruled order had five edit sites and nothing that noticed a missed one; that is the AGENTS.md #0.1 drift class.

.changeset/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/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/7804-objectql-handler-key-arms.md

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

    The four plain objectql.ts node faces declare the nine handler keys their registered renderers read (the objectql.ts slice): ObjectFormSchema.onCancel / .onError / .onOpenChange / .onStepChange / .onSuccess, ObjectGallerySchema.onCardClick / .onRowClick, ObjectGridSchema.onNavigate and ObjectViewSchema.onNavigate.

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

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

    'runtime-slot' and not 'retired', measured at this key's own channel. 'retired' publishes "no renderer reads this key, so nothing could ever run it" — true of the two siblings already tombstoned on this arm (onSelectChange, onExpandChange) and flatly false here, since the read is live and INVOKED. ⚠️ No in-repo host builds a tree-view node carrying the key: the channel is wired end to end and only the supplier is absent, which is the same shape as ObjectFormSchema.onStepChange in this card's objectql.ts slice and is not evidence of a dead read. The TypeScript declaration is unchanged and still callable, so a programmatic host supplies it exactly as before.

  • 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/7963-alert-dialog-footer-keys-retired.md

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

    Nothing else moves. These spellings are overloaded across the tree and every other owner is a live key on a different declaration — FormSchema.cancelLabel, objectql.ts's confirmLabel, plugin-designer's ConfirmDialog React props, plugin-grid's def.confirmLabel, and plugin-form's ModalForm / DrawerForm, which build a local cancelLabel from schema.cancelText. None is an AlertDialogSchema; none is touched, and a pin asserts it. No fixture, catalog schema, example app or doc fence authored any of the three on an alert-dialog node, so no shipped document is stranded.

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

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

    ⚠️ Dated note, 2026-09-28 — a second category module — objectui#10872. Later still in this same release zod/public-blocks.zod.ts joins them as well and imports zod/base.zod.ts too, so both counts in the mechanical note above are two higher, not the one the objectui#10859 note says; the reason the arm cannot be an import is unchanged. The rest of this entry, and that note, are kept as their readings.

.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/8767-object-grid-refuses-string-sort.md

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

    Migration. Write the array: sort: [{ field: 'name', order: 'desc' }]. Both keys are required. SortConfig.order carries no ? in @object-ui/types (packages/types/src/objectql.ts) and no .optional() in its zod mirror, and the protocol's own reusable SortItemSchema requires order as well — measured: that schema refuses [{ field: 'name' }] with invalid_value at 0.order. Do not omit it: this block's array arm interpolates whatever is present, so an omitted order lowers to $orderby: 'name undefined' today. That is pre-existing behaviour on the arm this change does not touch, and it is filed as a successor card rather than widened into here.

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

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

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

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

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

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

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

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

  • 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/9092-inline-locale-declared-face.md

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

    • AppComponentSchema.label (app.ts) - ObjectGridSchema.label and .description (objectql.ts) - PageNodeSchema.aria.ariaLabel (layout.ts)

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

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

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

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

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

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

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

  • 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/9550-object-tree-root-barrel.md

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

    ObjectQLComponentSchema declares the node types an ObjectQL block may be. Every one of its arms was a named export of this package's root barrel except ObjectTreeSchema, which was declared in objectql.ts, applied by the union, and re-exported by the ./zod barrel (objectui#7917) — while no TypeScript consumer could name it. There is no ./objectql subpath to reach around the barrel: the package's exports map is pinned by packages/types/src/__tests__/package-exports-manifest.test.ts, and the root barrel was the only route to this type.

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

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

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

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

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

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

    The key was declared on both published faces of the object-kanban arm — the lane element of ObjectKanbanSchema (objectql.ts and its Zod mirror) and the runtime lane KanbanColumn (complex.ts and its mirror) — and read by KanbanEnhanced alone, a module no production source imports. An authored { "id": "todo", "title": "To Do", "collapsed": true } therefore parsed green on both faces and reached a board that did nothing with it: KanbanImpl's only collapse is the SWIMLANE row's, held in viewer state under objectui:kanban-collapsed:ANGLE-BRACKETS(swimlaneField) and never keyed to a lane's declared value. That is the ADR-0049 declared-but-unhonoured shape.

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

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

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

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

Angle-bracketed names in the quoted prose above are rewritten as ANGLE-BRACKETS(name): GitHub deletes tag-shaped fragments from a stored body, and a quote that silently loses the identifier it is about is worse than a visible repair.

Compared the checked-out tree with 0e6e76bc4 (merge-base with origin/main): 16 file(s) changed outside .changeset/, read against 1890 pending declaration(s) that publish a body (2503 pending in total). · run

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

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

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


📦 Bundle Size Report

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

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 03b6e0d98f411fdbb7b3c7dd8e7366d39ee7be4d
Local-runs: none

Inputs: card #11276 (body and all seven comments, 5921136804 through 5924540849), PR #11328 (body, the 17-file list, the net diff against main), the head's check-runs read with filter=latest, and head or base file reads for context (objectql.zod.ts, public-blocks.zod.ts, base.zod.ts, tombstone.zod.ts, strict-authoring-face.ts, index.zod.ts, zod-mirror-parity.test.ts, core registry/public-blocks.ts, AGENTS.md). CI was in progress at the start; waited until every run completed (the last at 2026-10-01T04:19:40Z): 43 runs, 40 success, 3 skipped (dependabot and the two coverage lanes), 0 failed, 0 in progress. The gate verdicts are green; the only annotations are runner-image notices and no-explicit-any lint warnings on e2e/live/** lines this diff does not touch.

① Derived judgments

  1. The bag without a spec row — right. ObjectChartPropsBag = z.looseObject(ObjectChartSchema.shape).omit(OBJECT_CHART_NODE_LEVEL_KEYS) (objectql.zod.ts:3318 at the head). The shape entries are the flat mirror's own schema objects (zod carries them into looseObject and through .omit untouched; the pin's each member IS the mirror's schema object row reads that with toBe on every run), so the bag is by reference, not a copy. The node-level mask is read off BaseSchema.shape (21 keys at the head, type / id / data / body / children among them) and NODE_ENVELOPE (responsiveStyles alone), leaving 19 members: objectName chartType specType xAxisField yAxisFields aggregation dataset dimensions values filter aggregate xAxisKey series colors drillDown title compareTo xAxis yAxis. No ComponentPropsMap symbol is read anywhere in the arm; the only spec reads are the pin's lit controls, so no row is invented. The posture stays loose (BaseSchema ends .passthrough(); the strict walk sets catchall: z.never() on every reachable object and keeps that object's own checks, strict-authoring-face.ts:262-279), so an undeclared bag key is unjudged on the tolerant face and refused by name on the strict one, exactly as it was on the flat node. Stated in the arm's docblock, in the bag's .describe() (through the propsBag description argument), in the zod README (ObjectQL Public Blocks, 6) and in the PR body.
  2. The re-measured reason — right. The pin's lit controls read the installed spec on every run: ComponentPropsMap has no own object-chart (control: object-map has one); PageComponentSchema refuses the flat { type, objectName, chartType } with one unrecognized_keys naming both keys and accepts all four bag fixtures. Green at this head, so the reason holds for this type.
  3. The census and the stop line — right. The classes agree with the triage and seat-1 readings: 0 authored page-node producers in any spelling; 8 composer sites in 5 files build flat nodes typed by the TS twin and render through SchemaRenderer, which no zod face judges; the 2 governed skills/** hits are prose lists, listed and not rewritten, as the claim required. At the head the zod ObjectChartSchema object's readers in objectql.zod.ts are the new arm alone (.shape for the bag and five members by reference, lines 3318 and 3443-3449) plus the index.zod.ts barrel export (line 324); nothing .extend()s, .omit()s or slots the mirror into another schema (the object-view table-slot shape object-grid meets is absent here). The stop line did not fire, correctly; needs_decision did not apply, because the TS twin is re-declared, not retired.
  4. The chart-family floor and the tombstones — right. requireObjectChartFamilyInBag refuses a node whose bag names neither chartType nor specType, at properties.chartType, on both faces (the strict walk keeps the arm's check). xAxisField / yAxisFields / aggregation are bag members (the mirror's tombstones ride in by reference) and, written flat, override the spread aliasKeyRefusal rows with the same tombstone objects (:3443-3445), so their remedy stays the spec spelling in both positions; body / children are the mirror's objectui#9256 refusals by reference. The spec has no family rule; the divergence is recorded in the pin.
  5. The flat mirror leaving the ObjectQL union (10 to 9) — right. The union at the head has nine arms and no object-chart; the mirror stays exported; EXPECTED_MIRROR_PAIRS still pairs objectql.zod.ts#ObjectChartSchema with the TS twin (parity test lines 1779 and 1953), and EXCLUSIONS gains the one row the closed census forces. The TS twin's diff is docblock only, members unchanged, and the TS union still carries it.
  6. propsBag gaining description — right, and the byte-identity claim holds. 36 call sites at the head: 35 pass two arguments on lines the diff does not touch, one (the chart arm) passes three. The fallback is description ?? followed by the base's template and literal byte for byte, so every other description is unchanged; the pin's control row reads ObjectMapBlockSchema's. The helper is not re-exported from index.zod.ts, so no public surface moves with it. Outside the claim's file list but inside the construct the claim names (the shared propsBag); an inline .optional().describe() beside the shared helper would have been the second copy the one-copy rule refuses.
  7. The census rows and fixture triage (10 files) — each forced, each pin keeps its subject. 9256: AUTHORED_REQUIRED gains the chart (the door now judges the bag arm; mirror rows unchanged). 10872 batch 9: FLAT_ARMS drops object-chart (the node union no longer selects the mirror) and one new row pins that the mirror keeps the shared responsiveStyles member and is not the union's arm; the authored arm is covered by the batch-8 pin, where RECORD_SOURCE gains { properties: { chartType: 'bar' } } because the arm keeps the floor. 10518 and 10770: door rows write the bag, mirror rows keep the flat node. 10608: the door now refuses each retired key in the bag and written flat, each with exactly one invalid_type at its own path; mirror row unchanged. 7363 (nine literals), b2 (the exact public-block list), 11070 (same verdict, dataSource alone undeclared), parity (one EXCLUSIONS row).
  8. The ablation and the type-level reverse check — consistent, read not re-run. The new pin is 42 fixed rows plus 16 live-key rows, 58; with the flat arm restored, 15 rows are arm-independent (five spec lit controls, key set and member identity, description, non-vacuity, the three retired-flat rows and the two channel rows the mirror shares, the mirror-published row) and 43 are red, which is the itemisation given; the 23 triaged rows (10608 12, 10518 3, 9256 2, 10872-b9 2, 10770 / 7363 / b2 / 11070 one each) are exactly the rows this diff changed. Dropping dataset from the bag mask can fail only the keyof Bag equality row: one TS2344, as reported.

② Semver level

@object-ui/types minor with a BREAKING (authoring) banner — right. The diff widens (@object-ui/types/zod exports ObjectChartBlockSchema; a bag object-chart node parses on both faces where it was refused) and narrows (a flat object-chart node that parsed is refused by name on both faces, so at safeValidateSchema / validateSchema and at objectui validate); the changeset names both. The PR body's second line, Clause-②: yes (narrowing), reads declared / yes / narrowing through clause2-line.mjs (the changeset's bolded copy reads the same): the arm for a diff that widens one surface and narrows another, copied from the claim 5924110462. 16 source files in one released package, one changeset, no other released package's source changed; Changeset Bump Policy, Declaration, Claim Re-read and Fixed Group are green. Part of #11276 is right: the card stays open (object-grid is the next batch, flex is with the maintainer as #11300).

③ Boundary flags

No open_questions. The deviations:

  1. Reporter and timeout (dot reporter, 540 s inside the lock): run mechanics, disclosed, no bearing on the diff.
  2. Not merged with main — disclosed, but its premise is stale, and it is corrected here. The three commits date 03:36Z to 03:53Z; PR fix(types): a gate-wrapped arm waives objectName when dataSource.object binds the node (objectui#11117) #11312 (0e6e76bc4, objectui#11117) and PR feat(types,components)!: retire data-table selectionStyle and chatbot floatingConfig on both faces, file data-table's inline-edit flags by name (objectui#6152, round 5) #11320 (b5b928ab0, objectui#6152 round 5) merged at 04:02:44Z, before this PR was opened (04:05:23Z) and before the dev report (04:08:08Z). So at this head main has moved by three commits, not one, and the last two touch four of this PR's files (objectql.zod.ts, objectql.ts, public-blocks.zod.ts, zod-mirror-parity.test.ts). The PR body's sentences "Draft PR objectui#11312 is still open (head 80ec249bc3)", "draft PR objectui#11312 has not landed" and "main moved by be5211522 (components and plugin-grid only)" are not true at this head. What still holds: the hunks are disjoint (GitHub reports mergeable: true, mergeable_state: behind; main's objectql.zod.ts hunks end in the ObjectMapBlockSchema region, this PR's begin after the flat mirror and in the two union docblocks), and the check-runs started at 04:05:27Z, after both merges, on a pull_request event (whose default checkout is the merge ref; not verified from the job logs, which sit behind a host the proxy refuses). Escalated to the seat, not back to the dev: by the PR's own overlap rule ("whichever lands second merges main") this PR now owes the merge of main before it lands, and the queue's re-run settles it. No derived judgment above depends on it.
  3. The propsBag argument: judged at ① 6.
  4. The trailers: each commit ends with the Claude-Session: and Co-authored-by: Claude pair objectui's AGENTS.md prescribes, no model identifier anywhere in the PR, the changeset or the code; the PR footer carries the session URL. Disclosed as the rule asks; right.

The out-of-scope notes, against the filing rule (three classes, each owing reach:, a measured wrong answer at a public door or a named producer; otherwise Acceptance notes only, with carrier: or 「承接者:无」):

  • data: a BaseSchema key at the head, so node-level on this arm by construction. The public door (safeValidateSchema) accepts a flat data and leaves properties.data unjudged; only the strict face, not yet a door, refuses the latter. No measured door reach and no producer named writing data on an authored chart, so Acceptance-notes-only with 「承接者:无」 is the rule's own disposition, and the note is accurate at this head.
  • dataSource: stays undeclared on the arm as on the mirror (the 11070 row keeps its verdict with the bag spelling); carrier named (objectui#11070 and the object-grid batch). Right.

Every other sentence of the PR body was checked against the diff and the head files and holds; the stale ones are the three quoted in item 2.

Implemented-by: claude/issue-11276-object-chart-properties-bag
Reviewed-by: session_011p7ikEivgXefNDaE5S5Uec

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 1, 2026 04:25
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 1, 2026 04:25
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 1, 2026
Merged via the queue into main with commit 5262f7d Oct 1, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11276-object-chart-properties-bag branch October 1, 2026 04:40
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Oct 7, 2026
…Source are the spec's, by reference; GridColumnDefinition retires (objectui#11070, round 7) (objectstack-ai#11352)

Refs objectstack-ai#11070
Clause-②: yes (narrowing). The strict face's accept set widens (a grid
field's `columns` and an `object-chart` node's `dataSource` binding
parse), and the tolerant face narrows (both keys are now judged by their
declared type: a grid column the spec refuses, or a non-binding
`dataSource` on `object-chart`, is refused where it used to be stripped
or kept unjudged). `GridColumnDefinition` retires from
`@object-ui/types`, and `GridColumn` in `@object-ui/fields` becomes the
spec's `InlineGridColumn`. Priced in
`.changeset/11070-grid-columns-chart-binding-round7.md` as minor with a
BREAKING banner.

`Refs`, not a closing keyword, on purpose. Round 7 of the card, under
the seat's claim `5925545318` (remainder items 2 and 3 of `5919314708`).
Draft; the dev does not mark it ready.

## What changed

**(i) The grid field's `columns`, by reference to the spec.**
- `GridFieldMetadata.columns` (`field-types.ts`) and `FormField.columns`
(`form.ts`) are `SpecField['inlineColumns']`; the zod mirror
`FormFieldSchema` declares
`stripImportedDefaults(SpecFieldSchema).shape.inlineColumns`.
`form-field-zod-coverage` gains the `columns` row. `FormFieldSchema` was
already a registered, spec-derived pair in `zod-mirror-parity`; its
`SPEC_DERIVED_PAIRS` note records the new member, and no ledger total
moved.
- `GridColumnDefinition` is retired (interface and barrel export).
Nothing in production read it, and it was not the shape the widget read.
- `GridField`'s `GridColumn` is `InlineGridColumn` from
`@objectstack/spec/data`, by reference. The renderer notes its
per-member JSDoc carried move into one docblock. No read changes: the
widget already read exactly the spec's twenty keys.
- Fixtures: `fields-grid/*` already write only `name` / `label` /
`type`; all nine columns parse against the spec element, so none
changes.
- `content/docs/fields/grid.mdx`: the typed fence drops a column
`defaultValue`; the prose names the spec column and lists the nine cell
types; the plaintext fences stop teaching `boolean`, `percent` and
`user` column types; the validation sketch drops a column `min` the
column does not have.

**(ii) `object-chart` · `dataSource`, re-measured and declared.**
- `ObjectChartSchema` (TypeScript and the flat zod mirror) declares
`dataSource` as the spec's `ElementDataSource` /
`ElementDataSourceSchema`, by reference. `OBJECT_CHART_NODE_LEVEL_KEYS`
keeps it out of the authored bag, and `ObjectChartBlockSchema` carries
it at node level (the mirror's own member), as `ObjectFormBlockSchema`
and `ObjectMapBlockSchema` do.
- The stale `SHOWCASE_NODE` pin
(`object-chart-react-tier-node-10770.test.ts`) drops `dataSource: null`
and the comment describing a wrapper write that round 2 removed; a
binding row and a `null`-refused row join it.

**Rows the declarations force (fixture triage):**
`object-chart-properties-bag-11276` (the node-level key set gains
`dataSource`, on the type and runtime legs; its "left undeclared" row
becomes a "parses at node level, not a bag member" row) and
`public-block-responsive-styles-10872` (`DECLARES_DATA_SOURCE` names
`object-chart`, the control built to redden exactly when an arm gains
the key). `strict-face-read-keys-11070` moves `columns` and
`object-chart` to the declared blocks and empties its PENDING list.
Comment-only: `deriveMasterDetail.declaredSpelling.test.tsx`, and the
two `GridField` tests now type their fixtures
`GridFieldMetadata['columns']`.

**Changesets.** One new (`@object-ui/types`, `@object-ui/fields` minor).
Dated notes on the five pending changesets of this release whose
sentence the round makes false: `11276-object-chart-properties-bag`,
`10872-flat-arm-responsive-styles`, `11070-strict-face-read-keys`,
`6138-fields-schema-block-parity-pr2`, `8209-datetime-widget-faces`.

## Census (i): the grid column, measured at `5262f7dd`

| source | shape |
|:--|:--|
| installed `@objectstack/spec` 17.5.0 | No `grid` field type and no
`columns` member on `FieldSchema`. The list is `inlineColumns` (on
`master_detail`), elements `InlineGridColumnSchema`: strict, 20 keys
(`name` required; `label`, `type` in nine values, `width`, `required`,
`options`, `prefix`, `step`, `reference`, `displayField`, `idField`,
`multiple`, `accept`, `defaultHidden`, `computed`, `expr`, `scale`,
`autofill`, `readonlyWhen`, `requiredWhen`). Named refusals: `field` /
`fieldName` / `key` (→ `name`), `title` / `header` (→ `label`), `size`,
`expression`, `hidden`; `scale` refused on a `currency` column. |
| `GridField`'s local `GridColumn` | The same 20 keys and the same
nine-value `type`. Value domain differs on `readonlyWhen` /
`requiredWhen` only (local object arm had an optional free-string
`dialect`). |
| what `GridField` reads | `cfg.columns`, and per column exactly those
20 keys (a census of member reads off the column: 20 distinct, 0 outside
the spec; no spread or computed read of a column). |
| `GridColumnDefinition` | 7 keys: `name`, `label`, `type` (required,
free string), `required`, `defaultValue`, `width`, `validate`. 2 keys
not in the spec, `type` wider. Production readers: 0 (two `fields` tests
and the docs prose). |
| `fields-grid/*` fixtures | 3 documents, 9 columns, keys `name` /
`label` / `type` (`text`, `number`, `currency`, `date`); 9 of 9 parse
against `InlineGridColumnSchema`. |
| `grid.mdx` | typed fence: a column `defaultValue`; plaintext fences:
`boolean` (2), `percent`, `user` column types; validation sketch: a
column `min`. |
| objectstack example apps (`94608a7d7`) | No `grid` field (the spec has
none). One producer of the spec element: `app-showcase`
`invoice.object.ts` `inlineColumns`, seven identity-only `{ name }`
entries. |

H1 holds in substance, not as posed: the spec declares no grid
`columns`; its `inlineColumns` element is key-for-key `GridColumn`. H2
holds: `GridColumnDefinition` had no production reader, so it retires.
No non-spec column spelling is read, so nothing else retires and no
producer stop applied. The alignment is a type reference, not a renderer
redesign.

## Census (ii): `object-chart` · `dataSource` on `5262f7dd`

| fact | reading |
|:--|:--|
| renderer read | the `object-chart` registration is
`elementDataSourceBlock`-wrapped; `ElementDataSourceGate` reads the
node's `dataSource` and lands `object` on `objectName` |
| producer | the react-page wrapper builds `{ ...props, specType, type
}`, no `dataSource` write (round 2) |
| strict face | `{ type: 'object-chart', properties: { objectName,
chartType }, dataSource: { object } }` refused, unrecognized key
`dataSource` |
| tolerant face | the same document, and `dataSource: null` or an
adapter object, accepted unjudged (the arm is passthrough) |

Not resolved by PR objectstack-ai#11328 or round 2, and no ruling needed: declared
like the seven other gate-wrapped blocks.

## M3 before / after

`scripts/measure-strict-authoring-face.mjs --json` plus the PR objectstack-ai#11069
`findUndeclaredKeys` port for pairs, each on a freshly built
`@object-ui/types`.

| reading | base `5262f7dd` | main `770cc5ba` | this head `62fe84ae` |
|:--|--:|--:|--:|
| documents | 601 | 601 | 601 |
| nodes | 2229 | 2229 | 2229 |
| strict-refused nodes | 94 | 94 | 91 |
| strict-only nodes | 63 | 63 | 60 |
| red today (tolerant) | 31 | 31 | 31 |
| documents refused whole-tree | 88 | 88 | 85 |
| port: pairs | 60 | 60 | 59 |
| port: tolerant-refused | 28 | 28 | 28 |

Main to head: exactly `form · fields[].columns` (3 occurrences, 3
strict-only, the three `fields-grid` documents) leaves; no pair enters
or moves, and the tolerant face refuses the same documents. H3 holds.
`object-chart` · `dataSource` has no corpus occurrence, so it moves no
M3 figure; its pins carry it.

## Verification

All on this branch; heavy runs through `os-verify-lock` with 2 workers
and a memory watchdog.

- `@object-ui/types`: `type-check` exit 0 and `vitest packages/types/`
309 files / 7828 tests passed at `62fe84ae`; build exit 0.
- `type-check` exit 0 in all 11 census packages (types, core,
components, fields, plugin-form, plugin-charts, plugin-dashboard,
plugin-detail, plugin-tree, plugin-grid, schema-catalog) at `3eecfc2c`.
- Tests at `3eecfc2c`: `packages/fields/` 226 files passed, 1 skipped
(3515 tests); `packages/plugin-form/` 157 / 1822;
`packages/plugin-charts/` + `examples/schema-catalog/` 121 / 3233; the
86 test files outside those packages that read the zod faces, 86 / 2110.
- Reverse type check: a temporary `plugin-form` file assigning a
`boolean` column, a column `defaultValue`, an adapter `dataSource` and a
dialect-less `readonlyWhen` object made `tsc` exit 2 with exactly those
four errors (TS2322, TS2353, TS2353, TS2322), and a spec-valid control
line none; the file was removed.
- Ablations, committed first, predicted in writing before each run,
through objectstack's `ablation-replace.mjs` (anchor 1 → 0, restore blob
== HEAD, `git diff HEAD` empty; subjects resolve from source):
- A1, item (i): drop `FormFieldSchema.columns`. Predicted 7 red + tsc
red; measured 7 failed / 52 passed, `tsc -p tsconfig.test.json` exit 2
(the parity ledger).
- A1b, docs leg: plant the column `defaultValue` back in `grid.mdx`'s
typed fence. Predicted 1 block red; measured `check:doc-snippets` 698
judged, 1 failed (TS2353 on `defaultValue`).
- A2, item (ii): drop `dataSource` from `ObjectChartBlockSchema`.
Predicted 5 red, tsc green; measured 5 failed / 321 passed, tsc exit 0.
- Gates at `62fe84ae`: `check:doc-snippets` 698 of 698 judged, 0 failed
(closure rebuilt, 35 of 35 tasks); `check:doc-fences`,
`docs:check-links`, `check:control-bytes`, `check:changeset-claims`,
`check:pending-changeset-literals`, `changeset:check`,
`check-changeset-presence`, `check:new-line-citations` (0 new),
`check:spec-symbols`, `check:test-path-roots`, `check:doc-types`,
`check:doc-examples`, `check:doc-example-ids` all exit 0.
`check-governed-queue-guard --test` over the 23 net paths: NOT GOVERNED.
- Narrowed lint: the population is `**/*.{ts,tsx}` (the config's
`files`), so 16 of the 23 changed paths; `eslint --no-inline-config
--format json` on those 16 at head and at the base: 0 new errors,
warnings equal per file except `field-types.ts` 5 → 4. The one error
(`index.ts`, `no-restricted-imports`) is present at the base too. The
config has no type-aware linting and no rule reads another file, so the
diff cannot move an untouched file's verdict.

## Notes

- **The `Spec Main Shape Gate`.** objectui#11330's repair (PR objectstack-ai#11343)
landed on `main` as `47e3ce00` and is merged into this branch, so its
fleet-wide red is not expected here; the result is CI's.
- **PR objectstack-ai#11333** (round 6) shares `packages/types/src/field-types.ts` on
disjoint hunks (`FormulaFieldMetadata` there, the grid metadata here).
This branch merges `main` again once it lands.
- **Not touched:** `strict-authoring-face.ts`, PR objectstack-ai#11333's surface,
`docs/adr/0001-master-detail-subform.md` (a governed record that still
names `GridColumnDefinition`).

## Acceptance notes

- `grid.mdx`'s typed fence still teaches `allow_reorder: false`.
`GridFieldMetadata` declares `allow_reorder`, and `GridField` reads
`reorderable`: a probe rendering the widget measured two drag handles
with no key, two with `allow_reorder: false`, and none with
`reorderable: false`. The widget's other field-level reads
(`total_field`, `amount_field`, `amountField`, `allow_duplicate`,
`show_line_numbers`, `sort_field`, `add_label`, `reorderable`) are
declared on neither face. These are field-level keys, outside this
round's `columns` scope; they are in the report for the seat.

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

---------

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

documentation Improvements or additions to documentation package: types tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants