Repository navigation
Commit 0638322
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
File tree
- .changeset
- packages
- cli/src/__tests__
- types/src
- __tests__
- zod
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
Lines changed: 14 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
70 | | - | |
71 | | - | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
72 | 73 | | |
73 | | - | |
| 74 | + | |
74 | 75 | | |
75 | 76 | | |
76 | 77 | | |
| |||
168 | 169 | | |
169 | 170 | | |
170 | 171 | | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
171 | 182 | | |
172 | 183 | | |
173 | 184 | | |
| |||
Lines changed: 6 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
48 | 52 | | |
49 | 53 | | |
50 | 54 | | |
| |||
249 | 253 | | |
250 | 254 | | |
251 | 255 | | |
252 | | - | |
| 256 | + | |
253 | 257 | | |
254 | | - | |
| 258 | + | |
255 | 259 | | |
256 | 260 | | |
257 | 261 | | |
| |||
0 commit comments