Skip to content

feat(types,plugin-grid)!: object-grid and list-view conditionalFormatting take the spec list view's { condition, style } rule only; the native dialect is refused by name (objectui#11533) - #11540

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-11533-grid-listview-rule-dialect-retired
Oct 3, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-11533-grid-listview-rule-dialect-retired

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11533

Clause-②: no (narrowing)

conditionalFormatting on object-grid (ObjectGridSchema, and so the object-view table slot built from it) and on list-view takes ONE rule dialect, the spec list view's { condition, style }. The native { field, operator, value } rule, its expression template predicate and a colour written at the top level of a rule (backgroundColor, borderColor, textColor) are retired with no alias window and refused by name. This executes triage's ruling 5965301211 (retire, as objectui#11522 ruled for the kanban board in 5963861071; never widen the spec's list-view member). Stored stock is not authoring: resolveConditionalFormatting keeps every arm, so a grid or list view STORED with a native rule still paints, byte for byte as before.

What changes

Rule on ObjectGridSchema, an object-view table or a list-view Before (BASE 8bfc0012) After
{ condition, style } (string, envelope or '' condition) accepted on every face accepted, unchanged
native { field, operator, value, backgroundColor } accepted on every face, tolerant and strict refused at field, operator, value and backgroundColor, each naming the retirement
{ expression, backgroundColor } refused as a bare invalid_union at the rule refused at expression and backgroundColor by name
flat CEL { condition, backgroundColor } refused as a bare invalid_union at the rule refused at backgroundColor by name, and style (required)
{ condition, style, backgroundColor } tolerant faces accepted it (the key was stripped, then painted by the shared resolver anyway); strict face refused it as invalid_union refused at backgroundColor on every face
{ condition, style, label } tolerant faces accepted it, strict face refused it refused on every face, unrecognized_keys with the spec rule's own message

The faces read: the ObjectGridSchema and ListViewSchema mirrors, and safeValidateSchema (tolerant) and StrictAnyComponentSchema (strict), each on a list-view node and on an object-view table slot. Both columns were read with a throwaway probe on each tree; it was deleted and never committed.

Not judged here, and unchanged: the authored object-grid node's properties bag. Its members are @objectstack/spec's ComponentPropsMap['object-grid'] row by reference (objectui#11276). The installed 17.5.0 row types conditionalFormatting as unknown, so the bag accepted all six rules before and after. It carries no objectui copy of the rule, and objectui does not narrow a spec row (see Acceptance notes).

  • zod (@object-ui/types, src/zod/objectql.zod.ts). The grid's and the list view's shared rule ConditionalFormattingRuleSchema (module-private, one declaration for both mirrors) is no longer a union. It is the spec ListViewSchema.conditionalFormatting element (SpecListViewRuleSchema, now declared once above the grid mirror and .extend()-ed by both this rule and the kanban rule), so it inherits the spec rule's strictness and its style map. Two things are layered on top: condition stays SpecRuleConditionSchema (objectui's z.string() arm before the spec slot, so a string is not canonicalized and '' is still accepted, objectui#10946), and seven retirement tombstones: field, operator, value, expression, backgroundColor, borderColor, textColor, through a gridRuleKeyRetired message builder. The grid member's .describe() and the "intentionally broader than spec (migration deferred)" docblock lines go; conditionalFormatting leaves that list.
  • TS (objectql.ts). ConditionalFormattingRule is an interface that extends SpecConditionalFormattingRule and declares the same seven keys ?: never. ObjectUIConditionalFormattingRule is deleted and dropped from the root barrel (TS2305 for an importer). tsc reports each retired key at the key (TS2322).
  • @object-ui/plugin-grid. The object-grid registration's conditionalFormatting input description stops advertising both forms: it describes the one rule and names the retired spellings. No render path changes.
  • Docs. A conditionalFormatting row in content/docs/api/schema-reference.md's ObjectGridSchema table; packages/types/src/zod/README.md drops the member from the broader-than-spec list.
  • One changeset (11533-grid-listview-rule-dialect-retired.md): @object-ui/types and @object-ui/plugin-grid minor, BREAKING in the first line, before/after table, respelling, the bag and the paint stated as unchanged.

Mechanism hypotheses, measured

H1, the faces and the shared union (a type-checker census, not grep). A TypeScript LanguageService over all 5,034 tracked TS sources, under the root tsconfig's paths (which resolve @object-ui/types to source): findReferences on each declaration, plus every conditionalFormatting identifier resolved to the declaration it binds.

Declaration Consumers How each moved
zod ConditionalFormattingRuleSchema ObjectGridSchema.conditionalFormatting and the ListViewSchema override (2 refs); through the grid mirror's .shape it also builds the object-view table slot all narrow with it. No other zod schema embeds either member: ObjectGridBlockSchema takes six other mirror members by reference, not this one
TS ConditionalFormattingRule ObjectGridSchema.conditionalFormatting, NamedListView.conditionalFormatting, the root barrel, the 10946 relay pin, a docblock link in the kanban rule narrowed in place (same name, now the one-dialect interface)
TS ObjectUIConditionalFormattingRule the old union and the root barrel only deleted
ObjectGridSchema.conditionalFormatting member (12 refs) ObjectGrid.tsx (5 reads, each handed to the resolver or the harvest as an unknown array), plugin-view's route-2 relay, tests follows the type; plugin-grid and plugin-view type-check green
zod ListViewSchema member (10 refs) ListView.tsx (evaluateConditionalFormatting's rules parameter, the harvest, the relay into its grid node), app-shell ObjectView.tsx's relay (its other operand is any), tests follows the type; plugin-list type-check green
resolveConditionalFormatting types its own input (ConditionalFormattingRuleLike in @object-ui/core) does NOT narrow, by construction
listViewPredicates (the predicate-field harvest) reads readonly unknown[] does NOT narrow: it still projects a stored native rule's field

Can the kanban rule and the grid/list rule become one schema? Measured: same base element (now one declaration), same condition arms (pinned arm by arm), same style (identity, pinned). They differ in exactly two things: the carrier and card each message names, and expression, which the grid and list rule carried and the kanban rule never did (kanban refuses it through the spec's own unrecognized-keys message with its "Did you mean expression → condition?"). Folding them would change object-kanban's landed refusal text and its expression path, a surface objectui#11522's contract record judged on its landing head. Not done here; the report carries the option.

H2, the census. Two instruments. (1) An AST pass (TypeScript parser) over the 59 TS files that mention conditionalFormatting or ConditionalFormattingRule: every object literal with a field + operator pair, a top-level colour beside a predicate key, or an expression beside a style key: 50 hits, each triaged by hand. The filter literals it also caught (UserFilterTabRuleSchema, ReportFilterSchema, filter fixtures) are its live control. (2) A text pass over every other tracked file that mentions the member: the 500 characters after each token, for a retired key in any syntax: 1 window, the kanban row of the schema reference, which teaches the forms as refused.

Where Writers at BASE Disposition
unmirrored-declared-round6-6152.test.ts 1 native (the valid row) and 1 native with a wrong operator (the invalid row) valid respelled { condition: "record.status == 'open'", style: { backgroundColor: '#fee2e2' } }; invalid replaced by { condition: "record.status == 'late'", style: 'color: red' }, because its wrong value (an operator outside the native enum) has no member left to be wrong in
export-options-spec-parity.test.ts 1 native (the neighbour-member control) the native line removed; the control keeps the { condition, style } line and the non-array refusal
objectViewHostSurface.test.tsx (plugin-view) 1 forwarding sentinel { field, operator: 'eq', value, color } respelled { condition: "record.stage == 'won'", style: { color: '#ff0000' } }
gridRowDecorationMembers-8071.test.tsx (plugin-grid) 11 rules carrying retired members (the predicate precedence, the colour members) kept and re-framed as STORED-rule reads; a new block pins refused-at-authoring beside painted-at-render
spec-expression-wire-slots-10946.test.ts none; it indexed the union's second arm reads condition straight off the rule's shape
listConditional.test.ts, predicate-fields.test.ts (core) 10 and 1 untouched: the resolver's arms and the harvest stay
ConditionalFormattingEditor.test.tsx (app-shell) 4 untouched: Studio's upgrade-on-read of stored rules
kanban-conditional-formatting.test.ts, unmirrored-declared-round3-6152.test.ts 4 and 3 untouched: the kanban refusals, and the report component's own rule (a different carrier)
plugin-grid registration description taught both rewritten
objectui examples/, apps/console non-test code, skills/ zero; controls fire (object-grid / list-view in 5 examples/ files, object-grid in 1 console file and 3 skills/ files) none
objectstack examples/ at 4916168 (my own sparse clone) zero. The one conditionalFormatting (app-showcase field-zoo.view.ts) is { condition, style }; controls fire (object-grid / list-view view files, { field, operator, value } filter literals) none
objectstack-ai/hotcrm NOT MEASURED: not in this container. Triage measured zero none

H3, stored rules still paint. Before and after, through the real ObjectGrid and through the real ListView handing its node to the real grid, for all six rules of the table: the readings are byte-identical, for example native: Alpha=[background-color: rgb(1, 2, 3);] Beta=[null] on both trees and both renderers. Read paths that could validate a stored view: none on the render path (SchemaRenderer runs only the dev-only structural validateSchema; nothing parses a grid or list-view node). Outside packages/types, the only non-test callers of objectui's zod faces are the CLI's objectui validate / objectui check (authoring doors) and three unrelated schema imports (a dashboard widget, a dashboard designer door, a map config). Studio's clientValidation judges a view draft against @objectstack/spec/ui's ViewSchema, not objectui's face, so this change does not reach it; the installed spec's list-view rule already refuses the native rule there (unrecognized_keys naming field, operator, value, backgroundColor), at BASE as now. Premise held; nothing for the seat.

H4, the refusals. One arm, so each refusal sits at the retired key's own path; the messages are byte-identical on the tolerant and strict faces and on both carriers. Quoted, with backticks as in the source:

  • at conditionalFormatting.N.field (operator and value the same, key swapped):

    field belongs to the native rule dialect { field, operator, value }, which conditionalFormatting on object-grid and list-view no longer accepts: RETIRED (objectui#11533), with no alias window. A rule is { condition, style } — a CEL condition over record.* and a CSS style map, the rule @objectstack/spec's ListViewSchema.conditionalFormatting declares. Respell { field: 'priority', operator: 'equals', value: 'high', backgroundColor: '#fee2e2' } as { condition: "record.priority == 'high'", style: { backgroundColor: '#fee2e2' } } (not_equals is !=, greater_than is >, less_than is <, contains is .contains(…), in is record.f in [ … ]).

  • at .expression:

    expression is the template predicate of the native rule dialect, which conditionalFormatting on object-grid and list-view no longer accepts: RETIRED (objectui#11533), with no alias window. A rule is { condition, style } — […same sentence…]. Write the predicate as the rule's condition, in CEL over record.* with no ${…} wrapper: { expression: '${record.amount > 1000}', backgroundColor: '#fee2e2' } is { condition: 'record.amount > 1000', style: { backgroundColor: '#fee2e2' } }.

  • at .backgroundColor (borderColor the same; textColor ends style: { color }):

    backgroundColor is a colour written at the top level of the rule, which conditionalFormatting on object-grid and list-view no longer accepts: RETIRED (objectui#11533), with no alias window. A rule is { condition, style } — […same sentence…]. Move the colour into the rule's CSS map: style: { backgroundColor }.

  • Beside these, a rule with no condition also draws invalid_union at .condition and invalid_type at .style (both required); an undeclared key draws the spec rule's own message, "Unrecognized key(s) on this conditional formatting rule: label. …".

The pins

  • packages/types/src/__tests__/grid-list-view-conditional-formatting-11533.test.ts (new, 44 tests): on each of the six faces, the { condition, style } control (string and envelope); the native rule refused at each retired key with the message starting with the key's name and containing RETIRED (objectui#11533), { condition, style } and the respelling, while the accepted rule at index 0 of the same document draws no issue; expression refused with its respelling; the flat CEL rule refused at the colour key with no invalid_union at the rule; all three colour keys refused beside a style; the spec's unrecognized-keys message. The reference: the two mirrors share one rule object, style is the spec element's own member (identity), condition's second arm is the spec slot and both arms are the kanban rule's, with the control that condition is not the bare slot. The TS face: @ts-expect-error on each retired key, on a native rule in an ObjectGridSchema and a ListViewSchema literal, keyof equality with the zod input, and the three members (ObjectGridSchema, ListViewSchema, NamedListView) carrying the one rule.
  • gridRowDecorationMembers-8071.test.tsx (plugin-grid): the stored native rule refused by ObjectGridSchema at field with the retirement, AND painted by the real grid on the matching row only; the respelling accepted and painting the same row the same colour as the control.
  • ListView.storedRuleDialect-11533.test.tsx (plugin-list, new): a stored list view's native rules refused by ListViewSchema at field, AND handed to its object-grid node untouched; the respelling as the control.
  • apps/console/.../registry-inputs-spec-parity.test.ts: the object-grid.conditionalFormatting member-pin prose says the retired members are stored-rule reads now (prose only, no logic).

Pending changesets (read, not recalled)

Read as text: every pending entry naming conditionalFormatting, ConditionalFormattingRule, the resolver, a rule union, "broader than spec", or a file this diff touches (41 by symbol and prose, and the 51 that check:changeset-claims lists by file name, each grepped for the symbols this diff moves).

  • Dated note appended: 10946-expression-wire-slots-by-reference.md. Its "the list view's and the kanban board's rule unions share one condition schema" now describes no union at all; the note keeps every condition claim, which still holds and is still pinned. (+16/−0)
  • Dated note appended: 11522-kanban-rule-dialect-retired.md. Its "Not changed" paragraph gives "because the grid's and the list view's rule union still declares them" as the reason the evaluator keeps every arm; the reason is now the stored-rule compatibility read. (+15/−0)
  • Left alone, still true: 7664-kanban-arm-plugin-dialect.md (kanban only, already noted by objectui#11522); 6152-objectgrid-round6-mirrored.md (says the mirror declares the key typed as the twin types it, still true, and makes no dialect claim); 6152-unmirrored-round3.md (the report's rule); 11276-object-grid-properties-bag.md (the bag, which did not move); 10976, 10885, 10758, 7928 (relays and named views, unchanged); 10689 ×2, 7216, 7727, 8167, 8972, 9318, row-predicate-phase2-record-only-5741 (harvest and condition scope, unchanged); member-pins-object-grid-row-layer-8071 (names the key only); 8313, 8801, 9242, 8932-retire-kanban-enhanced, 7742, 7322, 4730, 10275, 8818 (kanban, i18n or FLS readings that name the key only); 4535 and 6956 (name export-options-spec-parity.test.ts for its export-options rows, not the neighbour control); the entries naming KanbanImpl.tsx (card, lane and click behaviour, none about the evaluator comment); 10719 (a different "rule union").
  • check:changeset-overwrite reports the two modified entries with their declarations unchanged, which is the appended-note case.

Verification

  • Final head ebb37b7b. It differs from aab1cc53 (where the suites below ran) by one test file, ListView.storedRuleDialect-11533.test.tsx (as any to as never), re-run at ebb37b7b with plugin-list's type-check.
  • pnpm exec vitest run --maxWorkers=2 packages/types/ packages/plugin-grid/ at aab1cc53: 527 files, 10,923 tests passed (VERDICT command-exit 0).
  • 41 targeted files at aab1cc53 (the plugin-list, plugin-view, plugin-kanban, app-shell, core, components, react, i18n, console and schema-catalog files that read the member or the changed files): 968 tests passed. 19 scripts/__tests__ suites that read the changed files (one-authority, doc-links, doc-snippet, readme-exports, changeset gates and others): 1,093 tests passed.
  • type-check (each script name echoed, each exit read separately), after turbo run build of the dependency closure: @object-ui/types (src, examples and test configs; --listFilesOnly confirms the new pin file is compiled), plugin-grid, plugin-list (again at ebb37b7b), plugin-view, plugin-kanban: exit 0.
  • Reverse leg, committed first, restored from HEAD: a native rule appended to plugin-grid's specExpressionWire-10946.test.tsx as an ObjectGridSchema literal turned plugin-grid's test type-check red (exit 2, TS2322 at field, operator, value), proving it reads the rebuilt declaration; after restore the blob equals HEAD's and git diff HEAD is empty.
  • After the scoped build (35/35 tasks): check:doc-snippets (777 of 777 blocks, 0 failed), check:doc-examples (exit 0, ledger-exact), check:doc-types (exit 0). With the console built: check:sdui-registration-pins exit 0, check:component-surface-parity (report-only) exit 0 with no object-grid row.
  • At ebb37b7b, all exit 0: check:new-line-citations (0 new), check:control-bytes, check-changeset-no-major, check-changeset-presence, check-changeset-fixed, check:changeset-claims, check:pending-changeset-literals, check:test-path-roots, check-type-check-coverage, check-doc-links, check:spec-symbols, check:doc-fences, check-doc-expression-carriage, check:readme-exports, check-changeset-overwrite; check-governed-queue-guard --test over the 18 paths: NOT GOVERNED.
  • NOT MEASURED: the repo-wide lint and the full test matrix (CI's). A narrowed eslint pass over the 13 changed TS files found no error this diff introduces.

Acceptance notes

  • The authored object-grid bag. At the installed spec the bag still accepts any rule, because the spec row's member is unknown; objectstack main (4916168) types it as the list view's own member by reference, so the bag narrows with the @objectstack/* bump that carries objectstack PR #21463. Carrier: objectui#11438 (the 17.6.0 trunk) or the bump after it.
  • The skills guide. skills/objectui/guides/schema-expressions.md's "Legacy shapes are translated" bullet says the native and expression forms "still work" under a heading naming list, grid and kanban: true of the evaluator, now false of authoring on all three carriers. skills/** is governed and this PR stays ungoverned, so it is left; it is the same item as objectui#11522's contract record escalation (c).
  • The evaluator's native arms now have no authored carrier on any objectui face; they serve stored rules only. Their removal is the separate act after a stored-stock census, as triage ruled.
  • The report component's own rule (ReportComponentSchema.conditionalFormatting, a { field, operator, value, backgroundColor?, textColor? } shape ReportViewer reads per cell) is a separate declaration, and neither spec line declares a report rule member. Untouched. Carrier: none.
  • ROADMAP.md records the dual-format union as delivered; it is a dated log and is left.
  • Files outside the claim's list: packages/plugin-kanban/src/KanbanImpl.tsx (comment only: getCardStyles' stated reason became false), ListView.storedRuleDialect-11533.test.tsx (the H3 pin), objectViewHostSurface.test.tsx (sentinel respelled), the console pin prose.

Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC


Generated by Claude Code

claude added 4 commits October 3, 2026 04:57
…ting take the spec list view's { condition, style } rule only; the native dialect is refused by name (objectui#11533)

The grid's and the list view's shared rule is the spec ListViewSchema
rule element by reference, .extend()-ed with objectui's string condition
arm and seven retirement tombstones (field, operator, value, expression,
backgroundColor, borderColor, textColor). The TS twin
ConditionalFormattingRule extends SpecConditionalFormattingRule with the
same seven keys declared ?: never; ObjectUIConditionalFormattingRule is
deleted. plugin-grid's input description stops advertising both forms.
resolveConditionalFormatting is untouched: stored rules still paint.

WIP step 1 of the card: faces only; tests, docs and changeset follow.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
…t-view rule refusals on every zod face and the TS face, and that a stored native rule still paints (objectui#11533)

- grid-list-view-conditional-formatting-11533.test.ts: each retired key
  refused by name on ObjectGridSchema, ListViewSchema, and the tolerant
  and strict faces on a list-view node and an object-view table slot;
  the spec rule by reference (style identity, condition arms, the one
  shared declaration); the TS face by @ts-expect-error and keyof parity.
- gridRowDecorationMembers-8071: the retired members are re-framed as
  stored-rule reads; a new describe pins refused-at-authoring beside
  painted-at-render, with the respelling as the control.
- ListView.storedRuleDialect-11533: a stored list view's native rules
  reach the grid untouched while ListViewSchema refuses them.
- Respelled: round 6 MIRRORED row, the export-options neighbour control,
  the 10946 list-view rule pin, the object-view forwarding sentinel.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
… pin prose for the grid and list-view rule retirement (objectui#11533)

- .changeset/11533-grid-listview-rule-dialect-retired.md: types and
  plugin-grid minor, BREAKING spelled out, before/after table,
  respelling, the bag left to the spec row, what still paints.
- Dated notes on 10946 (the list view's rule is no longer a union) and
  11522 (the evaluator keeps its arms as a stored-rule read, not because
  the grid and list view still declare them).
- schema-reference: an ObjectGridSchema conditionalFormatting row.
- zod README: conditionalFormatting leaves the broader-than-spec list.
- registry-inputs-spec-parity: the object-grid member-pin prose says the
  retired members are stored-rule reads now.
- KanbanImpl: the getCardStyles comment's reason, made false by this
  change, is corrected (comment only).

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

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

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

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

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

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

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

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

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

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

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

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

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

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

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

.changeset/4535-export-options-spec-parity.md

  • names export-options-spec-parity.test.ts → packages/types/src/__tests__/export-options-spec-parity.test.ts — edited by this change

    So the reason is corrected and, more to the point, it stops being load-bearing. A new export-options-spec-parity.test.ts reads the object branch out of the spec package that is actually installed, at test time, and asserts against it: the key set, the format enum ('pdf' absent from both sides), upstream strictness, the migration prescription the 'pdf' refusal carries, and the parse-time array lift. The key set is projected from keyof ListViewExportOptions through an exhaustive Record, so a local key added or dropped fails to compile rather than passing a comparison against a hand-copied list.

.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/6051-gantt-flat-config-declared-keys.md

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

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

.changeset/6152-object-form-unmirrored-members.md

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

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

.changeset/6170-retire-timeline-dead-keys.md

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

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

.changeset/6172-markdown-kanban-one-authority.md

  • names KanbanImpl.tsx → packages/plugin-kanban/src/KanbanImpl.tsx — edited by this change

    KanbanCard / KanbanColumn — the three in-package copies converged to one. KanbanImpl.tsx and KanbanEnhanced.tsx each redeclared both names. A TypeScript-AST comparison found them strict-SUBSET copies of ./types with nothing typed differently, so their extra members moved onto the one declaration and, at this change, both files re-pointed at it.

.changeset/6896-retire-chart-inline-data.md

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

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

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

  • names KanbanImpl.tsx → packages/plugin-kanban/src/KanbanImpl.tsx — edited by this change

    Breaking, deliberately. KanbanColumn declared its card list as items in complex.ts and in the zod mirror complex.zod.ts. Every board reads cards. Measured on origin/main 78a3cc238: KanbanImpl.tsx reads .cards on 12 lines, KanbanEnhanced.tsx on 8, and bucketCardsIntoColumns twice more as col.cards || []; .items had zero read sites in either board (a same-shaped .title control on the same two files returns 8 and 3, so those zeros are readings and not a mis-shaped probe). Both catalog entries, the plugin docs and content/docs/api/schema-reference.md all author cards.

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

    Breaking, deliberately. KanbanColumn declared its card list as items in complex.ts and in the zod mirror complex.zod.ts. Every board reads cards. Measured on origin/main 78a3cc238: KanbanImpl.tsx reads .cards on 12 lines, KanbanEnhanced.tsx on 8, and bucketCardsIntoColumns twice more as col.cards || []; .items had zero read sites in either board (a same-shaped .title control on the same two files returns 8 and 3, so those zeros are readings and not a mis-shaped probe). Both catalog entries, the plugin docs and content/docs/api/schema-reference.md all author cards.

.changeset/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/6951-tree-view-data-retired.md

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

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

.changeset/6956-listview-export-options-spec-mirror.md

  • names export-options-spec-parity.test.ts → packages/types/src/__tests__/export-options-spec-parity.test.ts — edited by this change

    What was measured, on this branch's base. The mirror declared a pre-test(scripts): check-shell-escape-residue reds on an agent-tree document no scan root reaches (#7413) #8010 shape of its own — 'pdf' in both branches, no streaming, a non-strict z.object — and ListViewInferred is z.input of that mirror, so the ListViewSchema TYPE the ListView renderer is written against disagreed with its sibling ObjectGridSchema['exportOptions'] (the clean five-key ListViewExportOptions), and the renderer could only read streaming through as any. Against the installed pin (@objectstack/spec@17.2.0, not a working tree), ListViewSchema.shape.exportOptions from @objectstack/spec/ui lifts ['csv', 'xlsx'] to { formats: ['csv', 'xlsx'] }, refuses ['csv', 'pdf'] with the migration prescription, refuses { formats: ['csv'], compression: 'gzip' } (strict), and accepts { formats: ['csv'], streaming: true } with the value intact. The mirror now IS that schema object, so the four verdicts are the spec's by construction; export-options-spec-parity.test.ts pins the identity, the four verdicts, and the survival of streaming through a parse.

.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 (77cb489b4, maintainer ruling 2026-09-02), pinned by objectql-record-source-refinement-6939.test.ts — and it was hand-copied into five plugin components with no gate holding them together. A change to the ruled order had five edit sites and nothing that noticed a missed one; that is the AGENTS.md #0.1 drift class.

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

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

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

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

    ⚠️ Dated note, 2026-10-03 — KanbanConditionalFormattingRuleSchema is no longer a union — objectui#11522. At this change KanbanConditionalFormattingRuleSchema was "the rule union the 'object-kanban' arm already applied": the native { field, operator, value } comparison or { condition, style }. Now it is one object, the spec list view's { condition, style } rule by reference, and the native rule and a top-level colour key (backgroundColor, borderColor, textColor) are refused by name; the 'kanban' arm it was shared with has itself retired in this release (objectui#8802). It keeps its name, and it is a module export of src/zod/objectql.zod.ts inside @object-ui/types, NOT an export of the @object-ui/types/zod barrel. Measured at objectui#11522's change: that barrel re-exports KanbanCardSchema, KanbanColumnSchema and ObjectKanbanSchema from the kanban family and not this schema, and no entry of the package's exports map carries it. So the bullet above that calls it "newly exported from @object-ui/types/zod" does not hold in this release either; whether it held at objectui#7664's own commit was not measured. The rest of this entry is kept as the reading of this change.

.changeset/7804-objectql-handler-key-arms.md

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

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

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

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

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

.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/7997-detail-view-related-retired.md

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

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

.changeset/8067-component-input-member-kind.md

  • names apps/console/src/__tests__/registry-inputs-spec-parity.test.ts → apps/console/src/__tests__/registry-inputs-spec-parity.test.ts — edited by this change

    A registration's type: 'array' said a value was a list and stopped there, so a member that drifted from @objectstack/spec was invisible to every layer that reads a declaration. page:header.actions is the measured cost: the contract declares z.array(z.string()) ("Action IDs"), the renderer read the members as ActionDef objects, and the repo-wide parity gate in apps/console/src/__tests__/registry-inputs-spec-parity.test.ts stayed green for the whole life of the drift because both sides carried the key and neither could say what was inside it. What settled it was a maintainer ruling, not a test — and even after the fix, "these are ids" survived only as English in the registration's description.

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

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

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

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

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

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

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

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

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

.changeset/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/9187-record-highlights-layout-two-values.md

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 160c6c6ea (merge-base with origin/main): 15 file(s) changed outside .changeset/, read against 2069 pending declaration(s) that publish a body (2701 pending in total). · run

@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3551.2 KB 3574.6 KB
Main entry chunk (gzip) 150.3 KB 350 KB
Entry file index-4WJA1n5t.js —
Status PASS —

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


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 571.09KB 136.86KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 230.46KB 63.94KB
fields (index.js) 261.51KB 66.22KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 39.47KB 11.25KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.17KB 15.46KB
plugin-charts (index.js) 83.60KB 22.89KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 142.26KB 38.44KB
plugin-designer (index.js) 231.28KB 48.76KB
plugin-detail (index.js) 245.43KB 64.53KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 172.73KB 44.50KB
plugin-gantt (index.js) 179.16KB 45.06KB
plugin-grid (index.js) 233.34KB 63.99KB
plugin-kanban (index.js) 49.59KB 15.58KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 38.80KB 11.71KB
plugin-tree (index.js) 14.51KB 5.15KB
plugin-view (index.js) 90.23KB 22.73KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.16KB 1.96KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.07KB 2.39KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

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

Inputs: card #11533 (body; comments 5965301211 ruling, 5965613954 claim, 5966105748 dev report), PR #11540 (body, 18-file list, the diff against main at this head), the check-runs on the head, and every pending .changeset/*.md at the head read as text from the git tree (.changeset tree b6a39efb, 2,704 blobs, not truncated, 0 fetch errors, 5,311,696 bytes fetched equal to the tree's sum). Head files read by REST where a judgment turned on them: packages/types/src/index.ts, zod/index.zod.ts, zod/objectql.zod.ts, zod/tombstone.zod.ts, zod/complex.zod.ts, src/objectql.ts, src/complex.ts, src/data.ts, packages/types/package.json, .changeset/config.json, packages/core/src/evaluator/listConditional.ts, packages/core/src/validation/schema-validator.ts, packages/react/src/SchemaRenderer.tsx, packages/app-shell/src/views/metadata-admin/clientValidation.ts, packages/plugin-grid/src/{index,ObjectGrid}.tsx, packages/plugin-list/src/{index,ListView}.tsx. The two precedent records on PR #11532 (5964941644, 5965014904), as the brief names them, for the standard of care on export loci. For the two "installed spec" claims, @objectstack/spec's ui/view.zod.ts and ui/component.zod.ts at the objectstack tag @objectstack/spec@17.5.0 (e2394c44), the version packages/types pins (^17.5.0); the lockfile's exact resolution was not read. The PR's two issue comments were indexed only (both gate bots; no earlier record names this head on the PR or the card). Nothing built, run or re-run.

Check-runs on the head: first read 2026-10-03T05:54:11Z, 42 runs, 29 success, 3 skipped (dependabot, Test (coverage), Test (coverage shard): structural skips), 10 in_progress (Type Check, Spec Main Shape Gate, Test shards 1 to 8), 0 red. Second read 2026-10-03T06:10:38Z: 43 runs (the Test rollup appeared), 40 success, 3 skipped, 0 pending, 0 red. Third read 2026-10-03T06:16:01Z, just before this post: unchanged, 40 success, 3 skipped, 0 pending, 0 red. Type Check, Spec Main Shape Gate, Lint, Governed Surface Queue Guard, Changeset Bump Policy, Changeset Claim Re-read, Changeset Declaration, Changeset Fixed Group Check, Changeset Overwrite Report, Doc Snippet Type Check, README Export Check, Line Citation Gate and all eight shards are success. The dev's NOT MEASURED families (repo-wide lint, the full matrix, spec-main-shape) are answered green by the head's own runs. Nothing waited on.

① Derived judgments

Accept-set and public-surface changes the diff implies, each read against the head sources:

  1. Zod ConditionalFormattingRuleSchema (objectql.zod.ts:443, module-private, one declaration feeding ObjectGridSchema.conditionalFormatting at :732 and the ListViewSchema override at :2187): a z.union([native z.object, loose spec-shape z.object]) becomes SpecListViewRuleSchema.extend({ condition: SpecRuleConditionSchema, seven tombstones }), where SpecListViewRuleSchema (:333) is the installed spec's ListViewSchema.conditionalFormatting[] element by reference. RIGHT, and the ruling's shape (the spec list view's rule, as objectui#11522 did). At 17.5.0 that element is a strictObject with exactly condition and style: z.record(z.string(), z.string()) (spec ui/view.zod.ts:2772), so the base adds no accepted member and style's accept set equals the old arm's record of strings. Every input the new rule accepts, the old spec arm accepted; nothing widens.
  2. Native { field, operator, value, backgroundColor?, textColor?, borderColor?, expression? }: accepted before on every face, refused now at each present key via retirementTombstone() (z.never({ error }).optional(), tombstone.zod.ts:64), an absent key passing. RIGHT; named in the PR table and the changeset table; the message text is quoted in the PR and matches gridRuleKeyRetired.
  3. { expression, backgroundColor } with no native triple: a bare invalid_union before (neither arm matched), refused at expression and the colour key by name now. RIGHT; named. The expression tombstone is the one key the grid rule carries that the kanban rule does not (kanban refuses expression through the spec element's own alias path); it widens nothing (z.input is undefined).
  4. Flat CEL { condition, backgroundColor }: invalid_union before, refused at the colour key plus invalid_type at the required style now, with no issue at the bare rule path (pinned). RIGHT; named.
  5. { condition, style, backgroundColor | borderColor | textColor }: the tolerant face accepted and stripped the key before (while the shared resolver painted it over style, listConditional.ts:509-511); refused on every face now. RIGHT; named in both tables.
  6. Any other undeclared key (label): stripped on the tolerant face before, refused as unrecognized_keys with the spec rule's own message now, because .extend() keeps the spec element's strictness. RIGHT and within the ruling (the spec's rule by reference brings the spec's strictness); named as the last table row.
  7. condition: still SpecRuleConditionSchema (z.string() first, the spec slot by reference second, :359-362), so a string is not canonicalized and '' is still accepted. UNCHANGED; the 10946 pins now read it off .shape directly, and the new pin asserts both arms are the kanban rule's arms by identity. RIGHT.
  8. style: the spec element's own member by identity (pinned), z.input equal to the TS Record of strings (pinned by expectTypeOf, enforced by the green Type Check). RIGHT.
  9. The object-view table slot: rebuilt from ObjectGridSchema.shape (:1193-1199), so it narrows with the grid member; ObjectGridBlockSchema (:4869) takes six other mirror members and not this one, so the authored object-grid node's bag does not move (judgment 14). RIGHT; the slot is named in the changeset's first line and table header.
  10. TS ConditionalFormattingRule (objectql.ts:497): the union of ObjectUIConditionalFormattingRule | SpecConditionalFormattingRule becomes interface extends SpecConditionalFormattingRule plus seven ?: never. RIGHT; @ts-expect-error on every retired key on a rule, an ObjectGridSchema literal and a ListViewSchema literal, keyof parity with the zod input, and SpecConditionalFormattingRule matching the new interface are pinned; Type Check green. The TS ListViewSchema member follows through ListViewAuthored, built from ListViewInferred = z.input of the zod schema (objectql.zod.ts:2326, objectql.ts:3227), which is what the changeset says.
  11. ObjectUIConditionalFormattingRule deleted (objectql.ts) and dropped from the root barrel (index.ts:482-488 hunk): a public type leaves @object-ui/types. RIGHT, and NAMED in the changeset ("removed from @object-ui/types. Importing it is a compile error (TS2305)"). At the head the only remaining mention is the docblock of its successor (objectql.ts:473); no exports entry can re-export a deleted declaration, and Type Check is green. No pending entry other than the new changeset names the symbol (grep over all 2,704).
  12. @object-ui/plugin-grid: the object-grid registration's conditionalFormatting input description (index.tsx:268) is the one runtime string that changes; ObjectGridComponentProps.schema is ObjectGridSchema (ObjectGrid.tsx:629-630), so the published props type narrows transitively. RIGHT; both named in the changeset's plugin-grid paragraph. The changeset names the type as ObjectGridProps, which is on the barrel (index.tsx:49) as the deprecated alias of ObjectGridComponentProps since objectui#4650: a real export, so the sentence is true, with the deprecated name chosen over the canonical one. Noted, not a defect. No render path changes: ObjectGrid.tsx:2970 still hands every rule to resolveConditionalFormatting.
  13. resolveConditionalFormatting (listConditional.ts:532, imported from @object-ui/core by ObjectGrid.tsx:60 and ListView.tsx:24): untouched, types its own ConditionalFormattingRuleLike (:440-454), reads condition, expression, field/operator/value and the three colour overrides. Every arm is a stored-rule compatibility read now, which is exactly the ruling's condition for keeping them ("Stored stock is not authoring"; removal only after a stored-stock census, a separate act). RIGHT; the listViewPredicates harvest reads readonly unknown[] and does not narrow either.
  14. The authored object-grid node's properties bag: propsBag('object-grid', stripImportedDefaults(SpecObjectGridPropsSchema)) (:4874), the spec row by reference, and at 17.5.0 that row's member is conditionalFormatting: z.unknown().optional() (spec ui/component.zod.ts:3571). So the bag accepted every rule before and after. RIGHT that it is not narrowed here: the ruling's landing names ObjectGridSchema and the list view's override, the claim scoped the bag to "if it carries its own copy of the rule" and it carries none, and objectui does not narrow a spec row. Named in the PR body, the changeset ("Not judged here") and the new schema-reference.md row. See ③ for the carrier.
  15. KanbanConditionalFormattingRuleSchema: unchanged in accept set; only its base element's declaration moved above the grid mirror (:333, the memoised boundary walker returns the same object) and the two rules now .extend() the one declaration. Pinned: the grid rule's condition arms are the kanban rule's arms by identity. RIGHT.
  16. Docs: a conditionalFormatting row added to the ObjectGridSchema table in content/docs/api/schema-reference.md, and packages/types/src/zod/README.md drops the member from the broader-than-spec list and states the new shape. Both read true against judgments 1, 13 and 14. The .describe() strings on the two members change with the shape. RIGHT.

② Semver level

Changeset 11533-grid-listview-rule-dialect-retired.md: '@object-ui/types': minor, '@object-ui/plugin-grid': minor, BREAKING in the first line, before/after table, respelling, per-package paragraphs, the bag stated as not judged, "Not changed" paragraph, pins. The two packages are the two whose published source moves (judgments 1 to 12); both sit in the one fixed group of .changeset/config.json, so the group bumps together either way, and Changeset Fixed Group Check, Changeset Bump Policy and Changeset Declaration are green. minor for a spelled-out break is this repo's version-alignment rule (the fixed group's major follows @objectstack; the pending 10946 and 11522 entries state it). Level: RIGHT. @object-ui/plugin-kanban (a comment in KanbanImpl.tsx), @object-ui/plugin-list and @object-ui/plugin-view (tests only), the console (a test) and content/ are not published behaviour changes and are rightly undeclared; plugin-list's evaluateConditionalFormatting narrows only through @object-ui/types, and the types entry says so.

Export loci, each sentence checked at the head with the care the 11522 round-1 FAIL set: ConditionalFormattingRule "exported from @object-ui/types" (index.ts:488); SpecConditionalFormattingRule (:487); "types the conditionalFormatting members of ObjectGridSchema and NamedListView" (objectql.ts:1378, :2890); "the ListViewSchema type's member is the zod rule's input" (z.input, judgment 10); "the rules parameter of @object-ui/plugin-list's evaluateConditionalFormatting" (plugin-list/src/index.tsx:22 exports it; ListView.tsx:787-789 types rules?: ListViewSchema['conditionalFormatting']); "ObjectGridSchema and ListViewSchema mirrors (both on the @object-ui/types/zod barrel)" (index.zod.ts:323, :335); the shared zod rule "is module-private" (const, unexported, absent from the barrel, no export *; the new pin imports the barrel's two mirrors and reads the element off .shape); "resolveConditionalFormatting in @object-ui/core" (imported from that package by both renderers); ObjectGridProps (judgment 12). Every locus TRUE. One pre-existing line beside the diff disagrees with the changeset and the source: zod/README.md's "The TS type is z.infer of ListViewSchema and ListViewRuntimeProps" (diff context, untouched by this PR) says z.infer where ListViewAuthored is built from z.input (objectql.zod.ts:2326); the changeset's z.input is the source's reading. The README line predates this card and is not its defect; noted for the seat.

Clause-②: no (narrowing). RIGHT. Every face moves one way (judgments 1 to 6, 10); condition and style keep their accept sets (7, 8); the kanban rule and the bag do not move (14, 15).

Pending sweep (read, not recalled; 2,704 entries, grepped for conditionalFormatting, ConditionalFormattingRule, ObjectUIConditionalFormattingRule, the resolver, "rule union" and its variants, "broader than spec", "migration deferred", the native and expression spellings, "dialect", the kanban rule, 11522, the colour keys, KanbanImpl, and every file this diff touches, then each hit read in context). The readings this change makes false are exactly two, and both carry a dated, append-only note in the house form (⚠️ **Dated note, 2026-10-03 — topic — objectui#11533.**, closing "The rest of this entry is kept as the reading of this change"):

  • 10946-expression-wire-slots-by-reference.md line 9, "the list view's and the kanban board's rule unions share one condition schema": now describes no union. Note RIGHT (+16/-0, hunk @@ -46,3 +46,19 @@, nothing removed): it names the sentence, states the one-object shape, keeps the condition claims that still hold and are still pinned, and adds that ConditionalFormattingRule is still built on SpecConditionalFormattingRule, which is true (judgment 10).
  • 11522-kanban-rule-dialect-retired.md line 28, "keeps every arm, because the grid's and the list view's rule union still declares them", and line 24's "the list view's and the grid's { condition, style } arm": the reason and the word "arm" are now false. Note RIGHT (+15/-0, hunk @@ -28,3 +28,18 @@): the reason becomes the stored-rule compatibility read, which is the ruling's own reason, and the shared condition is named as their whole rule now, pinned arm by arm.
    Entries examined and rightly left alone, each read: 7664-kanban-arm-plugin-dialect (kanban only; its union and locus sentences already carry objectui#11522's note); 6152-objectgrid-round6-mirrored and the 2026-10-02 correction in 11276-object-grid-properties-bag (the mirror declares the member "typed as the TypeScript twin types it", still true after the twin narrowed; the bag "still judges these keys inside properties", still true); 11276's own bag paragraph (the spec row judges the bag, still true); member-pins-object-grid-row-layer-8071 (names the key only); 10689 x2 (the harvest, which still projects a stored rule's field), 10758, 10885, 10976, 7928 (relay, named-view and table-slot key lists), 7727 and row-predicate-phase2-record-only-5741 (the condition's scope and fault policy at the resolver), 7742 (objectFields reaching the resolver), 8313, 8801, 8932, 9242, 6152-unmirrored-round3 (the report component's own rule, a separate declaration), 10719 (a different "rule union"), 3917 (an ActionCondition branch); the 42 native-spelling hits outside that set are filter rules, sort entries, chart axes and column objects; the 129 dialect hits outside it are filter, kanban-board and body dialects; the 11 KanbanImpl entries describe card, lane and click behaviour, none the getCardStyles comment; the 11 colour-key entries are charts, designer canvases and gantt. No false reading is left without a note; the sweep passes. Changeset Overwrite Report is green (two modified entries, declarations unchanged: the appended-note case).

③ Boundary flags

open_questions: none declared; none found.

Dev deviations, each: (1) files outside the claim's enumerated loci, judged below; (2) the bag not narrowed, judgment 14; (3) H1's one-schema fold not taken, judged below; (4) objectstack read from a sparse clone in the dev's scratch, no git command in the shared checkout, and one search/code GET refused 403 and not retried: process, no contract bearing; (5) model-free commit trailers per objectui AGENTS.md: not a contract matter.

Out-of-scope findings, each answered: (a) skills/objectui/guides/schema-expressions.md's "Legacy shapes are translated" bullet: the same item as objectui#11522's record escalation (c), already filed as objectui#11534; nothing further owed here. (b) The authored object-grid bag still accepts any rule at installed spec 17.5.0, and narrows with the @objectstack/* bump that carries objectstack PR #21463 (the dev reads objectstack main 4916168 as typing the row's member by reference; not verifiable from these inputs and not needed for the verdict). ANSWERED, not escalated as a card: it is outside this card's accept set (judgment 14), the narrowing arrives with the row and needs no objectui rule code, and schema-reference.md's new row ("judged by the installed spec") stays true on either side of the bump. Owed to the seat: a one-line re-read of the bag's accept set, and of the two retirement messages' "object-grid and list-view" wording, in the review of objectui#11438 or whichever bump carries it, which the claim's serial constraints already name.

Boundary note, each item:

  • ObjectUIConditionalFormattingRule leaving the public exports IS named in the changeset, with the TS2305 consequence (judgment 11). RIGHT.
  • The H3 premise is consistent with the head. Studio's clientValidation.ts:656-663 loads ViewItemSchema, ViewSchema and ViewMetadataSchema from @objectstack/spec/ui for the view kind and imports nothing from @object-ui/types's zod face; the render path's only check is SchemaRenderer.tsx:2122, __DEV__ ? validateSchemaOnce(schema), whose validateSchema (core/src/validation/schema-validator.ts:525) imports a TS type from @object-ui/types and no zod mirror, so it is structural. No read path validates a stored view against the narrowed face; a stored native rule reaches the resolver as stored, which the two new renderer pins assert (the grid paints it, the list view hands it to its grid untouched).
  • The bag: outside this card's accept set, a gap the spec bump closes, not one the ruling covers (judgment 14, finding (b)).
  • Files outside the claim's enumerated list, each: KanbanImpl.tsx (comment only, getCardStyles' stated reason "whose rule union still declares the native and colour-key arms" became false with this diff) WARRANTED by the same no-false-reading rule the dated notes serve, not by a gate; ListView.storedRuleDialect-11533.test.tsx (new) WARRANTED, within the claim's "fixtures and tests" surface, pinning the list-view half of the ruling's "stored rules stay readable" beside the authoring refusal; objectViewHostSurface.test.tsx (the forwarding sentinel respelled) NOT forced by tsc (the sentinel map is Record of unknown) but WARRANTED, within the claim's "fixtures and tests respelled": a sentinel authored in a retired dialect would teach the dialect; registry-inputs-spec-parity.test.ts NOT gate-forced (as the 11522 record found for the same file) but WARRANTED, prose only: its pins text described the three predicate members as alternative authored members, false now, and it now says the rows pin stored-rule reads; zod/README.md FORCED by the claim's own "migration deferred comments removed" line, since the README listed the member among the broader-than-spec configs.
  • Two rule instances on one shared base over a single schema with the kanban rule: RIGHT for this card. The ruling asked for the kanban's shape, not a fold; the shared base element already removes the drift the fold would guard against (one declaration, pinned by identity and arm by arm); the measured differences are message text and the expression path, where a fold would change object-kanban's landed refusal text that objectui#11522's record judged on its head and owe that entry a further note. The fold stays an option the dev's report carries, not something owed.

Dev NOT MEASURED items: hotcrm, accepted on triage's own zero (5965301211); spec-main-shape, lint and the full matrix, answered green on the head (second read).

Implemented-by: claude/issue-11533-grid-listview-rule-dialect-retired
Reviewed-by: session_01XvhGmGAP79ZB8swnkapxPC

VERDICT: PASS

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

Projects

None yet

2 participants