Skip to content

Commit 0638322

Browse files
feat(types): the six plugin-designer node types get zod arms; variables / parameters leave the TS face (objectui#10859 batch 7) (#11425)
Part of #10859 Clause-②: yes Batch 7 of objectui#10859, claim `5943202824` (seat `domain:ui#2`, session `session_01JG2jy8a9su7ia4Hx7zxv42`): the six `@object-ui/plugin-designer` node types (`page-designer`, `data-model-designer`, `process-designer`, `report-designer`, `object-manager`, `field-designer`) get zod arms in a new `packages/types/src/zod/designer.zod.ts`, after the two zero-read members ruling `5861220023` names are settled. `REFUSED_AT_TYPE` falls 72 → 66. ## The settlement first: the premise, measured before any edit The claim retires `ProcessDesignerSchema.variables` and `ReportDesignerSchema.parameters` from the TypeScript face only if, at base, neither has a reader in any package's non-test source AND neither has a producer beyond its own declaration docblock. Both halves held for both members (base `9547063da`, which is `138ad4554e` plus PR objectui#11407, a `packages/types` list change that touches none of these files): | half | instrument | `variables` | `parameters` | control (a member that IS read) | |:--|:--|:--|:--|:--| | reader, component | `git grep -n -w` over `packages/plugin-designer/` | 0 hits | 0 hits | `showMinimap` in `ProcessDesigner.tsx`: 2; `showPropertyPanel` in `ReportDesigner.tsx`: 4 | | reader, any package | every TS consumer of the two types (`git grep` for either type name) | only `designer.ts`, the `index.ts` re-export and one `types` test (its fixtures author neither) | same | the same grep finds the `content-channel-remeasure-9256` fixtures | | reader, by node type | every tracked file naming `process-designer` / `report-designer` | six files: the declaration, the registration, `known-schema-types.ts`, one changeset (prose), two tests; none reads the key | same | — | | reader, at runtime | real `SchemaRenderer` + real registry (`import '../index'`), only the one key varied (temp probe, not committed) | markup identical with and without an authored `variables` array | identical with and without `parameters` | `processName` / `reportName` changed: markup differs | | producer | docs (`content/docs`, the plugin README), examples, schema-catalog, apps, skills, CHANGELOGs; and objectstack `fbcc05f` | none | none | objectstack: `object-gantt` hits 14 files | The generic readers of a `variables` key (the `page` renderer's `schema.variables`, `react-page`) read it off their own node types only. None of the six designer components spreads a rest prop onward (`grep` for a props/rest spread in the six files: exit 1), so a key a component does not destructure is dropped. **How they were retired.** Removed outright from the TypeScript face, not tombstoned: neither prong of the retire-vs-remove discriminator `complex.ts` states holds (no live replacement key, and nothing taught the key as working). Each removal leaves a block comment where the member stood saying why. The arms do not declare them either, so neither face declares them now. Consequence, stated in the changeset: the `BaseSchema` index signature still admits either key at any value on the TypeScript face, `safeValidateSchema` passes it through as it does any undeclared key, and the strict authoring face refuses it by name (pinned). ## What changed - **`packages/types/src/zod/designer.zod.ts` (new).** The six node arms, each `BaseSchema.extend` with its `type` literal and the members of its TypeScript declaration, plus the record mirrors those members hold: `DesignerPositionSchema`, `DesignerCanvasConfigSchema`, `DesignerComponentSchema` (recursive through `children`, built as `TreeNodeSchema` is: a `z.lazy` annotated with the declaration as both type arguments), `DesignerPaletteItemSchema`, `DesignerPaletteCategorySchema`, `DataModelFieldSchema`, `DataModelEntitySchema`, `DataModelRelationshipSchema`, `BPMNNodeSchema`, `BPMNEdgeSchema`, `ReportDesignerElementSchema`, `ReportDesignerSectionSchema`, `ObjectDefinitionRelationshipSchema`, `ObjectDefinitionSchema`, `DesignerFieldOptionSchema`, `DesignerValidationRuleSchema`, `DesignerFieldDefinitionSchema`. The field designer's `type` is `z.enum(DESIGNER_FIELD_TYPES)`, read from `designer.ts` (the `complex.zod.ts` precedent imports `DASHBOARD_WIDGET_TYPES` the same way). Each arm refuses `body` / `children` by name through `neitherContentChannelGuidance` (objectui#9256 family D; the TypeScript twins already carry `?: never`). No `.default()`. `DesignerUnionSchema` is the category union (named so because `DesignerComponentSchema` is the canvas component's mirror; the `ReportUnionSchema` precedent). - **`index.zod.ts`.** The barrel exports, and `DesignerUnionSchema` in `AnyComponentSchema`'s arm list. - **`designer.ts`.** The two removals, and the six `children` docblocks no longer say "This declaration has no zod mirror, so this face is the only gate" (the sentence this change made false). - **Four other zero-read node members, NOT mirrored, NOT retired.** Measured the same way (props declared on the component, never used; the runtime probe drew identical markup with and without each, with a lit control per type): `DataModelDesignerSchema.autoLayout`, `ProcessDesignerSchema.version` and `.lanes` (destructured into an unused `_lanes`), `ReportDesignerSchema.previewMode`. Per the claim they stay on the TypeScript face, the arms omit them, and they are recorded for the seat (below). Every other node member is mirrored, and each has a reader in its component (destructured and used; counts in the report). - **Census (`zod-mirror-parity.test.ts`).** 22 `MIRRORS` / `Declared` pairs (`EXPECTED_MIRROR_PAIRS` 170 → 192); `EXCLUSIONS` rows for `DesignerComponentSchema` (recursive) and `DesignerUnionSchema` (a union); `DesignerComponentSchema` joins `RECURSION_BREAKING_MIRRORS`; three `UnmirroredDeclared` entries (four keys) for the zero-read members, each worded as unmirrored BY DECISION for the seat to rule, not mirroring debt; the header figures and the history sentence each pinned figure keeps. The type-level ledgers (`KnownDrift`, `WiderThanDeclared`, `MirroredUndeclared`) needed no row: every mirrored member's type equals its declaration's. - **Ratchet (`registered-types-validate-ratchet-10859.test.ts`).** `REFUSED_AT_TYPE` 72 → 66 and one row naming the six keys. Only the pin's own docblock, the constant and the new row are touched. - **New pin `designer-zod-arms-10859.test.ts`.** Type-level: key sets and member types equal per pair, the four omissions named per arm, `DesignerComponentSchema` equal to `DesignerComponent` both ways, the settlement on both faces, an instrument-fires row. Runtime: a fully populated document of each type accepted on both faces; required-members-only accepted; `{ type }` alone claimed at `type` with the refusal at the required members; nested judgment (a bad BPMN node type, a field type outside `DESIGNER_FIELD_TYPES`, a bad `zIndex` two levels down the canvas tree); content channels refused by name; `variables` / `parameters` refused by the strict face as unrecognized keys. - **`content-channel-remeasure-9256.test.ts`.** One dated note after the bullet that says the six have no zod mirror (a record of objectui#9256's tree, left as written), and the two inline comments that repeated it. - **`packages/types/src/zod/README.md`.** A Designer Components group. - **`.changeset/10859-designer-zod-arms.md`.** `@object-ui/types` minor, BREAKING (authoring) banner and migration text for the two retired members, `Clause-②: yes`. ## Gates At head `13f74331` (a merge of `main` `cf38a6ce` into `442799d7`; the merge touched no file under `packages/types`, `packages/cli` or `packages/plugin-designer`: `git diff 442799d 13f7433` over those three is empty). Exit codes captured to a file before reading. Node v22.22.2 from `/opt/nvm` (the container's v22.22.0 is refused by the engine-strict install). Heavy runs through the shared verify lock. | check | result | |:--|:--| | `pnpm exec vitest run packages/types/ packages/cli/` @ `13f74331` | exit 0 · Test Files 347 passed (347) · Tests 8984 passed (8984) | | `pnpm --filter @object-ui/types build && … type-check` (three legs) @ `442799d7` | exit 0 · "dist completeness: 1 package(s) complete (144 emitted files verified)"; the new pin is in `tsconfig.test.json --listFilesOnly` | | `pnpm exec vitest run packages/plugin-designer/` @ `442799d7` | exit 0 · 33 files, 226 tests | | plugin-designer `type-check` (after `--filter '@object-ui/plugin-designer^...' build`), cli `type-check` | exit 0, exit 0 | | sweep: 190 tracked test files outside `packages/types` / `packages/cli` that name `AnyComponentSchema`, `StrictAnyComponentSchema`, `safeValidateSchema`, `validateSchema`, `KNOWN_SCHEMA_TYPES`, `ComponentPropsMap`, the six schema names, or a designer key / `plugin-designer` (three chunks) | exit 0 each · 62 / 60 / 68 files, 1351 / 884 / 1406 tests | | `check:handler-key-reads` | exit 0 · "OK 146 arm(s), 212 registration(s) (144 keyed onto an arm), 56 reachable handler read(s), 56 judged" (140 / 138 at base) | | `check:spec-symbols`, `check:doc-types`, `check:new-line-citations` (0 new), `check:control-bytes`, `check-changeset-presence`, `changeset:check`, `check-changeset-overwrite` | exit 0 each | | `check:component-surface-parity` (report-only) | exit 0 · report byte-identical to base apart from blank lines | | also exit 0 | `check:test-path-roots`, `type-check:coverage` (43/43), `check:unreferenced-sources`, `check:esm-specifiers`, `check:pending-changeset-literals`, `census:changeset-polarity`, `check:registry-bare-names`, `check:prompt-keys`, `check:element-data-source-declaration`, `check:designer-field-key-parity`, `check:installed-pin-claims`, `check:side-effects-array`, `check:self-import`, `check:phantom-deps`, `check:doc-example-readers`, `check:doc-fences`, `docs:check-links`, `markdown-test-inputs --audit`, `check:action-forward-parity`, `check:i18n-designer-parity` | | `check:changeset-claims` (report-only) | 22 pending changesets name a touched file; read, none states a fact this change falsifies | | `check-governed-queue-guard --test` (9 paths) | NOT GOVERNED | | ESLint, narrowed | `--no-inline-config --format json` over the 7 changed `.ts` files: 0 errors, 12 warnings, equal file for file to the base blobs (`no-explicit-any`, 10 in `designer.ts`, 2 in the census); the two new files 0. Population from each package's `lint` script; `eslint.config.js` sets no type-aware parser project, so no untouched file's verdict can move. Full `pnpm lint` is CI's | | NOT MEASURED | `check:readme-exports`, `check:doc-snippets`, `check:doc-examples`: they read package-root READMEs and `content/docs`, and no file they read changed | ## Red first, ablation, reverse validation (one-shot, not committed) - **Base reading.** `safeValidateSchema({ type: KEY })` refused each of the six with one `invalid_union` at `type`; `timeline` accepted (the lit control). - **Runtime ablation.** `DesignerUnionSchema` removed from `AnyComponentSchema`'s arm list through objectstack's `scripts/ablation-replace.mjs` (WRAP, plus the driver's own EXIT/INT/TERM trap): anchor 1 → 0, blob `a5093c84` → `eacad442`. The pin and the ratchet: Tests 38 failed | 24 passed (62); the ratchet read "refuses 72 registered bare key(s) at `type`; the pin is 66". Red: every pin row that reaches an arm through the root union, and the two ratchet rows. Green: the rows that read the arms directly (`DesignerUnionSchema` literals, the four omissions) and the ratchet's controls. Restored: blob == HEAD, `git diff HEAD` empty. Vitest resolves `@object-ui/types/zod` to source, so no build sat between mutation and reading. - **Type-level reverse validation.** `BPMNEdgeSchema.isDefault` retyped `z.string()`: `tsc -p tsconfig.test.json` exit 2, TS2344 on the pin's key-type rows plus the census's `LedgerMismatch` / `WiderLedgerMismatch`. Restored, blob == HEAD. - **The ledger the four keys belong in.** The three entries moved from `UnmirroredDeclared` into `KnownDrift`: `tsc` exit 2 (`LedgerMismatch`, and the unmirrored reconciliation naming the three pairs). `KnownDrift` holds keys a mirror declares and refuses; an absent key is `UnmirroredDeclared`'s. A first attempt at this leg was a no-op (its anchor sat inside its own replacement, so the tool refused and restored); it was re-run with a different anchor and the reading above is the second run. ## Serial - Draft PR objectui#11069 also edits the ratchet file. `git merge-tree --write-tree` of this head against its head `a11f7334`: one conflict, `packages/cli/src/commands/check.ts`, which this branch does not touch and which conflicts the same way between the base and that PR alone; the ratchet file auto-merges. Its hunks are untouched. - Against `main` `cf38a6ce`: merged (no conflict). ## Acceptance notes - **For the seat: nested record members with no reader.** The ruling's zero-read rule is applied to the six declarations' own members, as the claim scopes it. The record types under them are mirrored member for member as declared, as batch 1 mirrored `AIRecommendationItem.metadata` and `NLQueryResult.parsedQuery`. A source read (`.member` reads in the component that renders each record, plus `MetadataService` / `MetadataObjectsPage` / `MetadataFieldsPage` for the two shared ones; no runtime probe) finds no reader for: `DesignerCanvasConfig.backgroundColor`; `DesignerComponent.children` / `parentId` / `locked` / `visible` / `zIndex`; `DesignerPaletteCategory.icon`; `DesignerPaletteItem.icon` / `preview`; `DataModelField.unique` / `defaultValue` / `description`; `DataModelRelationship.sourceField` / `targetField` (both REQUIRED on the TypeScript face) / `onDelete` / `onUpdate`; `BPMNNode.properties` / `assignee` / `dueDate` / `script` / `serviceEndpoint`; `BPMNEdge.condition` / `isDefault`; `ReportDesignerElement.dataBinding` / `format`; `ReportDesignerSection.groupField` / `pageBreakBefore`; `ObjectDefinition.relationships` (so all of `ObjectDefinitionRelationship`); `DesignerFieldDefinition.validationRules` (so all of `DesignerValidationRule`). The question is in the report's `open_questions`. - `data-model-designer`'s registration publishes `autoLayout` as an input while the component never reads it; `check:component-surface-parity` already reports it (base and head alike). - No authored document of any of the six node types exists in objectui or objectstack today (only test fixtures), so this change moves no shipped document's verdict. ## Deviations - The claim says the four other zero-read members are recorded as a pinned `KnownDrift`. Measured, the census cannot hold them there (the ledger leg above); they are in `UnmirroredDeclared`, the census's ledger for a declared key the mirror does not declare, worded as unmirrored by decision. - The harness attribution reminder asks for a model-named commit trailer and a different PR footer. Per objectui's AGENTS.md and the role file, the commits carry the model-free pair and this body ends with the session-URL footer. --- _Generated by [Claude Code](https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 47e956d commit 0638322

9 files changed

Lines changed: 1234 additions & 29 deletions
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@object-ui/types': minor
3+
---
4+
5+
feat(types): the six `@object-ui/plugin-designer` node types validate; `ProcessDesignerSchema.variables` and `ReportDesignerSchema.parameters` leave the TypeScript face (objectui#10859, batch 7)
6+
7+
**BREAKING (authoring):** `ProcessDesignerSchema` no longer declares `variables`, and `ReportDesignerSchema` no longer declares `parameters`. Nothing reads either key: `ProcessDesigner` and `ReportDesigner` take neither as a prop, and a node that carried one drew exactly what it drew without it. Nothing in this repository authored either one.
8+
9+
Migration: delete `variables` from your `process-designer` nodes and `parameters` from your `report-designer` nodes. Neither reaches the screen, so removing it changes nothing a user sees. TypeScript code that read `schema.variables` or `schema.parameters` off one of these types now reads the `BaseSchema` index signature (`any`) instead of the old array type. The strict authoring face refuses either key as an unrecognized key on these nodes; `safeValidateSchema` passes it through, as it does any undeclared key.
10+
11+
**Clause-②: yes** — `@object-ui/types/zod` accepts six registered node types it refused at `type` before (a widening), and the TypeScript face narrows by two members. Released as `minor` under the objectui narrowing rule, with this banner.
12+
13+
**What changed, in observable terms.**
14+
15+
- `@object-ui/types/zod` exports `PageDesignerSchema`, `DataModelDesignerSchema`, `ProcessDesignerSchema`, `ReportDesignerSchema`, `ObjectManagerSchema` and `FieldDesignerSchema`, each a mirror of its TypeScript declaration in `designer.ts`, and the mirrors of the record types those nodes hold (`DesignerComponentSchema`, `DesignerCanvasConfigSchema`, `BPMNNodeSchema`, `ReportDesignerSectionSchema`, `ObjectDefinitionSchema`, `DesignerFieldDefinitionSchema` and the rest). They reach `AnyComponentSchema` through `DesignerUnionSchema`, so `safeValidateSchema`, `validateSchema`, the strict authoring face and `objectui validate` now judge a designer node by its own members instead of refusing it with `invalid_union` at `type`.
16+
- Each arm requires what its declaration requires (for example `processName`, `nodes` and `edges` on `process-designer`), and refuses `body` and `children` by name, because none of the six components reads a content channel (objectui#9256).
17+
- Four declared members that no designer reads are NOT on the arms: `autoLayout` on `data-model-designer`, `lanes` and `version` on `process-designer`, and `previewMode` on `report-designer`. They stay declared on the TypeScript face. The strict face refuses them as unrecognized keys, and the tolerant face passes them through unjudged.

‎packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,11 @@ import { validate } from '../commands/validate.js';
6767
* The head's refused count (objectui#10859 batch 1: 79 on `main` before it,
6868
* minus the three `@object-ui/plugin-ai` arms; batch 2: minus `pivot`,
6969
* `object-metric` and `object-master-detail-form`; batch 3: minus
70-
* `object-timeline`, armed from its `@objectstack/spec` 17.5.0 row). LOWER it
71-
* when a batch arms more keys; never raise it.
70+
* `object-timeline`, armed from its `@objectstack/spec` 17.5.0 row; batch 7:
71+
* minus the six `@object-ui/plugin-designer` keys). LOWER it when a batch arms
72+
* more keys; never raise it.
7273
*/
73-
const REFUSED_AT_TYPE = 72;
74+
const REFUSED_AT_TYPE = 66;
7475

7576
/**
7677
* The head's refused count over the NAMESPACED keys (objectui#10872 batch 1:
@@ -168,6 +169,16 @@ describe('registered component types refused at `type` — a ratchet (objectui#1
168169
expect(BARE_KEYS).toContain('object-timeline');
169170
expect(refusedAtType('object-timeline')).toBe(false);
170171
});
172+
173+
it('counts the six designer keys batch 7 armed (objectui#10859 batch 7)', () => {
174+
for (const key of [
175+
'page-designer', 'data-model-designer', 'process-designer',
176+
'report-designer', 'object-manager', 'field-designer',
177+
]) {
178+
expect(BARE_KEYS, key).toContain(key);
179+
expect(refusedAtType(key), key).toBe(false);
180+
}
181+
});
171182
});
172183

173184
describe('registered NAMESPACED component types refused at `type` — a ratchet (objectui#10872)', () => {

‎packages/types/src/__tests__/content-channel-remeasure-9256.test.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@
4545
* `process-designer`, `report-designer`, `object-manager`,
4646
* `field-designer`) — the TypeScript face only: none of them has a zod
4747
* mirror, so that face is the only gate, as it was for `nl-query`.
48+
* (⚠️ AMENDED 2026-10-02 (objectui#10859 batch 7) — a reading of
49+
* objectui#9256's tree. All six have a zod arm now in
50+
* `zod/designer.zod.ts`, which refuses both channels by name with the same
51+
* guidance; its pin is `designer-zod-arms-10859.test.ts`.)
4852
*
4953
* `body` was already refused on every one of these faces by `BaseSchema`
5054
* (objectui#6771); it is restated because that refusal names `children` as the
@@ -249,9 +253,9 @@ describe('objectui#9256 re-measure — the TypeScript face refuses both channels
249253
const listViewBody: ListViewSchema = { ...listView, body: CONTENT };
250254
// @ts-expect-error objectui#9256 — `list-view` reads neither channel (this face is `z.input` of the mirror)
251255
const listViewChildren: ListViewSchema = { ...listView, children: CONTENT };
252-
// @ts-expect-error objectui#9256 — `page-designer` reads neither channel (no zod mirror; this face is the only gate)
256+
// @ts-expect-error objectui#9256 — `page-designer` reads neither channel
253257
const pageDesignerBody: PageDesignerSchema = { ...pageDesigner, body: CONTENT };
254-
// @ts-expect-error objectui#9256 — `page-designer` reads neither channel (no zod mirror; this face is the only gate)
258+
// @ts-expect-error objectui#9256 — `page-designer` reads neither channel
255259
const pageDesignerChildren: PageDesignerSchema = { ...pageDesigner, children: CONTENT };
256260
// @ts-expect-error objectui#9256 — `data-model-designer` reads neither channel
257261
const dataModelDesignerBody: DataModelDesignerSchema = { ...dataModelDesigner, body: CONTENT };

0 commit comments

Comments
 (0)