Skip to content

feat(types): ObjectChartSchema accepts the react tier's object-chart node — the spec's { name } series arm, family as specType (objectui#10770) - #10802

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-10770-object-chart-name-arm
Sep 28, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-10770-object-chart-name-arm

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Fixes #10770
Clause-②: yes

What this does

The react tier's ObjectChart block authors @objectstack/spec's ChartConfigSchema. Its react-blocks entry has schemaType: 'object-chart' and schema: ChartConfigSchema, and lists type and series in its data props. The react-page wrapper turns the objectstack showcase renewals-pipeline page's block into an object-chart node shaped { specType: 'bar', series: [{ name: 'total', label: 'Invoice value' }], … }. It parks the author's type as specType because type is the node's discriminator. Both faces of ObjectChartSchema refused that node.

This aligns the contract face with the spec (ruling 5617465269 principle 1, objectui#7759):

  • series element: two arms. An entry is either the spec's ChartSeriesSchema (TS: ChartSeries), BY REFERENCE, which is the AUTHOR arm, or the unchanged INTERNAL { dataKey } arm that the relays compose.
  • specType is declared, as the spec's ChartTypeSchema (TS: ChartType), by reference.
  • chartType is optional. A zod .superRefine (requireObjectChartFamily) requires one of chartType or specType. That is the floor the required flag used to carry, so a node that names no family is still refused, at chartType. The TS interface cannot say "one of two"; the docblock states this as a ceiling.
  • Docblocks. The series verdict ("INTERNAL … All five producers compose it") is rewritten: the react-page wrapper is a sixth producer, and it forwards the authored array untouched.

⛔ There is no objectui-only dialect: both new crossings are spec symbols, bound by reference. ⛔ There is no renderer change: normalizeChartSchema already reads specType and translates the { name } arm (one-time probe below). No other schema was changed.

Step 1: does any door run this schema on the wrapper's node? No.

  • Render. SchemaRenderer runs core's structural validateSchema (development only), not zod. The react-page wrapper runs no zod.
  • Publish. objectstack's react-page gate (packages/lint/src/validate-react-page-props.ts, read at objectstack fdb2669) reads the block's binding VALUES and resolves series[].dataKey ?? series[].name; the only schemas it parses are ChartAggregateSchema and ChartDrillDownSchema. objectstack imports no @object-ui/types: every hit in its tree is prose.
  • objectui validate. It parses one JSON or YAML document. A react page's source is an opaque string: safeValidateSchema of a { type: 'page', kind: 'react', source } document carrying this block PASSES and never judges the block.
  • Other runtime importers. Outside the types package, @object-ui/types/zod is imported at runtime only by the CLI (validate, check) and by plugin-map (ObjectMapConfigSchema).

⇒ No user met the refusal. The defect was on the contract face, and in the docblock's INTERNAL-only claim. This reading gives no reason to raise the grade.

Before (unmodified face, a probe on the wrapper-built node): ObjectChartSchema.safeParse gives invalid_value at chartType and invalid_type at series.0.dataKey; safeValidateSchema gives the same two. After: it parses with zero issues on both.

Files

In the claim's file surface:

  • packages/types/src/objectql.ts and packages/types/src/zod/objectql.zod.ts, within the ObjectChartSchema block and its spec imports.
  • the new pin packages/types/src/__tests__/object-chart-react-tier-node-10770.test.ts. It pins that the showcase node parses (on the mirror and through safeValidateSchema), that a series entry with neither name nor dataKey and a node naming no family are refused, that the author arm is strict and specType takes every spec ChartTypeSchema value, and the TS twin.
  • .changeset/10770-object-chart-react-tier-node.md: @object-ui/types minor.
  • zod-mirror-parity.test.ts: no row moved (green, unedited).

Beyond the claim's surface. Each is a pin that the widening falsified, not a drive-by:

  • packages/types/src/__tests__/widget-schema-anchors-7946.test.ts: assertionSeriesBindsDataKey indexed ['dataKey'] on the element and went compile-red. It now reads the internal arm through Extract.
  • packages/types/src/__tests__/imported-defaults-8317.test.ts: its census failed by name ("a schema imported by a mirror is not in this file's IMPORTED list … Add it", naming ChartSeriesSchema). The schema was added to the list.
  • packages/plugin-charts/src/__tests__/ObjectChart.schemaAnchor-7946.test.ts: a consumer pin asserting the OPPOSITE (a @ts-expect-error that series: [{ name: 'amount' }] is refused). It went "Unused @ts-expect-error" once the rebuilt dist carried the union, which also serves as the reverse proof that the consumer compiled against the new declaration. It now pins the refusal that stands ([{ label: 'amount' }]). PR objectui#10787 does not touch this file (its file list was read).

Verification: first taken at 83f3e8662a, re-taken at the final head 77646f4150 (a merge of main only; see Patch round 1), pinned spec 17.4.0 unless noted

  • @object-ui/types: type-check exit 0; vitest run packages/types/ exit 0, 250 files / 5387 tests at 77646f4150 (248 / 5362 at 83f3e8662a); the new pin 13/13.
  • Reverse run of the new pin: both source files were put back to the base commit (requireObjectChartFamily count 3 then 0 on disk), and the pin went 8 failed / 5 passed. Restored with git checkout HEAD: git diff HEAD empty, blob hashes equal to HEAD, pin 13/13.
  • Consumers (direction: four named downstream consumers of @object-ui/types, not the whole ...@object-ui/types closure). Dependency closure built first.
    • @object-ui/plugin-charts: type-check exit 0 (after the pin move above); vitest exit 0, 83 files / 1010 tests at 77646f4150 (82 / 964 at 83f3e8662a).
    • @object-ui/components (holds the react-page wrapper): type-check exit 0; vitest exit 0, 318 files passed and 1 skipped / 3121 tests passed and 17 skipped at 77646f4150 (315 + 1 / 3092 + 17 at 83f3e8662a).
    • @object-ui/plugin-dashboard (types ObjectChartSchema['aggregate']): type-check exit 0; vitest exit 0, 140 files / 1298 tests at 77646f4150 (137 / 1284 at 83f3e8662a).
    • @object-ui/cli (validate / check run safeValidateSchema): type-check exit 0; vitest exit 0, 20 files / 279 tests.
  • Renderer: a one-time probe (not committed) ran normalizeChartSchema on the showcase node. It gave chartType: 'bar', xAxisKey: 'status' and series: [{ dataKey: 'total', label: 'Invoice value' }].
  • Gates, each with its exit code before any pipe: check-changeset-presence 0 · check-changeset-no-major 0 · check:new-line-citations 0 (0 new citations) · check:control-bytes 0 · check:spec-symbols 0 · check:test-path-roots 0 · check-governed-queue-guard --test (7 paths) 0, NOT GOVERNED · check:changeset-claims 0 (report-only; see Acceptance notes) · check:pending-changeset-literals 0 · check:handler-key-reads 0 · check:component-surface-parity 0 (report-only) · check:designer-field-key-parity 0 · check:element-data-source-declaration 0 · check:doc-types 0.
  • ESLint --no-inline-config on the 6 touched TS files: exit 0, 0 errors, 40 warnings (all no-explicit-any, no warnings ratchet). The file count (6) is read from --format json. Type-aware linting is not enabled in eslint.config.js (no projectService or parserOptions.project, and no custom rule reads parser services), so this diff cannot move a verdict on an untouched file.
  • Spec Main Shape Gate, narrowed. @objectstack/spec was built from objectstack main fdb2669 in a throwaway checkout, packed, and injected with node scripts/spec-main-shape-gate.mjs inject. Then: @object-ui/types type-check exit 0, types build exit 0, @object-ui/plugin-charts type-check exit 0. CI's gate type-checks the whole tree. The patch round then measured the whole tree locally too (below), and CI's gate is green at 77646f4150.

NOT MEASURED locally:

  • check:doc-snippets, check:doc-examples and check:skill-examples exit 2 with PRECONDITION NOT MET (the whole tree is unbuilt). Bound: no doc or skill types a value as ObjectChartSchema, and the only narrowing TS change is reading .dataKey off a series element.
  • The repo-wide pnpm lint / pnpm test runs, which belong to CI. (The forced whole-tree type-check was measured locally in the patch round.)

Patch round 1 (final head 77646f4150)

The first CI run's required Spec Main Shape Gate was cancelled at its 45-minute timeout in step 12, the whole-tree type-check. The patch round measured the cause, and it was a degraded runner, not this PR:

  • The whole-tree type-check (the gate's step-12 command, forced) takes the same time with and without this PR:

    • pinned: 472 s vs 480 s at the merge base;
    • spec main 17bd318: 485 s vs 488 s;
    • the objectstack commit the failed job compiled (e462186): 490 s, 81/81 tasks.

    The types program gains +0.2% instantiations, with no hot ObjectChartSchema construct in a --generateTrace.

  • The failed job was slow on every step, including steps that never read this diff: spec build 547 s vs 109–156 s on four neighbouring gate jobs, and pack 62 s vs 10–13 s.

  • No source changed. The only commit is a merge of origin/main 12809a595a. At 77646f4150 the gate is green in 12.1 min, and every CI job is green (Type Check 7.1 min). The seat's earlier diagnosis on this PR was corrected at 5858943102.

Acceptance notes

  • Mixed entry. An entry that writes both name and dataKey parses through the internal arm with name stripped. That is the internal arm's existing strip posture, unchanged; the renderer reads dataKey first, so the drawn column is the same.
  • Both family keys on one node. A node writing both chartType and specType is accepted, and normalizeChartSchema takes chartType first. It is not refused here, because that would be a new rule.
  • specType value domain. specType accepts all of the spec's ChartTypeSchema values. normalizeChartSchema leaves a family it does not draw (metric, table, …) unset, which is already pinned by normalizeChartSchema.test.ts ("leaves a family this renderer cannot draw unset"). This is a pre-existing seam between spec and renderer. Carrier: none; noted, not filed.
  • Publish-gate leniency. objectstack's react-page gate resolves series[].dataKey and xAxisKey on this block as legitimate, while the spec's ChartSeriesSchema refuses dataKey by name. That is leniency in objectstack's lint; it is not measured through os validate here. Carrier: none; noted, not filed.
  • Comparison guard. ObjectChart's chartTypeIgnoresCompareTo(schema.chartType) reads chartType only, not specType. compareTo is not a react-block prop, so this is dormant. Carrier: none; noted, not filed.
  • Instrument drift under main's zod. Under zod@4.6.1, which main's spec resolves, imported-defaults-8317.test.ts's "no member became REQUIRED" goes red for 16 schemas, 15 of them pre-existing plus ChartSeriesSchema. zod 4.6.1 reports _zod.optin === 'defaulted' for a member with a default, while its stripped twin reads 'optional'. Both are omissible, so this is instrument drift, not a narrowed accept set. It surfaces on the zod ^4.6.1 floor bump. Carrier: none (the next spec pin bump); noted, not filed.
  • Superseded pending changesets. The pending changesets for objectui#10584, the objectui#7946 anchor and objectui#8086 describe series as the internal { dataKey } arm only. They record their own PRs, and this changeset supersedes them in the same release, so they are not edited.
  • Card residue. The residue named on the card, app-shell ObjectView forwarding viewDef.chart.series verbatim, is untouched.

Implemented by the domain:spec#1 dispatch in session https://claude.ai/code/session_014fWVhLzhxR8qrFsJ5o8TYW.


Generated by Claude Code

…rt` node — the spec's `{ name }` series arm, and the family as `specType`

The react tier's `<ObjectChart>` block authors the spec's `ChartConfigSchema`
(`type`, `series: [{ name }]`). The react-page wrapper turns it into an
`object-chart` node `{ specType, series: [{ name, label }] }`, which both
faces of `ObjectChartSchema` refused: `chartType` was required and every
series entry needed `dataKey`.

- `series` element: the spec's `ChartSeriesSchema` / `ChartSeries` BY
  REFERENCE (the author arm), or the unchanged internal `{ dataKey }` arm.
- `specType`: declared, the spec's `ChartTypeSchema` / `ChartType` by
  reference. `chartType` is optional, and a zod refinement requires one of
  the two, so a node naming no family is still refused.
- Docblocks: the `series` verdict is no longer INTERNAL-only ("All five
  producers compose it" missed the wrapper, a sixth producer).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014fWVhLzhxR8qrFsJ5o8TYW
…Schema`, and move the two pins the widening touches

- New `object-chart-react-tier-node-10770.test.ts`: the showcase
  `renewals-pipeline` node parses on the mirror and through
  `safeValidateSchema`; a series entry with neither `name` nor `dataKey`,
  and a node naming no chart family, stay refused; the author arm and
  `specType` are the spec's own schemas (strict refusal of an unknown key,
  every `ChartTypeSchema` value); the TS twin carries the same contract.
- `widget-schema-anchors-7946.test.ts`: the `dataKey` type pin reads the
  internal arm through `Extract`, since the element is now a union.
- `imported-defaults-8317.test.ts`: `ChartSeriesSchema` joins the
  measured import boundary (its census asked for it by name).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014fWVhLzhxR8qrFsJ5o8TYW
… `{ name }` series arm is refused; changeset for the widening

`ObjectChart.schemaAnchor-7946.test.ts` pinned, at compile time, that
`series: [{ name: 'amount' }]` does not type as `ObjectChartProps['schema']`.
That is the refusal this card removes (the spec's author arm is now a member
of the element union), so the `@ts-expect-error` went unused. It now pins the
refusal that stands: an entry naming neither `name` nor `dataKey`. The
`.dataKey` read narrows first, since the element is a union.

Changeset: `@object-ui/types` minor (a published accept set widens; two TS
types move — see the changeset).

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

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 28 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 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.

.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/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 (objectui#6939, 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/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 (objectui#7804, 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.

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

.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/8478-describe-line-addresses.md

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

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

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 12809a595 (merge-base with origin/main): 6 file(s) changed outside .changeset/, read against 1619 pending declaration(s) that publish a body (2208 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3076.6 KB 3104.5 KB
Main entry chunk (gzip) 148.3 KB 350 KB
Entry file index-uyntmMqA.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.57KB 6.15KB
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.17KB 10.58KB
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.65KB 2.22KB
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.21KB 10.80KB
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) 556.35KB 133.07KB
core (index.js) 9.57KB 3.81KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 224.98KB 62.48KB
fields (index.js) 259.68KB 65.89KB
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.28KB 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.65KB 14.69KB
plugin-charts (index.js) 83.58KB 22.75KB
plugin-chatbot (index.js) 198.40KB 47.22KB
plugin-dashboard (index.js) 134.11KB 35.62KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 233.23KB 61.74KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 158.35KB 40.47KB
plugin-gantt (index.js) 169.75KB 41.96KB
plugin-grid (index.js) 218.12KB 59.72KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 114.70KB 28.42KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.91KB 9.05KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 87.83KB 22.01KB
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.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
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.25KB
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.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 ABSENT at head 83f3e8662a, so this PR stays draft and out of the queue. domain:spec#1 @ objectui · session_014fWVhLzhxR8qrFsJ5o8TYW · 2026-09-27T15:53Z.

  • Where the PR stands. ObjectChartSchema now accepts the spec's { name } series arm and specType, both by reference, and a refinement requires chartType or specType. It is Clause-②: yes, with an @object-ui/types minor. The dev measured that no render, publish or objectui validate path runs the schema on the react-tier node, so the grade holds. Three pins beyond the claim moved because the widening falsified them, and the PR body names them.
  • Why no record. The isolated at-tier reviewer could not start: its model returned HTTP 429, 「reached your Fable limit」. Under the contract-review rule, a reviewer that cannot start means the review is absent. The PR carries needs:contract-review and stays draft. The only bypass is the maintainer reviewing it personally.

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Required check red at head 83f3e8662a: Spec Main Shape Gate hit its 45-minute job timeout while type-checking. This PR is the cause, so a patch round is queued. domain:spec#1 @ objectui · session_014fWVhLzhxR8qrFsJ5o8TYW · 2026-09-27T16:34Z.

  • What failed. Job 108652348491 (run 36330849718). Steps 1–11 were green: the spec was built from objectstack main and injected. Step 12, 「Type-check objectui against it」 (TURBO_FORCE=true pnpm type-check --continue, whole tree), ran from 2026-09-27T15:59Z and was cancelled at 2026-09-27T16:32Z by timeout-minutes: 45.

  • Why it is this PR's. The same gate took 12.3 min in total on PR feat(types,plugin-view,app-shell)!: retire NamedListView.densityMode; both relays read the density through the fold (objectui#7924) #10793 (dd43c757c8) and 12.9 min on PR docs(skills): the plugin guide's tombstone comment lists the serializer key list with of (objectui#10337) #10780 (b57c8a84df); on those, the type-check step takes a few minutes. The regular Type Check, against the pinned 17.4.0, also slowed here: 10.4 min, against 6.9 min on feat(types,plugin-view,app-shell)!: retire NamedListView.densityMode; both relays read the density through the fold (objectui#7924) #10793. The one type-level change is this PR's by-reference union: the spec's ChartSeriesSchema / ChartSeries { name } arm, or the internal { dataKey } arm, plus specType. Against spec main's types, the union apparently drives a type-instantiation blow-up somewhere in the whole-tree check. The dev's narrowed local run of this gate covered only @object-ui/types and @object-ui/plugin-charts, so it did not see it.

  • Not a flake, so no re-run. It reached the type-check body and ran past four times the normal time.

  • Next. A patch round root-causes it:

    • find the package and the construct that explodes under the spec-main build (tsc --extendedDiagnostics / --generateTrace);
    • fix the type without narrowing the ruled accept set;
    • prove the fix with the full-tree gate within budget.

    The maintainer asked this seat to run one dev at a time, so the patch round starts when the seat's in-flight dev (objectui#7928) is done, ahead of the next card. The PR stays draft with needs:contract-review.


Generated by Claude Code

Patch round 1: sync with main at 12809a5 before the Spec Main Shape
Gate re-runs. No conflicts; the upstream edits to objectql.ts and
objectql.zod.ts sit outside the ObjectChartSchema block.

Claude-Session: https://claude.ai/code/session_014fWVhLzhxR8qrFsJ5o8TYW
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) 3078.7 KB 3104.5 KB
Main entry chunk (gzip) 148.3 KB 350 KB
Entry file index-BFOUPLZ4.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.57KB 6.15KB
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.17KB 10.58KB
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.65KB 2.22KB
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.21KB 10.80KB
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) 556.80KB 133.32KB
core (index.js) 9.92KB 3.93KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 224.95KB 62.49KB
fields (index.js) 260.77KB 66.22KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.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.28KB 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.67KB 14.70KB
plugin-charts (index.js) 83.58KB 22.75KB
plugin-chatbot (index.js) 197.67KB 46.90KB
plugin-dashboard (index.js) 134.64KB 35.75KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 233.12KB 61.68KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 158.35KB 40.47KB
plugin-gantt (index.js) 169.83KB 41.99KB
plugin-grid (index.js) 218.66KB 59.90KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 114.78KB 28.44KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.91KB 9.05KB
plugin-tree (index.js) 10.64KB 3.75KB
plugin-view (index.js) 87.89KB 22.03KB
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.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
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.25KB
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.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

Correction to this seat's comment 5857715528: its diagnosis was wrong. The cancelled Spec Main Shape Gate was a degraded runner, not this PR. domain:spec#1 @ objectui · session_014fWVhLzhxR8qrFsJ5o8TYW · 2026-09-27T19:15Z.

5857715528 said 「This PR is the cause」 and 「Not a flake, so no re-run」. The patch round measured the opposite. Its report is the newest os-dev-report on objectui#10770.

  • No type cost from this PR. The gate's step-12 command, a forced whole-tree turbo run type-check, takes the same time with and without the PR:

    • pinned 17.4.0: 472 s vs 480 s at the merge base;
    • spec main 17bd318: 485 s vs 488 s;
    • the exact objectstack commit the failed job compiled (e462186): 490 s, 81/81 tasks, 0 TS errors.

    The types program gains +0.2% instantiations, and a --generateTrace shows no hot ObjectChartSchema construct.

  • The runner was slow on every step, including steps that never read this PR's diff. Failed job 108652348491 against four neighbouring gate jobs:

    step failed job neighbours
    spec build from objectstack 547 s 109–156 s
    pack 62 s 10–13 s
    setup-node 34 s 6–12 s
    checkout 12 s 2–5 s

    A uniform 3–6× slowdown is enough to push the type-check past the 45-minute ceiling. The regular Type Check at 10.4 min matched a docs-only PR running at the same time (10.4 min).

  • Where it stands. Head 77646f4150 is a merge of main only; no source changed. The gate is green in 12.1 min, and every CI job is green (Type Check 7.1 min).

The seat's error was comparing whole-job durations without the per-step control that tells a degraded runner apart from a PR-caused blow-up. The PR still needs its at-tier review (needs:contract-review), which is unchanged by this correction.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 77646f4150f1d8ffcc459447f6696b2138ad5dbb

① Derived judgments

Diff read three-dot against objectui origin/main a7fd877c3c (merge-base 12809a595a): 7 files, +436 / −62. The head is the reviewed increment 83f3e8662a plus one merge of main; the increment is unchanged (the patch round changed no source, 5858924713).

  1. The accept set widens on ObjectChartSchema in three places, each bound to the spec by reference. (i) series element becomes z.union([stripImportedDefaults(SpecChartSeriesSchema), <the internal { dataKey } object>]): the author arm is @objectstack/spec's ChartSeriesSchema (a strictObject, chart.zod.ts:238, name the column, dataKey refused by name), the internal arm is byte-unchanged (still without the per-series type, per objectui#10584). (ii) specType: stripImportedDefaults(SpecChartTypeSchema).optional(), the spec's ChartTypeSchema enum (chart.zod.ts:110). (iii) chartType becomes optional, and .superRefine(requireObjectChartFamily) refuses a node with neither key, issue at ['chartType']. The TS twin follows (chartType?, specType?: SpecChartType, series?: Array<SpecChartSeries | { dataKey … }>); the spec exports both types (chart.zod.ts:181, :1029).
  2. The producer the old verdict missed is real. packages/components/src/renderers/layout/react-page.tsx:97-99 spreads a react block's props into the node and parks a type that differs from the tag as specType; the spec's react-blocks entry for object-chart has schema: ChartConfigSchema with type and series among its data props (pinned by the new test against the installed spec); the objectstack showcase writes series={[{ name: 'total', label: 'Invoice value' }]}. So the wrapper is a sixth producer, and the "INTERNAL, five producers" docblock was false for it; the docblock is rewritten to two arms, one verdict each.
  3. No renderer change was needed, and none was made. normalizeChartSchema.ts:607-610 already reads chartType ?? specType ?? type … (so chartType wins when a node writes both, as the describe says) and translates the { name } arm.
  4. The floor is kept and the widening is exact. Pins in object-chart-react-tier-node-10770.test.ts: the showcase node parses with zero issues on the mirror and through safeValidateSchema; the authored series comes back as written (no spec default injected); an entry with neither name nor dataKey → invalid_union at series.0; a node with neither family key → custom at chartType; either key alone parses; the author arm is strict (an unknown field refused by name); specType takes every ChartTypeSchema value and refuses a bogus one; the internal arm is unchanged; two @ts-expect-error probes on the TS face.
  5. Doors, measured by the dev and read here. No render path runs zod on the wrapper's node (SchemaRenderer runs the structural validateSchema), objectstack's react-page publish gate parses only aggregate and drill-down schemas, and objectui validate treats a react page's source as opaque; the runtime importers of @object-ui/types/zod are the CLI and plugin-map. So no user met the old refusal; it was on the contract face and in the docblock, which is why minor is the right grade for a widening.
  6. Three pins beyond the claim moved because the widening falsified them, none deleted: ObjectChart.schemaAnchor-7946 narrows with 'dataKey' in first and its bad entry becomes { label } (since { name } is now legal); widget-schema-anchors-7946 uses Extract<…, { dataKey: string }>; imported-defaults-8317 registers the new crossing ChartSeriesSchema.
  7. Mergeability and CI. git merge-tree --write-tree origin/main refs/pull/10802/head at a7fd877c3c exits 0; pairwise clean with feat(types,plugin-view,app-shell)!: retire NamedListView.densityMode; both relays read the density through the fold (objectui#7924) #10793; conflicts with feat(types,plugin-view,app-shell)!: ObjectViewSchema.listViews mirrors the spec's ObjectListViewSchema by reference; ViewPreview folds a stored options bag (objectui#7928) #10821 in imported-defaults-8317.test.ts (both register a crossing) — ③. Check runs on 77646f4150: 40 success, 3 skipped, 0 red; the cancelled Spec Main Shape Gate at the previous head was a degraded runner (every step 3-6× slower, including the spec build that never reads this diff), measured per step by the patch round and corrected by the seat at 5858943102; green at head in 12.1 min.

② Semver level

'@object-ui/types': minor, Clause-②: yes — a runtime widening, with the two TS-face moves that can stop a consumer compiling stated in the changeset (chartType now includes undefined; a series element is a union, so .dataKey needs narrowing). Every changeset sentence checks out against items 1-6, including "no render, publish or objectui validate path ran ObjectChartSchema on the wrapper's node".

③ Boundary flags

Implemented-by: claude/issue-10770-object-chart-name-arm
Reviewed-by: session_01AsCNgFBs8HCjwhyHQsFbx3

VERDICT: PASS

Rendered at tier by the director seat (summon #30 续 2) on the fetched head. Not a governed path; the landing pre-checks are met (fresh PASS, CI green, no conflict with main), so needs:contract-review comes off and ready + auto-merge follow through the relay.


Generated by Claude Code

Merged via the queue into main with commit b956e69 Sep 28, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10770-object-chart-name-arm branch September 28, 2026 00:04
os-tesla pushed a commit that referenced this pull request Sep 28, 2026
…hart-series claims PRs #10793, #10802 and #10821 made false

Each false sentence gets a dated supersession note beside it, in the
objectui#10533 / objectui#10801 form, naming the superseding entry by
file and pull request. Frontmatter is byte-identical and no line is
deleted; the still-true sentences, and all of the objectui#8086 entry,
are untouched.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014fWVhLzhxR8qrFsJ5o8TYW
os-tesla pushed a commit that referenced this pull request Sep 28, 2026
…s whose listViews and series claims PRs #10821 and #10802 made false

Patch round 1 on the file-surface addendum: the same note form as the
ten notes already on this branch, one note beside each false sentence,
naming the superseding entry by file and pull request. Frontmatter is
byte-identical, no line is deleted, and the still-true sentences are
untouched.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014fWVhLzhxR8qrFsJ5o8TYW
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Sep 28, 2026
…ts name fourteen objectui issues that answer 404 (objectui#10803) (objectstack-ai#10854)

Part of objectstack-ai#10803
Clause-②: no

Dispatched implementation of the `domain:ui` seat objectstack-ai#1 claim (comment
`5859894652`) on objectui#10803, session
`https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk`. Citations
only: no sentence's claim moves and every edited pending changeset's
frontmatter is byte-identical. The only code tokens that move are eleven
zod `.describe()` strings in `@object-ui/types`, each losing its dead
pointer (patch round, under the seat's claim amendment `5860244997`).

## Why `Part of`, not a closing line

The enumeration pin for the fourteen now reads 0. The card stays open
because the seat's amendment `5860244997` (Q2) keeps the 115-number
remainder of the family on it, to be carried by later PRs after this one
lands (see **Acceptance notes**).

## Premise, re-measured

- REST `GET /repos/objectstack-ai/objectui/issues/N` and `/pulls/N`: all
fourteen (objectstack-ai#5489, objectstack-ai#5498, objectstack-ai#5853, objectstack-ai#6938, objectstack-ai#6939, objectstack-ai#6950, objectstack-ai#7070, objectstack-ai#7071, objectstack-ai#7628,
objectstack-ai#7632, objectstack-ai#7635, objectstack-ai#7688, objectstack-ai#9318, objectstack-ai#9473) answer 404 on both. Lit controls:
objectui#10533 and objectui#7714 answer 200.
- The same fourteen in objectstack all answer 200. Thirteen are
unrelated subjects. objectstack#6938 IS the `bodyShape` forwarding
subject, and the seven in-scope lines that name it already spell
`objectstack#6938`: live citations, which the card's instrument counted
because its bare `#` branch matches inside `objectstack#6938`. They are
untouched. No objectui site here is a mis-attributed objectstack number,
so no site is re-qualified.
- Pending means pending: no `CHANGELOG.md` names any of the fourteen (42
changelogs; control: `packages/core/CHANGELOG.md` carries 140
`objectui#` mentions), so all 47 changesets edited here are pending.
- Base: `195052fff` (origin/main at dispatch; it carries the four PRs
the deferral waited on). The census there reads the card's own numbers,
below.

## Census (the enumeration pin)

The pin is this re-count, as PR objectui#10797 recorded its own. The
instrument, reproducible as printed (REF = a commit):

```
git grep -nE '(objectui#|#|issues/)(5489|5498|5853|6938|6939|6950|7070|7071|7628|7632|7635|7688|9318|9473)([^0-9]|$)' REF -- '.changeset/*.md' 'packages/*/src/**' ':!**/__tests__/**' ':!**/*.test.*' ':!**/*.spec.*' ':!**/test/**' ':!**/tests/**' | grep -v 'objectstack#' | wc -l
```

- REF = `195052fff`: **224** lines, 66 changeset lines in 47 files and
158 src-nontest lines in 48 files.
- REF = `4906c4408` (this head): **0** lines. Round 1's head `6c3ad7c80`
read 11, every one a `.describe(` line; patch round 1 (`19da7f8c0`) took
them, and it read 0.
- This head merged with `main` at `4aebea00a` (`git merge-tree`, clean):
0.
- Lit control, the same printed instrument over live objectui#7714 at
this head: 17 lines (20 without the `grep -v 'objectstack#'` filter,
because three of those lines also name an `objectstack#` number).
Hex-colour false positives (the pattern followed by a hex letter): 0.
- The card's own instrument (no `objectstack#` filter) reads 67
changeset lines in 48 files and 164 src-nontest lines in 54 files at the
base, the card's figures exactly; the extra 1 and 6 are the
`objectstack#6938` lines. At this head it reads 1 and 6: those 7, all
live.
- Out of scope, as it stands (card's instrument, unchanged by this PR):
223 test lines in 80 files, 6 scripts lines in 2 files, 1 `.github`
line, 0 governed, 6 other lines in 6 files.

## Citation form

- The 9-character backticked sha of the commit on `main` that landed the
change the sentence rests on (the objectui#10707 / objectstack-ai#10766 / objectstack-ai#10797
form). Commits, not PRs: PR 7628, PR 7660 (the objectstack-ai#7632 landing) and PR
5530 (the objectstack-ai#5498 landing) answer 404 themselves. All 20 shas are
ancestors of the base (`git merge-base --is-ancestor`, exit 0 each;
control leg: the head of PR objectui#10851 answers exit 1) and `git
rev-parse --short=9` returns the same 9 characters for each.
- A changeset's pointer to its OWN card is dropped, not replaced:
`@changesets/changelog-git` prefixes each released entry with the hash
of the commit that added the file, which is that landing.
- Where the sentence cites a RULING recorded on a dead card and no
single commit is the ruling, the ruling is cited by its date, the form
the same sentences already use: "the 2026-09-02 ruling" for the
eight-group ruling on objectstack-ai#6939, "the 2026-09-01 ruling (总监批 objectstack-ai#28)" for the
one on objectstack-ai#7070.
- Inside a zod `.describe()` string the dead pointer is dropped, not
replaced, in the PR objectui#10707 shape: a reader of a runtime
description has no repository to resolve a sha against.
- Where the sentence says something was reported to a dead card, the
live successor card that carried it is named (objectui#7561,
objectui#7562).

## Number to resolution

| dead number | resolution | evidence |
|:--|:--|:--|
| objectstack-ai#5489 | `78cbdb530` | "retire ThemeComponentSchema, the `type:
'theme'` component kind no renderer implements (objectstack-ai#5489)"; it added
`retire-theme-component-schema-5489.md`. |
| objectstack-ai#5498 | `4bb940b6e` | "Readonly markdown/html/richtext form fields
render formatted, not their markup source (objectstack-ai#5498)"; it added
`readonly-richtext-raw-markup-5498.md` and moved the renderers into
`widgets/richTextDisplay.tsx`. PR 5530 answers 404. |
| objectstack-ai#5853 | `fc62bb490` | "make the 8-literal union the one canonical
TableColumn.type" (PR 6370); it added
`5853-tablecolumn-type-canonical-union.md`, `TABLE_COLUMN_TYPES` and
`normalizeTableColumnType`, citing the 2026-08-25 Option B ruling. |
| objectstack-ai#6938 | `b74a8598d` | "declare `wrapperClass` on `CheckboxSchema`, on
both faces" (PR 7723); it added `6938-checkbox-wrapper-class.md` and
`checkbox-wrapper-class-6938.test.ts`. The `objectstack#6938` sites are
live and untouched. |
| objectstack-ai#6939, tooltip / context-menu group | `bfaa1589c` | "declare what the
tooltip / context-menu renderers read" (PR 7456), "Part of objectstack-ai#6939 ... one
of eight groups"; added `6939-overlay-trigger-mirror.md`. |
| objectstack-ai#6939, tree-view group | `777e5c6f4` | "tree-view mirror stops
requiring the limb it reads third (group 2 of objectui#6939)" (PR 7533).
|
| objectstack-ai#6939, kanban group | `240b80f31` | "rename KanbanColumn.items to
cards on both published halves" (PR 7541); its message carries the 12 /
8 read-site measurement the `complex.ts` docblock quotes. |
| objectstack-ai#6939, filter-builder group | `d4493fdbc` | "the filter-builder mirror
names the keys and the vocabulary its renderer reads" (PR 7560); added
`6939-filter-builder-mirror.md`. |
| objectstack-ai#6939, object-map / object-gantt group (the record-source ladder and
`requireRecordSource`) | `77cb489b4` | "object-map / object-gantt —
objectName optional behind a record-source refinement (objectui#6939,
group 6 of 8)" (PR 7471); added
`objectql-record-source-refinement-6939.test.ts`, which `ce2aaefe1`'s
message names as the pin of the published ladder. |
| objectstack-ai#6939, chart row | `5f789538d` | "ChartSchema declares the data model
it renders" (PR 7545); added `7113-chart-data-model.md` and the series
binding refinement. |
| objectstack-ai#6939, the ruling itself and its grading language | "the 2026-09-02
ruling" | Recorded 2026-09-02 (director seat, summon objectstack-ai#8, decision batch
objectstack-ai#8) per `77cb489b4`; landed per group, the last row by `fd5b09aaf` (PR
9558). |
| objectstack-ai#6950 | `9e37d9b39` | "type the framework-injected `binding` input
instead of casting it in" (PR 8297); added
`6950-binding-framework-injected-input.md`. |
| objectstack-ai#7070, gantt dates | `5f4514f7b` | "no invented gantt date field
names, so the refusal screen becomes reachable" (PR 7110); added
`7070-no-invented-gantt-date-fields.md`, wrote the "only ever restate a
binding the view actually DECLARED" lines, the app-shell sibling-branch
note, and "re-expressed rather than deleted" for the control its change
retired. |
| objectstack-ai#7070, timeline step ③ | `04a67b9dc` | "retire the 'created_at'
timeline date-axis floors" (PR 7500), "objectui#7070 step ③ of the
maintainer ruling of 2026-09-01 (总监批 objectstack-ai#28)"; added
`7070-timeline-date-axis-floors-retired.md`. |
| objectstack-ai#7070, the ruling | "the 2026-09-01 ruling (总监批 objectstack-ai#28)" | The sentences
already name it by date and batch. |
| objectstack-ai#7071 | `bc5870c9f` | "a record with no date leaves the grid for an
'unscheduled' area" (PR 7453); "Ruled on objectui#7071 by the maintainer
(2026-09-01 ... re-confirmed 2026-09-02)". |
| objectstack-ai#7628 (a PR, not a card) | `00d2fa682` | The squash of PR 7628, which
answers 404: "fold ListView's `object` data provider onto `objectName`",
"released by the maintainer's ruling B". |
| objectstack-ai#7632 | `ce2aaefe1` | "one shared record-source ladder, five plugins
delegate (objectui#7632)"; PR 7660 answers 404. |
| objectstack-ai#7635 | `544ecba84` | "docblocks stop asserting a refusal the spec no
longer performs" (PR 9588); its body names objectstack-ai#7635 as the card it settles.
Already mapped by the triage. |
| objectstack-ai#7688 | nothing answers | A finding card that recorded the
`[floor-too-low]` finding the opposite way round. No commit lands it and
no successor carries it. See **Special cases**. |
| objectstack-ai#9318 | `e3cb47624` | "the field-rule wrong-layer verdict comes from
@objectstack/lint, not a second copy of it" (PR 9366); added
`9318-field-rule-verdict-from-lint.md` and
`celAuthoring.fieldRuleVerdict-9318.test.ts`. |
| objectstack-ai#9473 | `ab856ed30` | "type `resolveRecordSourceConfig`'s `data` by
the arm it is already told" (PR 9967); its body names objectstack-ai#9473 as the card
it settles, and it removed the calendar cast. |

## Special cases (the judgement calls)

1. **objectstack-ai#7688.** "and objectui#7688 records the opposite" becomes "and the
card that filed the `[floor-too-low]` finding recorded the opposite",
and "The correction is recorded on objectui#7688." becomes "The
correction is recorded in `639114c4d`, the commit that landed this
entry." That commit's message carries the measured correction verbatim.
2. **`6939-filter-builder-mirror.md`.** "it is reported on objectui#6939
rather than decided here" and "it is reported on the same card" become
the two live successors that carried them: objectui#7561 (the operator
vocabulary fork and the blank trigger) and objectui#7562 (the seven
refused field types). Both answer 200 and their titles match.
3. **"A control is restated, never deleted into a vacuum
(objectui#7070)"** (two changesets, `plugin-chatbot/renderer.tsx`,
`types/complex.ts`) cites `5f4514f7b`, whose message states that
discipline for the control it retired ("re-expressed rather than
deleted").
4. **`plugin-gantt/ObjectGantt.tsx`.** "the fabrication objectui#7070
spent this file's other branches removing" becomes "the fabrication the
invented-binding work (`5f4514f7b`) spent this file's other branches
removing". The claim is the original author's and is not re-judged here:
`5f4514f7b` itself adds a test in that package, not a change to that
file.
5. **`6150-undeclared-but-consumed-keys.md`.** Its sentence describes
the `nodes` describe text ("records that a `nodes`-only document became
legal ..."). The pointer becomes "(at `777e5c6f4`)", so the sentence
stays true now that the patch round has taken the pointer out of that
string too.
6. **`7070-no-invented-gantt-date-fields.md`.** "which objectui#7070
routes to a ruling rather than settling per-face" becomes "which is
routed to a ruling rather than settled per-face": the changeset's own
card, so the pointer is dropped and the sentence becomes passive.
7. **`8913-object-kanban-columns-declared.md`** and the
`ObjectKanbanLaneSchema` docblock: "restores objectui#6939's finding /
judging" becomes "restores what the kanban repair `240b80f31`
established" / "the judging `240b80f31` established".
8. **`ObjectGanttSchema.data`'s description** (patch round) was "read
FIRST by resolveRecordSourceConfig; undeclared on either face until
objectui#6939". Dropping only the pointer would leave a dangling
"until", and dropping "until" too would make the string say the key is
undeclared, which is false. The clause goes with its pointer, leaving
"Data source configuration — read FIRST by resolveRecordSourceConfig":
the same shape as its `ObjectMapSchema.data` twin, which reads "Data
source configuration — read FIRST by getDataConfig". It is the only one
of the eleven where more than the pointer and its connective (" at", "
(", ", ") left the string.

## Per file

Each row: the dead numbers the file carried, the lines changed, and what
they cite now. The diff is the site-by-site record.

| file | dead number(s) | lines | now cites |
|:--|:--|--:|:--|
| `.changeset/5853-tablecolumn-type-canonical-union.md` | objectstack-ai#5853 | 1 |
own pointer dropped |
| `.changeset/6004-grid-column-emit-boundary.md` | objectstack-ai#5853 | 1 |
`fc62bb490` |
| `.changeset/6150-undeclared-but-consumed-keys.md` | objectstack-ai#6939 | 2 |
`777e5c6f4` |
| `.changeset/6373-datatable-emit-boundary.md` | objectstack-ai#5853 | 1 | `fc62bb490`
|
| `.changeset/6594-headercolor-mirror-enum.md` | objectstack-ai#5853 | 2 | `fc62bb490`
|
| `.changeset/6938-checkbox-wrapper-class.md` | objectstack-ai#6938, objectstack-ai#6939 | 2 | own
pointer dropped; `bfaa1589c` |
| `.changeset/6939-filter-builder-catalog-operator-spellings.md` | objectstack-ai#6939
| 1 | own pointer dropped |
| `.changeset/6939-filter-builder-mirror.md` | objectstack-ai#6939 | 5 | own pointer
dropped; the 2026-09-02 ruling; live successors objectui#7561,
objectui#7562 |
| `.changeset/6939-kanban-column-cards.md` | objectstack-ai#6939 | 1 | own pointer
dropped |
| `.changeset/6939-objectql-record-source-refinement.md` | objectstack-ai#6939 | 2 |
own pointer dropped |
| `.changeset/6939-overlay-trigger-mirror.md` | objectstack-ai#6939 | 2 | own pointer
dropped |
| `.changeset/6939-tree-view-nodes-mirror.md` | objectstack-ai#6939 | 2 | own pointer
dropped |
| `.changeset/6950-binding-framework-injected-input.md` | objectstack-ai#6950 | 1 |
own pointer dropped |
| `.changeset/6951-tree-view-data-retired.md` | objectstack-ai#6939 | 1 | `777e5c6f4`
|
| `.changeset/7014-select-option-spec-claim-correction.md` | objectstack-ai#7635 | 1 |
`544ecba84` |
| `.changeset/7070-no-invented-gantt-date-fields.md` | objectstack-ai#7070 | 2 | own
pointer dropped |
| `.changeset/7070-timeline-date-axis-floors-retired.md` | objectstack-ai#7070 | 1 |
own pointer dropped; the 2026-09-01 ruling |
| `.changeset/7083-richtext-field-metadata.md` | objectstack-ai#5498 | 1 | `4bb940b6e`
|
| `.changeset/7113-chart-data-model.md` | objectstack-ai#6939 | 4 | the 2026-09-02
ruling (its `chart` row landed in this entry's own commit) |
| `.changeset/7122-spec-17-3-0-parity-reconciliation.md` | objectstack-ai#7688 | 2 |
`639114c4d` (nothing answers objectstack-ai#7688; see below) |
| `.changeset/7295-chat-message-avatar-keys.md` | objectstack-ai#6938 | 1 |
`b74a8598d` |
| `.changeset/7313-object-calendar-record-source.md` | objectstack-ai#6939 | 1 |
`77cb489b4` |
| `.changeset/7459-timeline-absent-date-axis-refusal.md` | objectstack-ai#7070 | 4 |
`04a67b9dc`, `5f4514f7b` |
| `.changeset/7470-designer-objectname-not-required.md` | objectstack-ai#6939 | 1 |
`77cb489b4` |
| `.changeset/7499-gantt-non-axis-floors-omitted.md` | objectstack-ai#7070 | 1 |
`5f4514f7b` |
| `.changeset/7546-chart-series-keys-declared.md` | objectstack-ai#6939 | 2 |
`5f789538d` |
| `.changeset/7547-forced-path-invented-bindings.md` | objectstack-ai#7070 | 1 |
`5f4514f7b` |
| `.changeset/7562-filter-field-type-fourteen-members.md` | objectstack-ai#6939 | 1 |
`d4493fdbc` |
| `.changeset/7627-shared-record-source-reader.md` | objectstack-ai#6939, objectstack-ai#7628 | 2 |
`00d2fa682`, `77cb489b4` |
| `.changeset/7632-shared-record-source-config.md` | objectstack-ai#6939, objectstack-ai#7632 | 2 |
own pointer dropped; `77cb489b4` |
| `.changeset/7638-navigation-url-follows-record-source.md` | objectstack-ai#6939 | 2
| `77cb489b4` |
| `.changeset/7654-chatbot-display-mode-tombstone.md` | objectstack-ai#7070 | 1 |
`5f4514f7b` |
| `.changeset/7694-chart-series-chart-type-alias-refusal.md` | objectstack-ai#6939 | 1
| `5f789538d` |
| `.changeset/7703-chatbot-dark-keys-retired.md` | objectstack-ai#7070 | 1 |
`5f4514f7b` |
| `.changeset/7722-wrapper-class-five-more.md` | objectstack-ai#6938 | 3 | `b74a8598d`
|
| `.changeset/7780-object-kanban-record-source.md` | objectstack-ai#6939 | 1 |
`77cb489b4` |
| `.changeset/8072-input-wrapper-class-mirrored.md` | objectstack-ai#6938 | 2 |
`b74a8598d` |
| `.changeset/8169-map-refuses-unbound.md` | objectstack-ai#7070 | 1 | the 2026-09-01
ruling |
| `.changeset/8284-content-channel-per-component.md` | objectstack-ai#6939 | 1 |
`bfaa1589c`; "Seven earlier cards" now reads "Seven earlier changes",
since the list carries a commit (patch round 2) |
| `.changeset/8315-sdui-parser-manifest-binding-field-retired.md` |
objectstack-ai#6950 | 1 | `9e37d9b39` |
| `.changeset/8802-8257-8008-kanban-gantt-family-retirement.md` | objectstack-ai#6939
| 1 | `240b80f31` |
| `.changeset/8913-object-kanban-columns-declared.md` | objectstack-ai#6939 | 1 |
`240b80f31` |
| `.changeset/9318-field-rule-verdict-from-lint.md` | objectstack-ai#9318 | 1 | own
pointer dropped |
| `.changeset/9618-record-source-ladder-attribution.md` | objectstack-ai#7632 | 1 |
`ce2aaefe1` |
| `.changeset/readonly-richtext-raw-markup-5498.md` | objectstack-ai#5498 | 1 | own
pointer dropped |
| `.changeset/retire-theme-component-schema-5489.md` | objectstack-ai#5489 | 1 | own
pointer dropped |
| `.changeset/retire-theme-switcher-preview-5647.md` | objectstack-ai#5489 | 1 |
`78cbdb530` |
| `app-shell/…/views/ObjectView.tsx` | objectstack-ai#7070 | 8 | `5f4514f7b` |
| `app-shell/…/views/metadata-admin/CelPredicateField.tsx` | objectstack-ai#9318 | 1 |
`e3cb47624` |
| `app-shell/…/views/metadata-admin/celAuthoring.ts` | objectstack-ai#9318 | 7 |
`e3cb47624` |
| `app-shell/…/views/metadata-admin/clientValidation.ts` | objectstack-ai#9318 | 1 |
`e3cb47624` |
| `app-shell/…/views/metadata-admin/inspectors/ObjectFieldInspector.tsx`
| objectstack-ai#9318 | 3 | `e3cb47624` |
| `components/…/renderers/complex/data-table.tsx` | objectstack-ai#5853 | 2 |
`fc62bb490` |
| `core/…/data-scope/element-data-source.ts` | objectstack-ai#6950 | 1 | `9e37d9b39` |
| `core/…/index.ts` | objectstack-ai#6939, objectstack-ai#7632 | 2 | `77cb489b4`, `ce2aaefe1` |
| `core/…/registry/Registry.ts` | objectstack-ai#6950 | 2 | `9e37d9b39` |
| `core/…/utils/record-source.ts` | objectstack-ai#6939, objectstack-ai#7628, objectstack-ai#7632, objectstack-ai#9473 | 5 |
`00d2fa682`, `77cb489b4`, `ab856ed30`, `ce2aaefe1` |
| `fields/…/coerceToSafeValue.ts` | objectstack-ai#5498 | 1 | `4bb940b6e` |
| `fields/…/index.tsx` | objectstack-ai#5498 | 3 | `4bb940b6e` |
| `fields/…/widgets/RichTextField.tsx` | objectstack-ai#5498 | 3 | `4bb940b6e` |
| `fields/…/widgets/richTextDisplay.tsx` | objectstack-ai#5498 | 1 | `4bb940b6e` |
| `plugin-calendar/…/ObjectCalendar.tsx` | objectstack-ai#6939, objectstack-ai#7071, objectstack-ai#9473 | 8 |
`77cb489b4`, `ab856ed30`, `bc5870c9f` |
| `plugin-chatbot/…/renderer.tsx` | objectstack-ai#7070 | 1 | `5f4514f7b` |
| `plugin-dashboard/…/ObjectDataTable.tsx` | objectstack-ai#5853 | 3 | `fc62bb490` |
| `plugin-gantt/…/ObjectGantt.tsx` | objectstack-ai#7070 | 1 | `5f4514f7b` |
| `plugin-gantt/…/index.tsx` | objectstack-ai#6939 | 1 | `77cb489b4` |
| `plugin-grid/…/ObjectGrid.tsx` | objectstack-ai#5853, objectstack-ai#6939, objectstack-ai#7632 | 6 |
`77cb489b4`, `ce2aaefe1`, `fc62bb490` |
| `plugin-list/…/ListView.tsx` | objectstack-ai#7070 | 7 | `04a67b9dc`, `5f4514f7b` |
| `plugin-map/…/ObjectMap.tsx` | objectstack-ai#6939, objectstack-ai#7070, objectstack-ai#7632 | 4 | `77cb489b4`,
`ce2aaefe1` |
| `plugin-map/…/index.tsx` | objectstack-ai#6939 | 1 | `77cb489b4` |
| `plugin-timeline/…/ObjectTimeline.tsx` | objectstack-ai#7070 | 2 | the 2026-09-01
ruling (总监批 objectstack-ai#28), already named beside it |
| `plugin-timeline/…/useTimelineTranslation.ts` | objectstack-ai#7070 | 1 | the
2026-09-01 ruling (总监批 objectstack-ai#28) |
| `plugin-tree/…/ObjectTree.tsx` | objectstack-ai#6939 | 1 | `77cb489b4` |
| `plugin-view/…/ObjectView.tsx` | objectstack-ai#7070 | 3 | `04a67b9dc`, `5f4514f7b`
|
| `react/…/hooks/useNavigationOverlay.ts` | objectstack-ai#6939 | 1 | `77cb489b4` |
| `sdui-parser/…/index.ts` | objectstack-ai#6950 | 1 | `9e37d9b39` |
| `sdui-parser/…/types.ts` | objectstack-ai#6950 | 1 | `9e37d9b39` |
| `types/…/base.ts` | objectstack-ai#6950 | 6 | `9e37d9b39` |
| `types/…/complex.ts` | objectstack-ai#6939, objectstack-ai#7070 | 6 | `240b80f31`, `5f4514f7b`,
`d4493fdbc` |
| `types/…/data-display.ts` | objectstack-ai#5853, objectstack-ai#6938, objectstack-ai#6939 | 7 | `5f789538d`,
`777e5c6f4`, `b74a8598d`, `fc62bb490` |
| `types/…/field-types.ts` | objectstack-ai#5498, objectstack-ai#7635 | 6 | `4bb940b6e`, `544ecba84`
|
| `types/…/form.ts` | objectstack-ai#6938 | 5 | `b74a8598d` |
| `types/…/index.ts` | objectstack-ai#5489, objectstack-ai#5853, objectstack-ai#6950 | 3 | `78cbdb530`,
`9e37d9b39`, `fc62bb490` |
| `types/…/objectql.ts` | objectstack-ai#6939 | 4 | `77cb489b4` |
| `types/…/overlay.ts` | objectstack-ai#6939 | 7 | `bfaa1589c` |
| `types/…/select-option.ts` | objectstack-ai#7635 | 1 | `544ecba84` |
| `types/…/theme.ts` | objectstack-ai#5489 | 3 | `78cbdb530` |
| `types/…/zod/blocks.zod.ts` | objectstack-ai#5489 | 1 | `78cbdb530` |
| `types/…/zod/complex.zod.ts` | objectstack-ai#6939 | 5 | `d4493fdbc` |
| `types/…/zod/data-display.zod.ts` | objectstack-ai#5853, objectstack-ai#6939 | 3 | `5f789538d`,
`fc62bb490`; one `.describe()` pointer dropped |
| `types/…/zod/form.zod.ts` | objectstack-ai#6938 | 1 | one `.describe()` pointer
dropped |
| `types/…/zod/index.zod.ts` | objectstack-ai#5489 | 1 | `78cbdb530` |
| `types/…/zod/objectql.zod.ts` | objectstack-ai#6939 | 9 | `240b80f31`, `77cb489b4`;
three `.describe()` pointers dropped |
| `types/…/zod/overlay.zod.ts` | objectstack-ai#6939 | 9 | `bfaa1589c`; six
`.describe()` pointers dropped |
| `types/…/zod/theme.zod.ts` | objectstack-ai#5489 | 2 | `78cbdb530` |

## Patch round: the eleven `.describe()` strings (commit `19da7f8c0`)

Under the seat's amendment `5860244997` (Q1 = A), each dead pointer is
dropped from its string; nothing else in any string moves, and no key,
path, accept set or refusal changes.

| file | member | before | after |
|:--|:--|:--|:--|
| `types/src/zod/data-display.zod.ts` | `TreeViewSchema.nodes` | "a
`nodes`-only document became LEGAL at objectui#6939; the `data`
fallback" | "a `nodes`-only document became LEGAL; the `data` fallback"
|
| `types/src/zod/form.zod.ts` | `CheckboxSchema.wrapperClass` | "...
around the box and its label (objectui#6938)" | "... around the box and
its label" |
| `types/src/zod/objectql.zod.ts` | `ObjectMapSchema.objectName` | "...
one of the three must be present (objectui#6939)" | "... one of the
three must be present" |
| `types/src/zod/objectql.zod.ts` | `ObjectGanttSchema.objectName` |
"... one of the three must be present (objectui#6939)" | "... one of the
three must be present" |
| `types/src/zod/objectql.zod.ts` | `ObjectGanttSchema.data` | "... read
FIRST by resolveRecordSourceConfig; undeclared on either face until
objectui#6939" | "Data source configuration — read FIRST by
resolveRecordSourceConfig" (special case 8; the `ObjectMapSchema.data`
twin names `getDataConfig` instead) |
| `types/src/zod/overlay.zod.ts` | `TooltipSchema.trigger` | "Element
the tooltip attaches to (objectui#6939)" | "Element the tooltip attaches
to" |
| `types/src/zod/overlay.zod.ts` | `TooltipSchema.content` | "... the
fallback for the same slot (objectui#6939). Text only: ..." | "... the
fallback for the same slot. Text only: ..." |
| `types/src/zod/overlay.zod.ts` | `TooltipSchema.children` | `... the
"Rich Content" slot (objectui#6939, objectui#6771)` | `... the "Rich
Content" slot (objectui#6771)` |
| `types/src/zod/overlay.zod.ts` | `ContextMenuSchema.triggerClassName`
| "... when none are set (objectui#6939)" | "... when none are set" |
| `types/src/zod/overlay.zod.ts` | `ContextMenuSchema.contentClassName`
| "... the underlying `ContextMenuContent` (objectui#6939)" | "... the
underlying `ContextMenuContent`" |
| `types/src/zod/overlay.zod.ts` | `ContextMenuSchema.modal` | "... root
as `modal` (objectui#6939)" | "... root as `modal`" |

- **Only those substrings moved, measured.** Each of the four files
printed by TypeScript's `createPrinter({ removeComments: true })` at
`6c3ad7c80`, with exactly these 11 substring deletions applied to the
print, equals the print at `19da7f8c0` (4 of 4 equal, 11 substitutions
counted). objectui#6771 and objectui#10295, which stay in two strings,
both answer 200.
- **Pins.** No test quotes any of the eleven strings: searching the
whole tree (not only `packages/types` tests and `scripts/__tests__`) for
a distinctive fragment of each string finds them only in the four zod
files, in two test COMMENTS (`recordSourceInput-7470` in `plugin-gantt`
and `plugin-map`, unchanged wording, no pointer), and in `overlay.ts`
docblocks and two `content/docs` pages that say the same words without
the pointer. No snapshot carries them.
- **`dist`.** After `@object-ui/types` is rebuilt, its `dist` carries 0
of the fourteen (round 1 left 22 hits, all inside `describe(` calls);
objectui#6771 is still in 21 files, the control.
- **Serial, re-checked for these four files.** The open-PR list was
re-read (10 open; objectui#10852 is new and touches none of the four).
objectui#10821 and objectstack-ai#10802 touch `objectql.zod.ts` and objectstack-ai#10714 touches
`data-display.zod.ts`; their `/files` hunks mapped onto this branch
cover lines 39-1246 and 2524-2716, and 167-206, and none contains a
describe line (1874, 1925, 1926; 575). Nothing was held.

## Serial

The open-PR list was re-read at the start (9 open). Six touch census
files: objectui#10851, objectstack-ai#10821, objectstack-ai#10802, objectstack-ai#10793, objectstack-ai#10714 and objectstack-ai#10278 (objectstack-ai#5400
is the release PR and regenerates). Each one's REST `/files` patch was
read and its hunk ranges were mapped onto the base with a line alignment
of the PR's merge-base file against the base file. **No census site lies
inside any hunk range**, so nothing was held for the serial rule. Trial
merges with `git merge-tree` at each PR's head, base against head of
this branch:

| PR (head) | at the base | at this head |
|:--|:--|:--|
| objectui#10851 (`8b146b0da`) | clean | clean |
| objectui#10821 (`24bcb0035`) | clean | clean |
| objectui#10802 (`77646f415`) | clean | clean |
| objectui#10793 (`dd43c757c`) | clean | clean |
| objectui#10714 (`d2afdb6bf`) | clean | clean |
| objectui#10278 (`eab4c8e52`) | 1 conflict region in `ObjectGrid.tsx`,
already there at the base | the same single region; this branch's five
`ObjectGrid.tsx` citations merge cleanly |

No file that exists only on another PR's branch was edited.

## Changesets

- `.changeset/10803-dead-citation-sweep-second-batch.md`, EMPTY
frontmatter: the comment-only edits in 17 released packages, no
published behaviour changes through them. Its prose now says so of the
comment edits alone and points at the second file.
- `.changeset/10803-describe-string-citations.md`, `'@object-ui/types':
patch`: the eleven runtime `.describe()` strings lose their dead
pointer, and nothing else in them, no key, path, accept set or refusal,
moves. It names the `ObjectGanttSchema.data` clause and quotes the
resulting string; patch round 2 (`4906c4408`) corrected its sentence,
which had called that string the same text as the `ObjectMapSchema.data`
twin (the twin names `getDataConfig`).
- Round 1: before the first changeset was added,
`check-changeset-presence` exited 1 on the comment-only `src` edits.

## Gates, patch round 2, on the head `4906c4408` (changeset prose only)

- `node scripts/check-changeset-presence.mjs`, exit 0: "48 source
file(s) of 17 released package(s) changed, and this change declares 2
changeset(s): .changeset/10803-dead-citation-sweep-second-batch.md,
.changeset/10803-describe-string-citations.md."
- `pnpm changeset:check`, exit 0: "No changeset declares a `major`
bump."
- `node scripts/check-changeset-overwrite.mjs` (report-only), exit 0: "2
changeset(s) added, 47 modified, 0 deleted"; `declared at base` equals
`declares now` for all 47 (the `8284` wording change moves no
frontmatter).
- `pnpm check:changeset-claims` (report-only), exit 0: 111 pending
changesets name a file this change touches, unchanged from patch round
1.
- `pnpm check:control-bytes`, exit 0: "check-control-bytes: OK (scanned
9087 tracked text file(s); skipped 85 binary)."
- Pin at this head: 0; this head merged with `main` at `4aebea00a`: 0.

## Gates, patch round 1, on the head `19da7f8c0`

- `node scripts/check-changeset-presence.mjs`, exit 0: "48 source
file(s) of 17 released package(s) changed, and this change declares 2
changeset(s): .changeset/10803-dead-citation-sweep-second-batch.md,
.changeset/10803-describe-string-citations.md."
- `pnpm changeset:check`, exit 0: "No changeset declares a `major`
bump." `node scripts/check-changeset-overwrite.mjs` (report-only), exit
0: "2 changeset(s) added, 47 modified, 0 deleted".
- `pnpm check:changeset-claims` (report-only), exit 0: 111 pending
changesets name a file this change touches; none quotes any of the
eleven strings, and the ones that describe them
(`6150-undeclared-but-consumed-keys.md` on the `nodes` text,
`7632-shared-record-source-config.md` and
`9618-record-source-ladder-attribution.md` on the ladder order) stay
true.
- `pnpm check:pending-changeset-literals`, exit 0. `pnpm
check:new-line-citations`, exit 0: "VERDICT
new-cross-file-line-citations: 0 new citation(s), enforcement
report-only -> exit 0". `pnpm check:control-bytes`, exit 0:
"check-control-bytes: OK (scanned 9087 tracked text file(s); skipped 85
binary)."
- `node scripts/check-spec-symbol-derivation.mjs`, exit 0: "spec member
citations: 1412 sources + 184 documentation pages; nothing cites a key
its spec symbol does not declare." `node
scripts/check-hand-rolled-comment-mask.mjs`, exit 0.
- eslint, narrowed: the four zod files, `--no-inline-config --format
json`: 4 files, 0 errors, 1 warning; the same four at the base: 0
errors, 1 warning. No type-aware linting in `eslint.config.js`.

## Gates, round 1, on the head `6c3ad7c80`

Each line is the gate's own verdict and its exit code, captured by
redirect-then-`$?`.

- `node scripts/check-changeset-presence.mjs`, exit 0: "47 source
file(s) of 17 released package(s) changed, and this change declares 1
changeset(s): .changeset/10803-dead-citation-sweep-second-batch.md."
- `pnpm changeset:check`, exit 0: "No changeset declares a `major`
bump."
- `node scripts/check-changeset-overwrite.mjs` (report-only), exit 0: "1
changeset(s) added, 47 modified, 0 deleted"; for all 47, `declared at
base` equals `declares now`. Separately, the sha256 of every edited
changeset's frontmatter block is equal before and after (47 of 47).
- `pnpm check:changeset-claims` (report-only), exit 0: "108 pending
changeset(s) describe a file this change touches." Read against the
diff: of 122 distinct 28-to-40-character segments of the removed comment
text, 9 occur in a pending changeset, and every one is a phrase this
diff keeps verbatim (for example "maintainer ruling 2026-08-25, Option
B"); none quotes a replaced pointer.
- `pnpm check:pending-changeset-literals`, exit 0: "No test source names
a pending changeset."
- `pnpm check:new-line-citations`, exit 0: "VERDICT
new-cross-file-line-citations: 0 new citation(s), enforcement
report-only -> exit 0".
- `pnpm check:control-bytes`, exit 0: "check-control-bytes: OK (scanned
9086 tracked text file(s); skipped 85 binary)." Self-scan of the 95
changed files for C0 / DEL bytes: 0.
- `node scripts/check-hand-rolled-comment-mask.mjs`, exit 0: "OK every
carrier is a DEBT entry, and every DEBT entry still carries one."
- `pnpm check:comment-mask-corpus`, exit 0: the standing report only (1
file, `apps/console/src/pages/DocsIndex.tsx`, not touched here).
- `pnpm check:spec-symbols`, exit 0: "spec member citations: 1412
sources + 184 documentation pages; nothing cites a key its spec symbol
does not declare."
- `pnpm check:installed-pin-claims`, exit 0: "OK". `pnpm
check:metadata-write-doors`, exit 0: "OK 17 metadata write door(s)
derived". `pnpm check:element-data-source-declaration`, exit 0: "OK — 13
gate-consuming file(s) checked". `pnpm check:handler-key-reads`, exit 0.
`pnpm check:designer-field-key-parity`, exit 0. `pnpm
check:action-forward-parity`, exit 0. `pnpm check:doc-example-ids`, exit
0: "414 real reference(s) all resolve in the catalog registry." `pnpm
check:doc-example-readers`, exit 0: "no @example hand-spells one." `node
scripts/check-doc-expression-carriage.mjs`, exit 0.
- `pnpm check:doc-examples` (`check-doc-example-types`): **NOT
MEASURED**, exit 2 "THE GATE COULD NOT RUN — the packages these examples
import are not built" (it needs every package's `dist`). Its input
cannot move here: 0 of the 150 changed source lines sits inside an
`@example` block (walked per line to the enclosing tag), and the
comment-stripped tree of every touched file is identical.
- **Comment-only, measured.** Each of the 47 touched source files parsed
with TypeScript 6.0.3's `createSourceFile` and re-printed by
`createPrinter({ removeComments: true })`: the sha256 is identical
before and after for 47 of 47, with 0 parse diagnostics. Lit control on
the same instrument: dropping "(objectui#6938)" from the `form.zod.ts`
describe string moves the hash; re-spacing a comment does not.
- Built `dist`: 0 hits of the fourteen outside the held describe strings
in any built package; the new shas reach `dist` in `types`, `core`,
`fields`, `react`, `sdui-parser`, `plugin-dashboard` and `plugin-grid`.

## Tests and type-check, through the shared verify lock

Each run reports `VERDICT command-exit 0`.

Patch round 1, on `19da7f8c0` (patch round 2 changed changeset prose
only, so these stand):

- `pnpm exec vitest run packages/types/ --maxWorkers=2`: `Test Files 252
passed (252)`, `Tests 5429 passed (5429)`.
- The 55 `scripts/__tests__` suites that read `packages/types/src/zod`,
any zod source, or `.changeset/` (every test there naming either, plus
the suite of every script that reads either): `Test Files 55 passed
(55)`, `Tests 2685 passed (2685)`.
- `turbo run build --filter=@object-ui/types...` (1 task), then `pnpm
--filter @object-ui/types run type-check` (`tsc --noEmit && tsc -p
tsconfig.examples.json && tsc -p tsconfig.test.json`), exit 0.

Round 1, on `6c3ad7c80`:

- `pnpm exec vitest run packages/types/ --maxWorkers=2`: `Test Files 252
passed (252)`, `Tests 5429 passed (5429)`.
- A second run of 128 files: every test file outside `packages/types`
that reads the file system and names a touched source file by path, the
`.changeset/**` readers `node scripts/markdown-test-inputs.mjs --list`
names, and the suites of the changeset, comment-mask, citation and
control-byte gates. `Test Files 128 passed (128)`, `Tests 3949 passed
(3949)`.
- Build of the 28-package dependency closure of the 17 touched packages
(`turbo run build --concurrency=2`), then `pnpm
--workspace-concurrency=2 run type-check` over all 17 touched packages:
every one printed its `type-check$ tsc ...` line and `Done`, exit 0.
- eslint, narrowed and declared. Population: the 47 touched source
files, each linted through the repo's `eslint.config.js` (47 results,
none ignored). Counts from `--no-inline-config --format json`: 47 files,
4 errors, 1167 warnings. The same 47 files at the base, through the
ESLint API with inline config off: 4 errors, 1167 warnings, the 4 errors
on the same lines and rules (`react-hooks/static-components` twice,
`@typescript-eslint/no-empty-object-type`, `no-restricted-imports`).
Invariance: `eslint.config.js` enables no type-aware linting (no
`projectService`, no `project` parser option, no type-checked preset),
so a comment diff cannot move another file's verdict.

**Declared narrowing.** NOT MEASURED locally: the full test suites of
the other 16 touched packages (their edits are comment-only and
unchanged since round 1). Reason: the comment-stripped syntax tree of
every touched file is identical, so only a test that reads source TEXT
can move, and those ran above. CI runs the full farm.

## Acceptance notes

1. **The family is larger than fourteen: the remainder rides this card
(seat amendment `5860244997`, Q2 = A), not this PR.** Census method:
every distinct `objectui#N` and objectui issue or pull URL cited in the
two in-scope classes (pending `.changeset/*.md`, non-test
`packages/*/src`) was read by REST `GET
/repos/objectstack-ai/objectui/issues/N`, and every 404 was read a
second time. At `6c3ad7c80`: 2791 numbers, 2663 answer 200 and 128
answer 404 (128 of 128 again). Thirteen of the 128 are this card's (the
fourteenth, objectstack-ai#7628, is only ever cited as "PR objectstack-ai#7628"). The other **115**,
cited on 652 lines (197 changeset lines in 153 files and 455 src-nontest
lines in 162 files), are: objectstack-ai#3499 objectstack-ai#3559 objectstack-ai#4784 objectstack-ai#5197 objectstack-ai#5221 objectstack-ai#5286 objectstack-ai#5309
objectstack-ai#5399 objectstack-ai#5401 objectstack-ai#5413 objectstack-ai#5414 objectstack-ai#5416 objectstack-ai#5417 objectstack-ai#5418 objectstack-ai#5425 objectstack-ai#5518 objectstack-ai#5533 objectstack-ai#5709 objectstack-ai#5738
objectstack-ai#5741 objectstack-ai#5852 objectstack-ai#5859 objectstack-ai#6296 objectstack-ai#6464 objectstack-ai#6661 objectstack-ai#6740 objectstack-ai#6757 objectstack-ai#6854 objectstack-ai#6859 objectstack-ai#6864 objectstack-ai#6872
objectstack-ai#6879 objectstack-ai#6882 objectstack-ai#6887 objectstack-ai#6888 objectstack-ai#6889 objectstack-ai#6892 objectstack-ai#6896 objectstack-ai#6905 objectstack-ai#6907 objectstack-ai#6921 objectstack-ai#6923 objectstack-ai#6924
objectstack-ai#6931 objectstack-ai#6940 objectstack-ai#6942 objectstack-ai#6951 objectstack-ai#6958 objectstack-ai#6959 objectstack-ai#6962 objectstack-ai#6965 objectstack-ai#7004 objectstack-ai#7008 objectstack-ai#7023 objectstack-ai#7025
objectstack-ai#7036 objectstack-ai#7068 objectstack-ai#7069 objectstack-ai#7077 objectstack-ai#7079 objectstack-ai#7087 objectstack-ai#7088 objectstack-ai#7091 objectstack-ai#7097 objectstack-ai#7108 objectstack-ai#7177 objectstack-ai#7612
objectstack-ai#7620 objectstack-ai#7623 objectstack-ai#7658 objectstack-ai#7666 objectstack-ai#7667 objectstack-ai#7669 objectstack-ai#7678 objectstack-ai#7681 objectstack-ai#7682 objectstack-ai#7703 objectstack-ai#7704 objectstack-ai#7708
objectstack-ai#7804 objectstack-ai#7844 objectstack-ai#7853 objectstack-ai#7869 objectstack-ai#7874 objectstack-ai#7877 objectstack-ai#7926 objectstack-ai#7959 objectstack-ai#7967 objectstack-ai#7979 objectstack-ai#7980 objectstack-ai#8058
objectstack-ai#8060 objectstack-ai#8072 objectstack-ai#8127 objectstack-ai#8137 objectstack-ai#8204 objectstack-ai#8229 objectstack-ai#8248 objectstack-ai#8307 objectstack-ai#8408 objectstack-ai#9231 objectstack-ai#9241 objectstack-ai#9244
objectstack-ai#9365 objectstack-ai#9373 objectstack-ai#9375 objectstack-ai#9542 objectstack-ai#9553 objectstack-ai#9585 objectstack-ai#10117 objectstack-ai#10119 objectstack-ai#10120 objectstack-ai#10129 objectstack-ai#10132
#14026. They are carried by later PRs on this card after this one lands,
in batches, each under the same serial rule. Two of them sit in
sentences this PR edits (objectui#6896 in the chart changesets,
objectui#6951 beside the tree-view history and in the `nodes`
description), and they are left as they are.
2. **Filenames are not citations.** Pending changeset FILENAMES carry
several of the fourteen (six carry `6939`); they stay, as PRs
objectui#10707 and objectstack-ai#10797 left theirs.
3. **The census instrument's one false positive class** is
`objectstack#N`: its bare `#` branch matches inside a repo-qualified
sister-repo citation. The printed pin above filters it; the card's
figures did not.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Sep 28, 2026
…s the spec's ObjectListViewSchema by reference; ViewPreview folds a stored options bag (objectui#7928) (objectstack-ai#10821)

Fixes objectstack-ai#7928
Clause-②: yes

Ruling `5856694523` (director seat, Q1 A · Q2 (i)), under ruling
`5565628927` (option A, staged, maintainer 「同意」). Claim `5857418366`,
seat `domain:spec#1`, session `session_014fWVhLzhxR8qrFsJ5o8TYW`.

## What changed

- **`@object-ui/types`: the mirror flips by reference.**
- The zod face: `ObjectViewSchema.listViews` is
`stripImportedDefaults(SpecViewSchema).shape.listViews`. That is the
spec container's own record of the strict `ObjectListViewSchema`,
crossed through the import boundary so no protocol default is written
into a parsed document.
- The TS face: a record of string to `z.input` of `typeof
ObjectListViewSchema`, imported type-only from `@objectstack/spec/ui`.
- A named view authoring `options` is refused `unrecognized_keys`,
naming `options`.
- The `UnmirroredDeclared` ledger row is deleted. The header figures
moved to 12 entries / 84 keys (2 / 16 spec-derived), each with its
history sentence.
  - The two ledger comments triage `5853448361` names are rewritten.
- **`@object-ui/app-shell` `ViewPreview`: the fold.** The new
`foldStoredListOptions` lifts each `options.KIND` of a stored body onto
the top-level `KIND` block before the node is built. The top-level value
wins per key, and `options` is not relayed. A body without `options`
comes back as the same object.
- **`@object-ui/plugin-view` `ObjectView`: both named-view `options`
reads are removed.** They are the `legacyViewOptions` rung and the
`options:` relay into the `list-view` node. The host `activeView` rung
stays on both, so objectui#10758 / objectstack-ai#10380's path is untouched. The JSDoc
example now teaches the canonical `kanban` / `calendar` blocks.
`currentNamedViewConfig` is typed off `ObjectViewSchema['listViews']`.
- **`NamedListView` is untouched** (Q2 (i)).
- **`examples/byo-backend-console`**: the named view gains per-object
`columns`.
- **Changeset** `7928-listviews-by-reference-fold.md`: `minor` for types
/ plugin-view / app-shell, with the breaking semantics spelled out.

## Beyond the claimed file surface, and why each was forced

Every item below is a measured consequence of the flip. None is a new
capability.

1. **The two named-view door checks now run under `superRefine(…, { when
})`, and they read the protocol's refusal.**
- The checks are objectui#8355's calendar aliases and objectui#10321's
stray `kanban.groupBy`, which landed on `main` mid-card and was merged
in.
- The strict record refuses the alias itself. That aborts a default
refinement, and the parse output DROPS the key. Measured: without the
change, the checks never ran on the documents they exist for.
- The author would then see only the protocol's near-miss hint, which
for `dateField` says `endDateField`: the hazard objectui#8355's docblock
names.
- A shared helper, `namedViewKindRefusals`, reads the
`unrecognized_keys` issue at `listViews.KEY.KIND` and adds the pointer
at the key. It adds messages only. It refuses and accepts nothing on its
own.
- The `options.KIND` nesting is not judged any more. The bag is refused
whole.
2. **plugin-view tab strip: a named view's `label` is resolved as the
protocol's `I18nLabel`.** It uses `resolveI18nLabel` with
`useDisplayLocale`, as `ListView` already does.
- Measured on the flipped tree before the fix: a locale-map label threw
"Objects are not valid as a React child".
   - The contract now declares that shape, so it has to render.
3. **plugin-view sort reads.** The protocol's named-view `sort` (17.4.0)
still admits the bare string clause objectui retired (objectui#8221).
- The two sink reads cast to the sink's declared input and hand the
value on unchanged. The sinks refuse a string out loud
(`normalizeSortEntries`, `ObjectGrid`). Nothing is narrowed, dropped or
lowered.
- A pin (`ObjectView.namedViewSpecRecord-7928.test.tsx`) reads that
answer: no `$orderby`, and the refusal is reported.
4. **Pins that asserted the unmirrored state are inverted. ⛔ None is
deleted.**
   - objectstack-ai#8980's three legacy-nesting cases.
- objectstack-ai#8355's legacy-nesting arm, scope controls and TIMELINE control, on
both the plugin-view and the types side.
   - objectstack-ai#10321's legacy nesting and scope controls.
- The objectui#7924 census in
`object-view-unmirrored-keys-7779.test.ts`: `options` moves from read to
unread (20 / 44), and bucket ③ is 11. `options` is HELD, not tombstoned,
per Q2 (i). The value-binding instrument now also recognises
`NamedViewConfig`, the mirrored member's entry type.
- `object-view-spec-parity` (the TS-only backlog is empty; 22 / 24
keys).
- `imported-defaults-8317`: `ViewSchema` is registered, and a fourth
`lazy` node sits under `formViews`. The walker docblock count moved with
it.
5. **Three plugin-view fixture files** typed as `ObjectViewSchema`:
named views gain `columns`, and a tuple filter is respelled as a rule
object. The assertions are unchanged.
6. **Three reference rows** (`schema-reference.md`, `plugin-view.mdx`,
plugin-view README) said a `listViews` entry is a `NamedListView`.

## Verification, first taken at `24bcb00353` (after an earlier merge of
`main`)

The readings in this section are of `24bcb00353`. The base-merge round
re-took them at the final head `395371dad2`; see Patch round 1 below.

Every heavy run went through `os-verify-lock.sh`. Each exit code was
captured redirect-first.

- vitest `packages/types/ packages/plugin-view/`: 301 files, 5854 tests
passed, VERDICT command-exit 0.
- vitest `packages/plugin-list/` plus 71 app-shell files: 173 files,
1816 tests passed, exit 0.
- The app-shell files are those reaching `ViewPreview` or the preview
registry, `ObjectView*` and `InterfaceListPage*`.
- The full app-shell suite (831 files) is declared to CI. app-shell's
only source change is `ViewPreview.tsx`.
- type-check (`tsc --noEmit` plus `tsconfig.test.json`), exit 0 each:
`@object-ui/types` (plus examples), plugin-view, app-shell, plugin-list,
and `examples/byo-backend-console`.
- Reverse verification of the cross-package type: before the fixtures
were respelled, plugin-view's `tsconfig.test.json` went red with TS2741
"Property 'columns' is missing" against the rebuilt `.d.ts`.
- ESLint `--no-inline-config` on the 20 touched TS/TSX files: 0 errors,
200 warnings.
- The only new warning is `react-refresh/only-export-components` for the
exported fold in `ViewPreview.tsx`.
- Not type-aware: `eslint.config.js` has no `projectService` /
`parserOptions` (count 0, control `languageOptions` 1), so no untouched
file's verdict can move.
- Gates, exit 0 each:
- `check-changeset-presence` (19 source files of 3 released packages, 1
changeset) and `check-changeset-no-major`.
- `check:new-line-citations` (0 new), `check:control-bytes`,
`check:spec-symbols`, `check:test-path-roots`.
  - The three `check:vi-mock-*` gates and `check-changeset-claims`.
- `check:doc-types`, `check:doc-snippets` (676 of 676 judged, 0 failed),
`check:doc-examples`, `check:readme-exports`, `check:doc-fences`.
  - `check-governed-queue-guard --test` (NOT GOVERNED).
- **Spec Main Shape Gate: NOT MEASURED locally.** It is a repo-wide
forced type-check against a spec built from objectstack `main`, which CI
runs on this PR. Source reading of objectstack `main` (`e4621867`):
`ViewSchema.listViews` is the same record, and the list-view `sort` is
array-only there, so the sort casts narrow nothing.

**Ablations.** Each used `ablation-replace.mjs`, on the committed tree,
with the restore proven (`git diff HEAD` empty, blob equals HEAD):

1. The fold call replaced by the raw body: 3 of 12 red (the legacy lane,
the per-key survival, the contract parse). "Canonical wins" stays green,
because the renderer no longer reads the bag.
2. The named-view `options` read restored: 4 red (two inverted objectstack-ai#8980
cases, the fold file's CONTROL, objectstack-ai#8355's half-1 legacy control).
3. `when` dropped from the door checks: 4 red (the canonical
`calendar.dateField` / `endField` pointer pins, types and plugin-view).
4. The tab-strip label resolution reverted: the locale-map case and the
empty-map fall-through went red, and the plain-string CONTROL stayed
green.
5. The kanban sibling unwired (merged head): the two objectstack-ai#10321 pointer pins
went red, and every control stayed green.

## Patch round 1: base merge (final head `395371dad2`)

After objectstack-ai#10793 (`66e8b2ab38`) and objectstack-ai#10802 (`b956e693d0`) landed,
`origin/main` `b956e693d0` was merged into this branch as merge commit
`9c2a692186`. Both landed commits were confirmed as its ancestors first.
There was no rebase and no force-push.

- **`imported-defaults-8317.test.ts`**: the `IMPORTED` list keeps both
crossings. `ChartSeriesSchema` (objectui#10770) comes first, then
`ViewSchema` (this PR). The walker's `lazy` count stays 4, since
`ChartSeriesSchema` owns no `z.lazy`.
- **`object-view-unmirrored-keys-7779.test.ts`**: the describe takes
`main`'s "seventeen `?: never` tombstones". The first `it()` checks 8 +
(11 − 1 − 1) = 17 tombstones derived off the AST, which is `main`'s
`NAMED_LIST_VIEW_TOMBSTONES` with `densityMode`. objectstack-ai#10793's assertions
(`_DensityModeTombstoned`, `_refusedDensityMode`,
`_acceptedDensityCanonical`, and the relay fold-through lines) and this
PR's (`listViews` by reference, `BUCKET_LOCAL_ONLY_HELD`, and "`options`
is HELD") all survive, and no assertion was re-pointed. Four comment
passages were re-pointed to the merged state: bucket ③ is eleven,
`options` is the held eleventh member, and the retained read quotes Q2
(i).
- **`plugin-view/src/ObjectView.tsx`** merged cleanly, and the two edits
have no semantic collision. objectstack-ai#10793's `rowHeight` rung reads
`normalizeListViewSchema(activeView ?? {})`, while this PR dropped the
`currentNamedViewConfig?.options` reads: there are 0 on the merged file,
with `currentNamedViewConfig?.kanban` at 1 as the control.
- **Changeset.** `395371dad2` re-points one paragraph that the merge
made false: the entry leaves `NamedListView` unchanged, objectstack-ai#7924's own
entry retires its `densityMode`, and any further retirement follows
ruling A′ and the `allowExport` ruling (`5856694523` Q2 (i)).

**Readings at the merged tree.** Every heavy run went through the lock,
and each exit code was read before any pipe.
- `type-check` exited 0 for `@object-ui/types`, `core`, `plugin-view`,
`app-shell` and `plugin-charts`, and also for `plugin-list` and
`examples/byo-backend-console`.
- vitest:

  | package | files | tests |
  | --- | --- | --- |
  | `types` | 257 | 5482 |
  | `core` | 182 | 3635 |
  | `plugin-view` | 52 | 490 |
  | `plugin-charts` | 84 | 1015 |
  | `plugin-list` | 104 | 1173 |

  All passed with exit 0.
- **`app-shell` exited 1: 838 files passed, 1 failed and 1 skipped (8551
tests passed, 1 failed, 9 skipped).**
- The failure is `AppContent.inaccessibleAppStrand.test.tsx`, "bounces
to the DECLARED landing…". Its network-escape guard fired on a real
socket to `localhost:3000/api/v1/meta`.
- The same file passed alone 3 of 3. It is outside this diff, which
touches nothing under `app-shell/src/console`.
- This is recorded, not waived. CI's sharded run on this head decides
it.
- The named pins passed together: 7 files, 411 tests (the 7779, 8317 and
7928 pins, `ViewPreview.optionsFold-7928`,
`ObjectView.densityModeThroughFold-7924` and
`ObjectView.relayRungCensus-7559`).
- ESLint `--no-inline-config` on the 20 TS/TSX files: 0 errors and 200
warnings, the same as at `24bcb00353`.
- Gates at `395371dad2` exited 0 each:
  - `check-changeset-presence` and `check-changeset-no-major`;
- `check:new-line-citations` (0 new), `check:control-bytes`,
`check:spec-symbols` and `check:test-path-roots`;
- the three `check:vi-mock-*` gates, `check:doc-types`,
`check:doc-fences` and `check-changeset-claims`;
  - `check-governed-queue-guard --test`: 24 paths, NOT GOVERNED.
- **Reverse check.** Writing `24bcb00353`'s 7779 pin over the merge
turned vitest red on objectstack-ai#10793's tombstone: 3 failed, exit 1.
`tsconfig.test.json` also went red, with TS2344 and TS2322. The restore
was proven: the blob equals HEAD, and `git diff HEAD` is empty.
- **NOT MEASURED:**
  - `check:readme-exports`: its prerequisite is not met (unbuilt dists).
- `check:doc-snippets` / `check:doc-examples`: not re-run, because this
PR's doc lines are byte-identical to `24bcb00353`, where both passed.
  - The Spec Main Shape Gate: CI runs it.

## Acceptance notes

- **The named view now has one dialect, and it is wider in two ways and
stricter in more.**
- Keys the protocol's strict kind blocks do not declare are refused on a
named view. Measured on 17.4.0: `kanban.titleField` / `groupField` /
`swimlaneField` / `conditionalFormatting`, `gallery.imageField`,
`timeline.dateField` / `endField`, `calendar.allDayField` /
`defaultView` and `map.style`.
- `generateViewSchema` still reads them off a named view that reaches it
unvalidated, so a stored body keeps rendering.
- This is ruling A's "by reference". It is not the stop condition (a
third top-level `options` read), but it is a larger population than the
thread measured (top-level members only).
- `kanban.titleField`, `calendar.defaultView` and `map.style` have no
protocol spelling on this surface. Whether any deserves one is an
upstream question for the seat.
- **Release text.** Pending changesets for objectui#7779, objectstack-ai#8355, objectstack-ai#10321,
objectstack-ai#8365 and objectstack-ai#9242 say `listViews` stays unmirrored, or that the doors
judge the `options` nesting. This PR's changeset states that it
supersedes both, rather than editing five other cards' bodies.
- **Left as found, deliberately:**
- `NamedListView`'s own docblock still says "Used in
ObjectViewSchema.listViews". Under Q2 (i) the interface is untouched.
objectui#7924 closed via objectstack-ai#10793, which retired only `densityMode` and
left this docblock as it was. The director seat carries it as a hygiene
card (record `5860951313` ③).
- The dev-mode warning in `generateViewSchema` still tells authors to
"Move them under options.KIND"; it now reads the host `views` entry
only.
- `normalizeSortEntries`' refusal text says the array is "the only one
`@objectstack/spec` accepts". That is false for 17.4.0's
`ObjectListViewSchema` and true on objectstack `main`.
- **Overlap, resolved.** objectstack-ai#10793 (objectui#7924) and objectstack-ai#10802
(objectui#10770) each conflicted textually with this PR: objectstack-ai#10793 in the
describe and first `it()` names of the 7779 census, and objectstack-ai#10802 in the
8317 `IMPORTED` list. Both landed first. Merge commit `9c2a692186`
resolved both conflicts and kept every assertion from both sides (Patch
round 1).

---

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Sep 28, 2026
…i#10793, objectstack-ai#10802 and objectstack-ai#10821 made false (objectui#10877) (objectstack-ai#10891)

Fixes objectstack-ai#10877
Clause-②: no. It is release-text hygiene, and no published type or
behaviour moves.

## What this does

Eight pending changesets each state a contract fact that a later PR in
this same unreleased batch made false: PR objectstack-ai#10793 (objectui#7924,
`densityMode` retired), PR objectstack-ai#10821 (objectui#7928,
`ObjectViewSchema.listViews` mirrored by reference, a named view's
`options` bag refused whole) and PR objectstack-ai#10802 (objectui#10770,
`ObjectChartSchema.series` became two arms). A pending changeset
publishes verbatim into the CHANGELOG at the next release, and the
release PR objectui#5400 is still open. So each false sentence now has a
dated supersession note beside it.

The note form is copied from the landed precedents, PR objectstack-ai#10533 (the
`6235-mergedsort-wrap.md` note) and PR objectstack-ai#10828 (objectui#10801): a bold
headline naming the date and the card whose change supersedes the
sentence, then what holds now, then the superseding entry by file and
PR, then "the text above is kept as the reading of this change".

- Zero lines deleted: `git diff --numstat` against the merge-base
`9f0c84a448` is 168 insertions, 0 deletions, over 8 files. The first
round added 10 notes in 6 files. Patch round 1 added 4 notes in 2 files
(below). Patch round 2 edited the 7779 note and added none, so there are
14 notes.
- The frontmatter is byte-identical in all nine entries the claim and
its addendum `5862987983` name. The md5 of the frontmatter block is
equal at the merge-base `9f0c84a448` and the head `50deccd71a` for each:
7924-retire `ecedb10c`, 8355 `8f322642`, 10321 `564c93d5`, 10584
`84f96a9a`, 8365 `379475b3`, 9242 `acef2a2c`, 8086 `e927372d`, 7779
`ecedb10c`, 7946 `2ffd8635`.
- The seventh entry, `8086-chart-series-type-arm.md`, is untouched
(whole-file md5 `7ea6b544` at base and head). Every sentence in it
measured STILL-TRUE (see the lit control below).
- No new changeset: the diff is `.changeset/` only, and
`check-changeset-presence` says none is owed.

| Entry | FALSE-NOW sentences, quoted | Superseding entry (PR) | Notes |
| --- | --- | --- | --- |
| `7924-named-list-view-retire.md` | "`NamedListView` (one entry of
`ObjectViewSchema.listViews`)"; "Two bucket-③ members are NOT retired"
and "They stay declared as they were." |
`7928-listviews-by-reference-fold.md` (objectstack-ai#10821);
`7924-density-mode-through-fold.md` (objectstack-ai#10793) | 2 |
| `8355-retire-calendar-date-aliases.md` | "it now names the key and the
remedy on all five authoring surfaces", for "a named view's two nestings
under `listViews`"; "plus a `.check()` on `ObjectViewSchema` for a named
view's two nestings"; "`ObjectViewSchema.listViews` stays UNMIRRORED"
and the rest of that paragraph; "`timeline.dateField` stays a live,
accepted alias", on a named view; "the structural guard that `listViews`
stays out of the object's `shape`" |
`7928-listviews-by-reference-fold.md` (objectstack-ai#10821) | 3 |
| `10321-named-view-kanban-groupby-refused.md` |
"`ObjectViewSchema.listViews` is unmirrored"; "and in its legacy
`options.kanban` bag" with the issue path under `options.kanban`;
"reports one issue per written key, naming the key and the replacement";
"Not changed, deliberately: `listViews` stays unmirrored" and the rest
of that paragraph | `7928-listviews-by-reference-fold.md` (objectstack-ai#10821) | 2 |
| `10584-object-chart-series-describe.md` | "It now says the copy is
that `{ dataKey }` arm minus its per-series `type`, and that the
per-series family override on an `object-chart` node is `chartType`";
"taking `type` up would widen a published accept set" |
`10770-object-chart-react-tier-node.md` (objectstack-ai#10802) | 1 |
| `8365-stray-kanban-groupby-refused.md` | "objectui#10321 gives that
route its own door, which refuses the key in either nesting with the
same message" | `7928-listviews-by-reference-fold.md` (objectstack-ai#10821) | 1 |
| `9242-stray-kanban-groupby-lane-second-route.md` | "which refuses the
key in either nesting with the same message"; "(a named view's `kanban`
is typed as the `list-view`'s), but not the key inside the untyped
`options.kanban` bag on either" | `7928-listviews-by-reference-fold.md`
(objectstack-ai#10821) | 1 |
| `object-view-unmirrored-keys-7779.md` (patch round 1) | the table's
"still unmirrored"; "`listViews` stays unmirrored, on the ruling's own
fallback clause."; "The declaration's value is the local
`NamedListView`"; the 21-read / 43-unread figures; "The key therefore
stays in the parity ledger … until the maintainer decides its value
type" | `7928-listviews-by-reference-fold.md` (objectstack-ai#10821) | 1 |
| `objectchart-schema-anchor-7946.md` (patch round 1) | the `series`
half of "value types taken from their READ sites (`ChartRendererProps`
for `xAxisKey` / `series`)" and of "two are INTERNAL, relay-composed
(`xAxisKey`, `series` …)"; "`series` entries must be `dataKey`-shaped.";
"`aggregate` is the exception, and only because the spec's own object is
strict." | `10770-object-chart-react-tier-node.md` (objectstack-ai#10802) | 3 |

**Placement.** Each note sits directly after the paragraph that holds
the false sentence. Where two false paragraphs are adjacent, one note
covers both: the first 10321 note, the 10584 note and the last 8355
note. The 8355 note after the UNMIRRORED paragraph also covers the
matching clause in the `@object-ui/types` bullet above it, because both
describe the same named-view check. No note cites a line address; source
facts are cited by symbol and test name, and `check:new-line-citations`
reads 0 new citations.

## Measured, not inherited (one-time probes, not committed)

The probes ran the worktree sources at base `b2683a2c0e`, with
`@objectstack/spec` 17.4.0 resolved from `packages/types`. The zod face
ran through `tsx`, and the TS face through `tsc --noEmit` against
`packages/types/src/index.ts`. Each probe sits beside a control that
differs only by the key. A named view is `listViews.v` with `columns`
set.

Zod face (`ObjectViewSchema.safeParse`; `safeValidateSchema` gives the
same issues on the two `kanban` rows):

| Probe | Result |
| --- | --- |
| control `kanban: { groupByField, columns }` | parses |
| `kanban.groupBy` | two issues: `unrecognized_keys` at
`listViews.v.kanban` naming `groupBy`, and `custom` at
`listViews.v.kanban.groupBy` |
| `options: { kanban: { groupBy } }` | one issue: `unrecognized_keys` at
`listViews.v` naming `options`; nothing at the `options.kanban.groupBy`
path |
| `calendar.dateField` | `unrecognized_keys` at `listViews.v.calendar`,
and `custom` at `listViews.v.calendar.dateField` |
| `options: { calendar: { dateField } }` | one issue: `options` refused
|
| `timeline.dateField` | `unrecognized_keys` at `listViews.v.timeline`;
control `timeline: { startDateField, titleField }` parses |
| each of the 16 retired `NamedListView` spellings, and `densityMode` |
`unrecognized_keys` at `listViews.v` naming the key (17 of 17); control
`userActions: { search: false }` parses |
| `listViews` in `ObjectViewSchema.shape` | present |
| list-view route: `kanban.groupBy`, `options.kanban.groupBy`,
`calendar.dateField`, `options.calendar.dateField`, `timeline.dateField`
| `invalid_type`, `custom`, `invalid_type`, `custom`, parses: unchanged
|

TS face (`tsc`):

| Probe | Result |
| --- | --- |
| named view `kanban.groupBy` in an `ObjectViewSchema` literal | TS2353,
excess property on the spec's kanban block |
| named view `options` | TS2353, `options` does not exist |
| named view canonical control | compiles |
| `list-view` `kanban.groupBy` | TS2322 (`never`): unchanged |
| `list-view` `options.kanban.groupBy` | compiles: unchanged |
| a `NamedListView` value as a `listViews` entry | TS2322, not
assignable |
| `NamedListView` with `densityMode`; with `allowExport` | TS2322;
compiles |

Chart series (`ObjectChartSchema.safeParse`): `{ name, type: 'line' }`
parses with `type` kept; `{ dataKey, type: 'line' }` parses with `type`
stripped; `{ name, chartType }` is refused (`invalid_union`). The series
`.describe()` string now names both arms.

The runtime fold was read, not probed: `normalizeListViewSchema` still
folds `densityMode` and the eight `show*` flags, and `ListView` still
merges `options.kanban` under `kanban`.

## Lit control: STILL-TRUE, left byte-identical

- `8086-chart-series-type-arm.md`, the whole entry. PR objectstack-ai#10802 changed no
`plugin-charts` source (its one file there is a test).
`ChartRendererProps.schema.series` still declares `type?: string` on
both arms. The `ObjectChartSchema.series` docblock still says the copy
of the internal arm omits `type`, and that internal arm's type is the
same literal as before PR objectstack-ai#10802.
- 8365, "takes the identical message through a check on that untyped
bag", and the distinguishing `options.kanban` fixture. Both are the
`list-view` route, measured unchanged.
- 9242, "a `list-view` document carrying the key, under `kanban` or
under the legacy `options.kanban` bag, is refused by
`safeValidateSchema`". Measured unchanged.
- 8355, the `@object-ui/types` bullet's "on the legacy
`options.calendar` nesting (a check, since an open record declares no
member". This is the list-view surface, measured unchanged.
- 7924-retire, "`densityMode` is folded onto `rowHeight` by
`normalizeListViewSchema`" and "Only the authoring face changes". The
fold still does both.
- 10584, "Wording only. The accepted shape does not move on either
face". This is a statement about that change's own delta, and it is
still true of that change.
- 7779 (patch round 1): `viewTabBar` retired; the eight keys enforced;
"64 declared top-level members"; the spec record requires `columns` and
refuses `options`, tuple filters and `default`; "not papered over with
`z.any()`"; "absent stays valid on all nine"; the ledger move 14/96 →
14/87 (that change's own delta). Each measured true at `0ea55c3701`.
- 7946 (patch round 1): the `xAxisKey` half of the INTERNAL sentence
(its `.describe()` still says all five producers compute it); `type:
'object-chart'` required; `chartType` must be the declared union;
`normalizeChartSchema` translates the `{ name }` arm; `aggregate`
`function` / `groupBy` required with unknown members refused; a
misspelled key on the node itself is not refused; `filter` keeps both
arms. Each measured true at `0ea55c3701`.

## Gates (re-taken at head `50deccd71a`; hand-derived from
`package.json` and `.github/workflows/`; each exit captured by
redirect-then-status)

| Command | Verdict line | Exit |
| --- | --- | --- |
| `pnpm check:pending-changeset-literals` | ✅ No test source names a
pending changeset. | 0 |
| `node scripts/check-changeset-claims.mjs` | ✅ No pending changeset
names a file this change touches. (Self-contradiction reading: every
declared package across the 8 bodies is not negated.) | 0 |
| `node scripts/check-changeset-presence.mjs` | ✅ No source or published
contract of a released package changed in this range, so no changeset is
owed. | 0 |
| `node scripts/check-changeset-no-major.mjs` | ✅ No changeset declares
a `major` bump. | 0 |
| `node scripts/check-changeset-overwrite.mjs` | report-only: this
change touches 8 changesets it did not add. The declared packages are
identical at base and head for all 8, which is the prose-correction
case. | 0 |
| `node scripts/check-changeset-fixed.mjs` | ✅ All workspace packages
are in the changeset fixed group. | 0 |
| `pnpm check:new-line-citations` | VERDICT
new-cross-file-line-citations: 0 new citation(s) | 0 |
| `pnpm check:control-bytes` | ✅ check-control-bytes: OK | 0 |
| `pnpm check:spec-symbols` | ✅ spec member citations: nothing cites a
key its spec symbol does not declare. | 0 |
| `pnpm check:test-path-roots` | ✅ check-test-path-roots: OK | 0 |
| `node scripts/check-installed-spec-pin-claims.mjs` | OK (it excludes
`.changeset/`, so it was run for completeness only) | 0 |
| `node scripts/check-governed-queue-guard.mjs --test` (the 8 paths) | ✅
NOT GOVERNED | 0 |

Tests, from the repo root under the shared verify lock: every
`scripts/__tests__` file that names `.changeset` (derived with `git grep
-l`: 24 test files, plus one fixture that is not a test), and the five
`@object-ui/types` pins that re-derive what the notes state
(`calendar-date-alias-refusal-8355`,
`named-view-kanban-stray-group-by-10321`,
`object-view-listviews-by-reference-7928`,
`object-chart-react-tier-node-10770`,
`object-view-unmirrored-keys-7779`). Result at `f5e0123f4b`: **Test
Files 29 passed (29), Tests 1193 passed (1193)**, lock `VERDICT
command-exit 0`. Re-taken at `0ea55c3701` with the 24
`scripts/__tests__` files and the four pins the new notes rest on
(`object-view-unmirrored-keys-7779`,
`object-chart-react-tier-node-10770`,
`object-view-listviews-by-reference-7928`, `zod-mirror-parity`): **Test
Files 28 passed (28), Tests 1200 passed (1200)**, lock `VERDICT
command-exit 0`. Re-taken at `50deccd71a` (patch round 2): the 24
`scripts/__tests__` files plus `object-view-unmirrored-keys-7779` gave
**25 files / 1146 tests**, and the four other pins
(`calendar-date-alias-refusal-8355`,
`named-view-kanban-stray-group-by-10321`,
`object-view-listviews-by-reference-7928`,
`object-chart-react-tier-node-10770`) gave **4 files / 48 tests**. Both
runs gave lock `VERDICT command-exit 0`.

Not applicable, with the reason:
- `@object-ui/types` type-check, package vitest runs and consumer
sweeps: the diff touches no package source, only `.changeset/`.
- The Spec Main Shape Gate: no touched type.
- ESLint `--no-inline-config` on the touched files: `eslint.config.js`
has no configuration that matches `.md`. All 8 files report "File
ignored because no matching configuration was supplied", so it judged
nothing (NOT MEASURED).

## Acceptance notes

- **Residue in the same family: closed by patch round 1.** The first
round stopped on two files outside the claimed surface,
`object-view-unmirrored-keys-7779.md` and
`objectchart-schema-anchor-7946.md`. Each is named by a superseding PR
itself. The seat extended the file surface (addendum `5862987983`,
option A), and patch round 1 (`80ee15eecb`) added one note to 7779 and
three to 7946, in the same form and measured the same way. `Fixes
objectstack-ai#10877` stays.
- **For the at-tier review.** The last 7946 note also names two changes
outside the three PRs: `drillDown` (objectui#8885, PR objectstack-ai#8895) and `xAxis`
/ `yAxis` (objectui#10518, PR objectstack-ai#10601). Both landed after the 7946 entry,
and both also make its "`aggregate` is the exception" sentence false.
That sentence of the note can be dropped if the review holds it out of
scope; the rest of the note stands without it.
- Not measured: the wider pending population that names `listViews` /
`NamedListView` or `ObjectChartSchema`. Only the entries the superseding
PRs name were read.
- **Source prose. These are observations and were not filed; carrier:
none.**
- The `ObjectChartSchema.series` TS docblock
(`packages/types/src/objectql.ts`) says "A `type` written on this arm
anyway is an excess property on a literal typed by this interface."
Since PR objectstack-ai#10802 that is false. `{ dataKey: 'amount', type: 'line' }`
compiles in an `ObjectChartSchema` literal, because the union's `{ name
}` arm declares `type` (tsc probe above). The zod mirror still strips
it.
- The zod docblock headed "WHERE THIS ARM IS INSTALLED — TWO ROUTES, TWO
NESTINGS EACH, ONE STRING" (`packages/types/src/zod/objectql.zod.ts`)
still says a named view's `listViews` "is unmirrored", and it still
names a `custom` issue under `options.kanban` on that route. The
docblocks of `checkNamedViewKanbanStrayGroupBy` and
`namedViewKindRefusals` are current.
- **A dispatch premise, corrected.** "Each one already states in its own
body that it supersedes the older entry" holds for
`7924-density-mode-through-fold.md` and
`7928-listviews-by-reference-fold.md`.
`10770-object-chart-react-tier-node.md` names no superseded entry. The
supersession is stated in PR objectstack-ai#10802's body ("Superseded pending
changesets"), which names the objectui#10584, objectui#7946-anchor and
objectui#8086 entries.
- **`main` merged, twice.** `origin/main` `4f38f39286` was merged as
`0ea55c3701`: the two merged commits touch none of the probed sources,
and the probes re-ran byte-identical. `origin/main` `9f0c84a448` was
merged as `bade176fff` (patch round 2), which carries `35d68c4c` (PR
objectstack-ai#10884, objectui#10758). That merge moved probed sources
(`objectql.zod.ts`, `objectql.ts`, `ChartRenderer.tsx`); the probes
re-ran at `50deccd71a`, byte-identical to round 1's head outputs.
Neither merge used a rebase or a force-push.
- **Patch round 2 (at-tier record `5864532186`, FAIL on one note).** The
7779 note said the renderer reads 20 members and leaves 44 unread. That
was true at `4f38f39286`, but PR objectstack-ai#10884 re-took the census to 43 / 21 on
`main`. Per the seat's decision (REWORK `5864543090`), the note now
states no figure. It says the split is no longer the 21 / 43 the entry
counted, names both changes that moved it (objectui#7928 and
objectui#10758), and cites the census pin in
`object-view-unmirrored-keys-7779.test.ts`, which re-derives the figures
on every run (`43 read + 21 unread = 64` at the head). No other note
moved.
- **The headline card.** Each note's headline names the card whose
change supersedes the sentence, as the PR objectstack-ai#10533 notes did. PR objectstack-ai#10828's
notes named their own card instead, because the cause there was a spec
release that has no pending entry.
- **Commit trailers are model-free** (`Co-Authored-By: Claude`), per the
dispatch. The harness offered a trailer that carries a model name, and
it was not used.

Implemented by a `domain:spec#1` dispatch (dev subagent). Session:
`https://claude.ai/code/session_014fWVhLzhxR8qrFsJ5o8TYW`.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
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