Skip to content

fix(types): the spec-shape rule condition and BulkActionDef.visible read the named view expression slots by reference (objectui#10946) - #10977

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-10946-expression-wire-types
Sep 28, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-10946-expression-wire-types

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #10946

Clause-②: yes — widens two published types to the spec's expression-input wire (triage execution note 3).

What changed

SpecConditionalFormattingRule.condition and BulkActionDef.visible in @object-ui/types now read the named view's own expression slots BY REFERENCE, from the authoring (z.input) face of ObjectListViewSchema. That is the derivation ObjectViewSchema.listViews already uses (objectui#7928). There is no hand-written copy of the spec wire.

Member Before After
SpecConditionalFormattingRule.condition string string, or the slot type of ObjectListViewSchema.conditionalFormatting[].condition
BulkActionDef.visible string | { dialect?: string; source: string }, spelled inline ExpressionWire (the same union, now by name), or the slot type of ObjectListViewSchema.bulkActionDefs[].visible
  • Zod mirror. The mirror moves with the type. Two mirrors carried condition: z.string(): the list view's rule union and KanbanConditionalFormattingRuleSchema. Both now read one module-private SpecRuleConditionSchema. Its first arm is z.string(), so a string condition parses exactly as before: it is not canonicalized into an envelope, and '' is still accepted. The spec slot alone would do both of those things to a string. Its second arm is the spec's own slot schema, taken by reference; a pin asserts reference identity. The zod-mirror-parity ledger rows name keys, not value types, and no key moved, so the ledger is untouched. BulkActionDef has no standalone zod mirror. The list view's bulkActionDefs is already the spec's schema by reference.
  • Relays. ObjectView route 2 drops the two assertions PR fix(plugin-view): the registered object-view reads a named view's grid members (objectui#10885) #10937 added: conditionalFormatting … as ObjectGridSchema['conditionalFormatting'] and bulkActionDefs … as ObjectGridSchema['bulkActionDefs']. Both relays now type-check as written.
  • No runtime behaviour change. No evaluator, renderer or relay changed. A type assertion compiles to nothing.
  • Changeset. @object-ui/types: minor. It is breaking for TypeScript readers of these members, and the changeset says so.

Assertions the PM listed that stay, and why (measured)

  • plugin-list ListView: kept. The four casts are schema.conditionalFormatting as unknown[] | undefined and the three (schema as any).bulkActionDefs reads. With all four removed, plugin-list tsc --noEmit exits 0 even against BASE-shaped types (ablation leg A[4] below). Their targets are readonly unknown[] parameters or an untyped node literal, so this gap never forced them. They are outside this card and untouched (see Acceptance notes).
  • plugin-grid resolveBulkActions: kept. action.visible as BulkActionDef['visible'] stays because action is NamedActionDef, an unknown pass-through bag. Removing the cast is red WITH the widened types (leg C: TS2322, visible?: {}), so it exists for a different reason.

The two spec lines differ on these slots

condition / visible slot ast-only envelope
installed @objectstack/spec 17.4.0 (the pin) ExpressionInputSchema, where source is optional admitted
spec main at dbddf02c (what the Spec Main Shape Gate compiles against) EvaluatedExpressionInputSchema, where a non-blank source is required refused
  • Because the members index the slot rather than naming ExpressionInput, they follow whichever line is installed. They narrow with the next spec bump, with no local edit.
  • Local spec-main probe, not the CI gate. I compiled the pin file plus the ObjectView-shaped relays against spec main SOURCE, with @objectstack/spec mapped to git archive origin/main packages/spec/src. The installed 17.4.0 was absent from the program. Result: 0 errors in objectui files or pins. Of the 8 errors, 5 are React namespace errors from the probe's empty types and 3 are in spec source (missing pg-connection-string and node types). A @ts-expect-error on an ast-only value was consumed, which confirms spec main refuses that value on the slot.
  • Runtime answer for the ast-only envelope the installed line admits. It is an evaluation fault. The formatting rule does not match, the bulk def qualifies no record, and the fault is warned. Both paths are pinned.

Verification at 6f5bfdcb0

  • type-check. turbo run type-check --continue, locally, over types, core, plugin-grid, plugin-view, plugin-list, plugin-kanban, app-shell and plugin-report. All 8 scripts ran, 0 error TS, VERDICT command-exit 0.
  • vitest. The whole packages/types/ suite plus the 37 other test files that name conditionalFormatting, bulkActionDefs, BulkActionDef or the rule types: Test Files 306 passed (306), Tests 6765 passed (6765).
  • New pins. spec-expression-wire-slots-10946.test.ts (types, 14 tests) and specExpressionWire-10946.test.tsx (plugin-grid, 5 tests) are green. Their type halves are compiled by each package's tsconfig.test.json.
  • Gates. Each of these exits 0: check-control-bytes, check-new-cross-file-line-citations (0 new), check-spec-symbol-derivation, check-changeset-presence, check-changeset-fixed, check-changeset-no-major, check-changeset-claims, check-changeset-overwrite, check-pending-changeset-literals, check-type-check-coverage, check-test-path-roots, check-vi-mock-override-shape, check-phantom-dependencies, check-package-self-import, check-unreferenced-sources, check-node-esm-load --specifiers-only. check-governed-queue-guard --test: NOT GOVERNED.
  • Lint, narrowed and declared. I ran eslint --no-inline-config --format json over the 5 changed TS files. It reported 5 files, 0 errors and 95 warnings, all on untouched lines (none in the added lines or the two new files). The population is the root eslint.config.js **/*.{ts,tsx} block, which includes all 5 files. That config enables no type-aware linting (no parserOptions.project or projectService), and no rule in eslint-rules/ reads the filesystem. So this diff cannot move a verdict on an untouched file. The repo-wide pnpm lint belongs to CI.

Reverse verification (ablations; each leg mutated with ablation-replace.mjs, anchor hit proven on disk, @object-ui/types rebuilt, dist marker counted)

Leg A reverts all three derivations to the BASE shapes: the rule alias to { condition: string }, the bulk alias to { visible?: never }, and SpecRuleConditionSchema to z.string(). It also removes the four ListView casts.

  1. plugin-view tsc --noEmit: exit 2. TS2322 on the route-2 grid schema: conditionalFormatting, then condition, is not assignable to string. TS names only the first incompatible member.
  2. types tsc -p tsconfig.test.json: exit 2, with 7 errors, all in the new pin: the five relays, the envelope rule, and the ast excess property.
  3. plugin-grid tsc -p tsconfig.test.json: exit 2, with 2 errors in the new grid pin.
  4. plugin-list tsc --noEmit with the ListView casts removed: exit 0. This is the control showing those casts are not this gap's.
  5. vitest on the types pin: 7 failed and 7 passed. The failures are envelope acceptance on both mirrors, the envelope-refusal cases on both mirrors (their valid-envelope control fails), the reference identity on both mirrors, and the reference control. That control throws on a z.string() that has no .options. The string and empty-string controls stay green.

Leg B reverts only the visible arm. plugin-view tsc --noEmit exits 2: bulkActionDefs is incompatible, then visible, then source.

Leg C keeps the widened types and removes the resolveBulkActions cast. plugin-grid tsc --noEmit exits 2 (TS2322).

Restore. Every leg was restored with git checkout HEAD -- PATH, and each restore was checked: blob equal to the HEAD blob, and git diff HEAD empty. @object-ui/types was rebuilt, all 3 mutation markers counted 0 in dist, and git status --porcelain came back empty.

The plugin-grid runtime cases characterize pre-existing behaviour, so they stay green under a type-only ablation by construction. Their type half is leg A[3].

Declared surface extension

packages/plugin-grid/src/__tests__/specExpressionWire-10946.test.tsx is a new file outside the claim's file surface. The card's "evaluates" pin needs the real grid, and @object-ui/types has no evaluator to import. It is a new file only; no existing file outside the claim was edited.

Not measured locally (CI owns)

  • The Spec Main Shape Gate. It is covered only by the source probe above.
  • Full pnpm test (8 shards).
  • Full pnpm lint.
  • pnpm type-check for the packages not listed above.

Acceptance notes

  • ListView casts (polish, not filed; carrier: none). The four casts are redundant, as leg A[4] measures, but for a reason outside this card. They were left untouched.
  • @object-ui/core FieldRulePredicate (observation, not filed). It declares source as required, which is narrower than the installed slot. Every call site that hands it these members already casts, and it converges with spec main's narrowing.
  • Model-named trailer on 17393ae3c. That commit carries a model-named Co-Authored-By trailer. The dispatch contract asks for model-free trailers. History was not rewritten, because the branch is never force-pushed. 6f5bfdcb0 is model-free.

Generated by Claude Code

…e` read the named view's expression slots by reference (objectui#10946)

`SpecConditionalFormattingRule.condition` was `string` alone and
`BulkActionDef.visible` had no arm for the protocol's envelope (optional
`source`, `ast`, `meta`), both narrower than `ObjectListViewSchema` declares and
than `ObjectGrid`'s evaluator reads, so relaying a named view into a grid needed
a type assertion.

- `condition` is `string | <spec slot>`, the slot indexed by reference from
  `z.input<typeof ObjectListViewSchema>`.
- `visible` is `ExpressionWire | <spec slot>`, same derivation.
- The zod twin (`SpecRuleConditionSchema`) keeps `z.string()` first, so a string
  parses exactly as before, and reads the spec's slot schema by reference for the
  envelope; both rule unions (list view, kanban) use it.
- `ObjectView` drops the two relay assertions the gap forced.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UwY3ahMixEFkfTUxMVkYm
… changeset (objectui#10946)

- `spec-expression-wire-slots-10946.test.ts` (types): relays of the named
  view's own rule and bulk-def values with no assertion (tsc is the pin), the
  zod twin's input equal to the TS member, envelope acceptance on both rule
  unions with string / empty-string controls, and the spec arm's reference
  identity with a control that can fail.
- `specExpressionWire-10946.test.tsx` (plugin-grid): the real grid paints an
  envelope-matched row (string control), the bulk fold reads `source` beside
  `ast`, and an `ast`-only envelope is a warned fault on both paths.
- Changeset: `@object-ui/types` minor, breaking for readers spelled out.

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

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

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

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

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

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

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

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

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

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

.changeset/6726-find-envelope-records-arms.md

  • names plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — edited by this change

    | module | what it does | | --- | --- | | components/src/hooks/related-count-store.ts | related-list tab badge count | | components/src/renderers/basic/data-list.tsx | element:repeater rows | | components/src/renderers/basic/elements.tsx | element:number client-side aggregate | | components/src/renderers/basic/record-picker.tsx | element:record_picker options | | plugin-detail/src/renderers/record-activity.tsx | record:activity self-fetch | | plugin-detail/src/renderers/record-history.tsx | record:history self-fetch | | plugin-view/src/ObjectView.tsx | non-grid (kanban / calendar / gallery / timeline) fetch |

.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/7070-no-invented-gantt-date-fields.md

  • names plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — edited by this change

    • app-shell/src/views/ObjectView.tsx — the console object page. The inline branch becomes ganttViewOptions, the sibling of calendarViewOptions and timelineViewOptions: the declared block spread whole, title floored at 'name', no date field invented. - plugin-list/src/ListView.tsx — the render branch AND the capability gate. - plugin-view/src/ObjectView.tsx — generateViewSchema, the authored object-view element route, which bypasses ListView entirely.

.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/7499-gantt-non-axis-floors-omitted.md

  • names plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — edited by this change

    • plugin-list/src/ListView.tsx — the object-gantt render branch. - plugin-view/src/ObjectView.tsx — generateViewSchema, the authored object-view element route, which bypasses ListView entirely.

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

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

  • names packages/plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — 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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

.changeset/9092-inline-locale-declared-face.md

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

.changeset/9550-object-tree-root-barrel.md

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

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

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

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

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

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

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

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

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

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

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

  • names packages/plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — edited by this change

    What was measured. Every reading was taken on the object-view node renderer (packages/plugin-view/src/ObjectView.tsx, registered by plugin-view/src/index.tsx) with schema.objectName / schema.layout as the positive controls of the same schema.KEY query, so each zero is a reading; the repo-wide census of viewTabBar finds the key in no source file outside @object-ui/types (two doc tables listed it as authorable and are corrected here). The spec side was read through the installed pin (@objectstack/spec@17.2.0, ui entry, 117 exported object schemas walked; control keys objectName / columns / navigation / listViews hit): the three spec-modelled keys are optional slots on ListViewSchema and ObjectListViewSchema; the six local keys have no spec slot anywhere.

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 328abeb55 (merge-base with origin/main): 5 file(s) changed outside .changeset/, read against 1690 pending declaration(s) that publish a body (2290 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3103.3 KB 3104.5 KB
Main entry chunk (gzip) 149.5 KB 350 KB
Entry file index-BpoQpOFZ.js —
Status PASS —

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


📦 Bundle Size Report

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

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 6f5bfdcb01fd7b96679a4cd85bb009470180c053
Local-runs: none

Inputs read: card #10946 (body, triage 5868108332, claim 5868862334, os-dev-report 5869691241, addendum 5869745369), PR #10977 (body, file list, net diff origin/main... the head, fetched into a review ref of this session's own), and the 43 check-runs on the head. The installed spec was read from the published @objectstack/spec 17.4.0 tarball's declarations (node_modules is absent in this checkout); spec main was read from the sibling packages/spec/src at df3ba164a5. Nothing was built, run or re-run.

Check-runs on the head at write time: 43, every one completed with success or skipped. Named gates: Type Check success, Lint success, Spec Main Shape Gate success, Governed Surface Queue Guard success (the diff touches no governed surface), Changeset Declaration, Changeset Claim Re-read, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report all success, Build & E2E success, Test (shard 1..8/8) success. The PR is a draft with no auto-merge armed.

① Derived judgments

  1. SpecConditionalFormattingRule.condition is string or the protocol slot, indexed by reference — RIGHT. The member is string unioned with the condition of one element of z.input of ObjectListViewSchema's conditionalFormatting. On the installed 17.4.0 that slot is ExpressionInputSchema, whose input is string or { dialect: 'cel' | 'cron' | 'template'; source?: string; ast?: unknown; meta? } (read from the published tarball). string is already the slot's first arm, so the union IS the slot's input: the member admits exactly the protocol slot, no wider, no narrower. There is no hand copy of the wire.
  2. The widening reaches the right readers and only readers that honour it — RIGHT. ConditionalFormattingRule and KanbanConditionalFormattingRule are unions carrying the spec rule, so ObjectGridSchema, ObjectKanbanSchema, ListViewSchema and NamedListView conditionalFormatting all widen with it. Both runtimes read one evaluator: plugin-grid and plugin-kanban (KanbanImpl.tsx) call @object-ui/core's resolveConditionalFormatting, whose ruleToPredicate returns rule.condition untouched into FieldRulePredicate, which already reads the envelope. No new accept set lands on a reader that treats the value as a string only.
  3. BulkActionDef.visible is ExpressionWire or the protocol slot — RIGHT, with one pre-existing residual noted. The second arm indexes visible of one element of z.input of the spec's bulkActionDefs; on 17.4.0 that is ExpressionInputSchema (BulkActionDefSchema.visible, read from the tarball), so the arm the card asked for (ast admitted, source optional) is exactly the protocol's. The first arm is the member's previous inline union, now spelled by its one name; it is wider than the protocol on dialect (unconstrained string against the enum). That accept set is pre-existing, is the runtime's measured accept set per the ExpressionWire docblock, and dropping it would be a narrowing (BREAKING) outside this card. Keeping it is right; it is not introduced here.
  4. Zod mirror SpecRuleConditionSchema — RIGHT. z.union([z.string(), the spec slot by reference]) on the list-view rule union and on KanbanConditionalFormattingRuleSchema. The accept-set change is: the { dialect, source } envelope is now accepted on list-view and object-kanban nodes and judged by the spec's own slot (reference identity is pinned, with a control that the string arm is not the slot). The z.string() arm first keeps the prior string behaviour byte-for-byte: not canonicalized, and '' still accepted. Residual, pre-existing: the mirror accepts '' where the protocol slot refuses it (min(1)); the card's own control pin mandates the string form unchanged, so a narrowing there is a separate (narrowing) decision, not this PR's. The zod-mirror-parity ledger names keys, not value types; no key moved, so leaving it untouched is right (checked against the ledger's conditionalFormatting rows). BulkActionDef has no standalone mirror; the list view's bulkActionDefs is already the spec schema by reference.
  5. ObjectView route-2 relays lose their two assertions — RIGHT. NamedViewConfig is one entry of ObjectViewSchema.listViews, itself z.input of ObjectListViewSchema, so source and target now derive from the same face and the relays type-check by construction (Type Check green on the head). A type assertion compiles to nothing, so runtime is unchanged, as the PR states.
  6. Assertions kept against the claim's list — RIGHT, measured, not asserted. The ListView.tsx casts (2540, 2565, 3306) target unknown[] or any, and resolveBulkActions.ts:143 casts out of an unknown bag (NamedActionDef); neither is forced by the two members this card widened. The dev's ablation legs A[4] and C say the same, and the seat's addendum accepted it. Leaving them is right; the redundancy of the ListView casts is polish, correctly carried as an acceptance note.
  7. Spec-main narrowing, handled soundly for the Spec Main Shape Gate — RIGHT. On spec main both slots are EvaluatedExpressionInputSchema (view.zod.ts conditionalFormatting[].condition; bulk-action.zod.ts:373 visible). Its envelope arm is EvaluatedExpressionSchema, which is ExpressionSchema with source required and non-blank, and it KEEPS ast and meta as optional members and dialect as the enum. Consequences checked: (a) both members narrow automatically at the next spec bump because they index the slot rather than naming ExpressionInput; (b) the two typed pins carrying ast beside source compile on both lines since ast stays declared; (c) the ast-only fixtures are typed through as unknown as on purpose, so they pin runtime behaviour only and compile on either line; (d) the zod pins ({ dialect: 'cel' }, { source } without dialect and 42 refused; '' refused by the slot alone; a bare string canonicalized by the slot alone) hold on both lines; (e) spec main's strictObject() returns a closed z.object(...).strict(), so the mirror's .shape.conditionalFormatting.unwrap().element.shape.condition read stays reachable. The gate's own conclusion on this head is success.
  8. Accept-set summary. Type face: two members widened to the protocol slot (plus one pre-existing arm kept). Validator face: the envelope accepted on two node types, judged by the spec. Runtime: no change. Nothing declared that the runtime does not deliver; the ast-only envelope the installed slot admits is pinned as an evaluation fault (no match, no eligible record, warned), which is the runtime's real answer.

② Semver level

  • Changeset .changeset/10946-expression-wire-slots-by-reference.md: '@object-ui/types': minor. What the diff publishes: two widened members in @object-ui/types (TS face and zod mirror accept set) and an assertion removal in @object-ui/plugin-view whose compiled output is unchanged. The fixed group bumps as one family, so one declaration covers both, and the body names plugin-view's change.
  • objectui AGENTS.md §9 version alignment: no major in a changeset; objectui's own breaking changes take minor with the breaking semantics written in the body. The body states "Breaking for TypeScript readers of these members, released as minor" and gives the typeof narrowing fix. That is the rule's form exactly. Changeset Bump Policy and Changeset Declaration are green.
  • Clause-②: line on the PR body: Clause-②: yes — widens two published types to the spec's expression-input wire. yes with no arm is a legal declaration; a widening takes at least minor; the changeset is minor. The line matches the diff. Level: RIGHT.

③ Boundary flags

  • Deviation 1 — new test file packages/plugin-grid/src/__tests__/specExpressionWire-10946.test.tsx outside the claim's file surface. Answered by the seat's addendum 5869745369 (option A; surface amended). Judged right: the card's acceptance item says an envelope condition "evaluates" through the grid path, @object-ui/types has no evaluator to import, the file is new and collides with no holder, and no existing out-of-surface file was edited.
  • Deviation 2 — a model-named trailer on the first branch commit (17393ae3c). AGENTS.md exempts a harness-written co-author trailer (reporting only; landed history is not rewritten), objectui forbids force-pushing a shared branch, and the head commit 6f5bfdcb0 is model-free. Reported, not rewritten: handled per the exemption. No model identifier appears in the PR title, body, changeset or code. Nothing to escalate.
  • open_questions[0] (accept the surface extension) — answered A by the addendum; closed.
  • out_of_scope_findings (a) — the four ListView casts are redundant for a reason outside this card; carried as an acceptance note, no card. Right under Prime Directive [WIP] Enhance every detail of the designer #10: polish, not a defect, contract violation or authoring trap.
  • out_of_scope_findings (b) — @object-ui/core FieldRulePredicate requires source, narrower than the installed persistence slot but equal to spec main's evaluated slot; every call site casts, and the next spec bump converges it. Noted, not filed: acceptable, since a card would be closed by the bump the spec has already taken.
  • Observation for the owning seat (not a dev flag, not a FAIL condition): the mirror's pre-existing acceptance of '' on condition (judgment 4) survives by the card's own design. Whether to file it as a (narrowing) card is the seat's call.
  • No flag is left unanswered; nothing needs escalating to the maintainer.

Implemented-by: claude/issue-10946-expression-wire-types
Reviewed-by: session_012UwY3ahMixEFkfTUxMVkYm

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