Skip to content

Commit 00ccdf7

Browse files
feat(types): arm the seven registered node types that pass the criterion — page kinds, app-schema-renderer, object-pivot, embeddable-form, detail-section (objectui#11440) (#11493)
Part of #11440 Clause-②: yes The first of objectui#11440's two pull requests. It arms the seven registered node types that the seat ruling `5945530142` on objectui#10859 (as amended by `5945583855`) passes: the spec page kinds `record`, `home` and `utility`, and `app-schema-renderer`, `object-pivot`, `embeddable-form` and `detail-section`. `safeValidateSchema`, and so `objectui validate`, refused each one with `invalid_union` at `type`. `REFUSED_AT_TYPE` falls 8 to 1. The eighth key, `spec-report`, is the fold into `report`, which is the second pull request (see "What is left" below). Dispatched run: claim `5954845613` (`domain:ui` seat 1), session `https://claude.ai/code/session_01YLg8XqWGJ785fwQ5v4pH37`. Draft, left for the seat. ## What changed | key | arm | shape | |:--|:--|:--| | `record`, `home`, `utility` | `PageKindNodeSchema` (`layout.zod.ts`), its own arm of `AnyComponentSchema` | every member is `PageNodeSchema`'s own, by reference (the spec's `PageSchema` fields among them, through `SpecPageFields`); `type` is the spec's `PageTypeSchema` narrowed with `.extract` to the three kinds `@object-ui/components` registers on `PageRenderer` | | `app-schema-renderer` | `AppSchemaRendererNodeSchema` (`app.zod.ts`), its own arm | `basePath` and `mobileNavMode` (`'drawer'` or `'bottom_nav'`), from the registration's inputs; `children` and `body` refused by name | | `object-pivot` | `ObjectPivotBlockSchema` (`objectql.zod.ts`), in `ObjectQLPublicBlockComponentSchema` | a REQUIRED `properties` bag of the registration's inputs (the cross-tab members are the `pivot` mirror's own, by reference; `drillDown` refuses `mode`; the retired `dataProvider` is refused); flat props refused by name toward `properties.KEY`; `dataSource` binding and `responsiveStyles` on the node; `RECORD_SOURCE_REQUIRED` unless `dataSource.object` names the object | | `embeddable-form` | `EmbeddableFormBlockSchema` (`objectql.zod.ts`), same union | a REQUIRED bag of exactly the registration's inputs (`formId` required), built as above | | `detail-section` | `DetailSectionNodeSchema` (`views.zod.ts`), in `ViewComponentSchema` | the ten `DetailViewSectionSchema` members the registration publishes, `.pick`ed by reference, flat; `fields` required | Also: - `object-pivot`'s registration publishes `drillDown` as an input (`@object-ui/plugin-dashboard`). `ObjectPivotTable` reads it, and the `pivot` node's refusal of `drillDown` names this block as where a pivot drill is authored. - `@object-ui/types` exports `ObjectPivotBlockNode` and `EmbeddableFormBlockNode`. objectui#11468 (landed during this run) derives `ObjectQLPublicBlockNode` from the union's arms, so the two arms joined it. Its `ClosedBag` closed only an optional bag, so it closes a required one too now, and no `AuthoringNode` member carries an open bag. - Prose this change made false is amended: three docblocks (`ObjectPivotDrillDownConfig`, `DrillDownConfigSchema`, `ObjectPivotTable`'s `dataProvider` note), and pure-append dated notes on three pending changesets (`10685-drilldown-per-block.md`, `7352-drill-down-config-mirror.md`, `11468-bag-carrier-node-types.md`). - Changesets: `.changeset/11440-arm-passing-types.md` (`@object-ui/types` minor, each new authorable spelling listed, no BREAKING banner: a pure widening) and `.changeset/11440-object-pivot-drilldown-input.md` (`@object-ui/plugin-dashboard` minor). ## Contract notes For the at-tier reviewer. - **Widens.** `AnyComponentSchema` accepts seven `type` literals it refused at `type`, on both faces, at the root and at every child slot. `@object-ui/types/zod` exports five schemas. `@object-ui/types` exports two TypeScript node types and `AuthoringNode` gains their members. The `object-pivot` registration publishes one more input (`drillDown`). - **Narrows.** Nothing that parsed before is refused now. Every document naming one of the seven was refused before, so the refusals inside each arm (a flat prop, a misspelt `mobileNavMode`, a missing `fields`, `drillDown.mode`) replace a refusal at `type` with a named one. The `ClosedBag` change touches only the two new bags: the objectui#11468 pin shows the other members unchanged. - **Evidence.** Base readings below (each key refused at `type`, both faces); the new pin `packages/types/src/__tests__/passing-keys-arms-11440.test.ts` holds what each arm accepts and refuses on both faces with a lit control per refusal; `packages/cli/src/__tests__/validate-passing-keys-11440.test.ts` runs `objectui validate` end to end; each registration's inputs are pinned against its arm in the package that registers it (four new files); two ablations turn the ratchet and the pins red (below). ## Zone 2 readings — the mechanism assumptions, measured 1. **Registrations: held, with one correction.** All seven are where the brief says. `app` is registered on `PageRenderer` too (with `page`), not only the three kinds; its literal is the app document's (`AppComponentSchema`). Each declaration is built from the registration's inputs; where a renderer reads a key the inputs omit, it is named below. 2. **The page kinds: held.** At the merged base `6007dd4e4` the card's probe `{ type: 'home', name, regions }` and the spec's own page document were refused at `type` on both faces, while `{ type: 'page', pageType: 'home' }` passed. After: all pass. The precedent followed is `PageNodeSchema` itself (the spec fields by reference through `specFieldsExcept`); the strict face derives from the arm like every other. On objectstack's example pages (objectstack `4e6dc233`, read only, the eleven `definePage` documents of kind `home` or `record` in `app-crm` and `app-showcase`, run through both faces by an uncommitted probe): base, 11 of 11 refused at `type` on both faces; after, none is refused at `type`, and 10 of 11 parse on both faces. The eleventh (`showcase_my_work`) passes the tolerant face, and the strict face refuses a nested `{ type: 'grid', properties: { … } }` region component as an unrecognized key (`properties`): the `grid` arm takes its props flat. That is the `grid` arm's existing posture, unchanged here: the same component inside a `type: 'page'` document is refused the same way (measured). **`app` and `list` today, unchanged here:** a spec page of kind `app` is judged by the app document's arm (the tolerant face passes it unjudged, the strict face refuses `regions`, `template`, `kind` as unrecognized keys); a spec page of kind `list` meets the `list` component's arm (refused for having no `items`, and on the strict face for `regions` and the other page keys). No new key is registered. ⚠️ A finding on the way: declaring the page node's members a second time inside `AnyComponentSchema` made `tsc` refuse the union with TS7056 (inferred type too long to serialize); `PageKindNodeSchemaType` names the arm's type by reference, so declaration emit names it. 3. **`spec-report`: not in this pull request.** Measured only: the strict face refuses `{ type: 'report', report }` as an unrecognized key (`report`), and the tolerant face passes it unjudged. The fold needs a file outside the claimed surface (below). 4. **`detail-section`: armed, not folded.** The spec's `record:details` section names its heading `label` and takes field NAMES; this node names it `title` and takes `DetailViewField` objects. The spec row refuses the README's section with `unrecognized_keys` on `title` and `invalid_type` on each field object (pinned). After arming, the plugin-detail README's `detail-view` document validates except for one issue, at `tabs.1.content.items`: its second tab hands `record:activity` a host feed (`items: activityData`), which objectui#11321 refuses by name in a JSON document, and whose refusal text names this README as the TSX composition where it stays legal. Measured both ways by `objectui validate` (the first tab alone: valid; the full document: that one issue). The README is not edited. 5. **`app-schema-renderer`: the guide's node validates; the `schema` input is not declared.** The governed guide teaches the node's `mobileNavMode` key, in prose, and `{ type: 'app-schema-renderer', mobileNavMode: 'bottom_nav' }` validates on both faces after. The registration declares three inputs. Measured through the real `SchemaRenderer` and registry: `SchemaRenderer` strips the `schema` key and hands the component the NODE as its `schema` prop, so a node with its navigation under `schema` drew no navigation, and the same navigation written on the node drew. A node's `children` drew nothing either. So the arm declares `basePath` and `mobileNavMode` and refuses both content channels; where the app document belongs on this node is an open question for the seat (below). No `skills/**` edit. 6. **Namespaced twins: none armed; the pin holds at 362.** The seven twins are `ui:home`, `ui:record`, `ui:utility`, `layout:app-schema-renderer`, `plugin-dashboard:object-pivot`, `plugin-form:embeddable-form`, `plugin-detail:detail-section`. Each is its own literal, so the bare arm does not claim it; the ratchet row asserts each twin is still refused at `type`. 7. **The objectui#8347 ceiling-pin tests: not tripped** (types suite green on the head). objectui#11468 and objectui#11478 landed during the run and were merged in; the first needed the two node types above. ## Gates Head `ccb1077bc` (a merge of `main` `401611b21` into `34d127803`). Exit codes captured to a file before reading. Heavy runs through the shared verify lock; absolute times are shared-box readings. | check | result | |:--|:--| | `pnpm --filter @object-ui/types build && … type-check` (three legs) | 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/types/ packages/cli/` | exit 0 · Test Files 361 passed (361) · Tests 9266 passed (9266) | | type-check `@object-ui/plugin-dashboard`, `plugin-form`, `plugin-detail`, `layout`, `cli` (after their closure build) | exit 0, each `type-check: Done` | | `packages/layout/` plus the plugin-form / plugin-detail tests that read a zod face or name the keys, plus every `apps/console` / `packages/app-shell` test that reads a zod face or names a key | exit 0 · 101 files, 1190 tests | | every other test outside the touched packages that reads a zod face, `KNOWN_SCHEMA_TYPES`, or names one of the eight keys (examples, scripts, components, core, react, plugin-report and others) | exit 0 · 89 files, 2309 tests | | `packages/plugin-dashboard/` whole suite, at `30614b964` (before the two merges; neither touched this package) | exit 0 · 161 files, 1534 passed, 6 skipped; its 20 zod-reading or pivot tests re-run on the head: exit 0 | | `node scripts/regenerate-known-schema-types.mjs --check` | exit 0 · "matches the registry derivation" | | `check:doc-snippets` (after its `--build-filter` closure build), `check:doc-examples`, `check:readme-exports` | exit 0 each · 776 of 776 blocks judged, 0 failed; 122 examples, ledger exact; OK | | `check:doc-types`, `check:doc-fences`, `markdown-test-inputs --audit` | exit 0 each | | `check:control-bytes`, `check:new-line-citations` (0 new), `check-changeset-presence`, `check-changeset-no-major`, `check:pending-changeset-literals` | exit 0 each | | `check:changeset-claims` (report-only) | exit 0 · read the pending changesets that name a touched file; three made false by this change got the dated notes above | | `check:handler-key-reads`, `check:element-data-source-declaration`, `check:registry-bare-names`, `check:spec-symbols`, `check:test-path-roots` | exit 0 each | | `check-governed-queue-guard --test` (the changed paths) | NOT GOVERNED | | `pnpm check` (the CLI sweep over the repository) | before (types source swapped to `main` `401611b21` under a trapped restore, proven by blob hash and an empty `git diff HEAD`) and after: identical, 287 validated, 146 recognised but not validated, 0 did not validate, 47 skipped. No tracked JSON or YAML document names one of the seven types, so the sweep has nothing new to validate. The expectation that more documents validate does not hold | | ESLint, narrowed | `--no-inline-config --format json` over the 24 changed lintable files: 1 error, 53 warnings, 0 of them on a line this branch adds (attributed by `git diff -U0` against the merge base). The error is the `no-restricted-imports` report on a `@objectstack/spec/ui` re-export in `packages/types/src/index.ts` that an `eslint-disable` comment covers, which `--no-inline-config` ignores; that line predates this branch. Population: each touched package's `lint` script; `eslint.config.js` sets no type-aware parser project (0 matches for `parserOptions`, `project:`, `projectService`), so no untouched file's verdict can move. Full `pnpm lint` is CI's | ## Reverse verification - **Base, predicted and read.** At `6007dd4e4` each of the seven was refused with one `invalid_union` at `type` on both faces, and the ratchet read 8. - **Ablation 1, the page kinds.** `PageKindNodeSchema` removed from the root union by `ablation-replace` (anchor hit 1 to 0, blob moved), the pin files run, the file restored. Predicted: the ratchet goes red with the three kinds. Read: 3 files red, 14 rows; "`objectui validate` refuses 4 registered bare key(s) at `type`; the pin is 1. Refused: home, record, spec-report, utility". Restore proven: blob equals HEAD, `git diff HEAD` empty. - **Ablation 2, a declared plugin key.** `ObjectPivotBlockSchema` removed from `ObjectQLPublicBlockComponentSchema` the same way. Read: 2 files red, 15 rows; "Refused: object-pivot, spec-report". Restore proven the same way. - The first attempt at both was a no-op, and is reported as one: the replacement text was a prefix of the anchor, so the tool refused (replacement count did not rise) and restored without running anything. The readings above are the second attempt, with replacement text that was not in the file. - **Ablation 3, the `report` wrapper:** belongs to the second pull request. ## What is left (the second pull request) The `spec-report` fold, in the ruled step order (`report` declares the wrapper shape, `DrillDownDrawer` moves to `report`, then the alias goes), needs one file the claim does not name: `apps/console/src/register-plugins.ts` registers a lazy `spec-report` stub beside `report-viewer`, and an unregistered key with a surviving console stub strands that stub. Per the brief, that waits for the seat to amend the claim; it is not started here. ## Acceptance notes - `object-pivot` and `embeddable-form` registrations still declare `objectName` `required: true`, while both renderers are gate-wrapped and the arms accept `dataSource.object` instead. The parser tier's verdict on a binding-only node was not measured here; objectui#10392 is the calendar instance of the same shape. - `EmbeddableForm` reads config keys its registration does not publish (`branding`, `thankYouPage`, the anti-spam keys and others). The arm declares the registration's inputs only, so the strict face refuses them as unrecognized keys. - `PageRenderer` derives its layout from `pageType`, defaulting to `record`, and not from `type`, so a kind node rendered outside `PageView` (which writes both) would draw the record layout. Read from the source, not measured. - The plugin-dashboard README and `plugin-dashboard.mdx` describe `object-pivot` as an "internal wrapper around `ObjectPivotTable`"; it is a Tier A public block with an arm now. --- _Generated by [Claude Code](https://claude.ai/code/session_01YLg8XqWGJ785fwQ5v4pH37)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4abc0aa commit 00ccdf7

30 files changed

Lines changed: 1429 additions & 56 deletions

‎.changeset/10685-drilldown-per-block.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,14 @@ node. Nothing ever drilled a `pivot` node; a pivot drill is authored on `object-
6666
entry says. `object-pivot` still has no zod mirror, so what this entry says of a stored
6767
`object-pivot` config stands. `.changeset/10932-pivot-drilldown-retired.md` (PR objectui#10972)
6868
states what ships; the text above is kept as the reading of this change.
69+
70+
⚠️ **Dated note, 2026-10-02 — `object-pivot` has a zod arm now — objectui#11440.**
71+
Later in this same release `object-pivot` gained an arm in `@object-ui/types/zod`,
72+
`ObjectPivotBlockSchema`, whose `properties.drillDown` is this entry's per-block shape: the shared
73+
drill config with `mode` refused by name (`invalid_type` at `properties.drillDown.mode`). So "A
74+
stored JSON pivot config carrying `mode` is still accepted" and "neither `object-pivot` nor
75+
`PivotTableSchema` has a zod mirror, so no validator in this repository reads the members of a
76+
pivot's `drillDown`" above, and "`object-pivot` still has no zod mirror" in the 2026-09-29 note, no
77+
longer hold: `safeValidateSchema` and the strict face refuse that `mode`.
78+
`.changeset/11440-arm-passing-types.md` states what ships. The rest of this entry is kept as the
79+
reading of this change.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
'@object-ui/types': minor
3+
---
4+
5+
`safeValidateSchema` — and so `objectui validate` — accepts seven more registered node types: the spec page kinds `record`, `home` and `utility`, and `app-schema-renderer`, `object-pivot`, `embeddable-form` and `detail-section` (objectui#11440, the objectui#10859 follow-up for the keys that pass the criterion).
6+
7+
**Clause-②: yes** — the accept set of `AnyComponentSchema` widens by seven `type` literals, and `@object-ui/types/zod` exports five new schemas. Nothing that parsed before is refused now: every document naming one of the seven was refused with `invalid_union` at `type`, at the root and at every child slot.
8+
9+
**What changed, in observable terms.** Each new authorable spelling, and what it accepts:
10+
11+
- **`{ type: 'record' | 'home' | 'utility', … }`** — a stored `@objectstack/spec` page whose `type` is its page kind, the spelling the spec's `PageSchema` declares and the node `PageView` renders. `PageKindNodeSchema` takes every member of the `page` node (`PageNodeSchema`) by reference, so the spec's own page document validates (`{ type: 'home', name, label, regions }`), each region component is judged by the node union, and the `page` node's refusals (`actions`, `breadcrumbs`, `maxWidth`, `padding`, `body`) apply. The spec's other two kinds are not armed here: `app` stays the app document's literal (`AppComponentSchema`) and `list` the `list` component's. `{ type: 'page', pageType }` validates as before; which spelling is canonical is not changed.
12+
- **`{ type: 'app-schema-renderer', mobileNavMode?, basePath? }`** — `AppSchemaRendererNodeSchema`, the whole-shell node `@object-ui/layout` registers. `mobileNavMode` is `'drawer'` or `'bottom_nav'`, as the registration declares it, so the node the mobile guide teaches validates and a misspelt mode is refused at `mobileNavMode`. The registration's third input, `schema`, is not declared: `SchemaRenderer` strips that key, so a node never delivers it, and the strict face refuses it as an unrecognized key. `children` and `body` are refused by name: a node draws neither.
13+
- **`{ type: 'object-pivot', properties: { … } }`** — `ObjectPivotBlockSchema`. The `properties` bag holds the registration's inputs (`objectName`, `title`, `rowField`, `columnField`, `valueField`, `aggregation`, `showRowTotals`, `showColumnTotals`, `filter`, `format`, `drillDown`) and is required: `rowField`, `columnField` and `valueField` are required members. `objectName` is required unless the node's `dataSource.object` names the object (`RECORD_SOURCE_REQUIRED`). `drillDown` refuses `mode` by name, and the retired `dataProvider` is refused by name.
14+
- **`{ type: 'embeddable-form', properties: { … } }`** — `EmbeddableFormBlockSchema`. The required bag holds exactly the registration's inputs (`formId`, required; `objectName`, required unless `dataSource.object` names the object; `title`, `description`, `fields` as field names, `allowMultiple`).
15+
- On both blocks, a prop written flat on the node instead of in `properties` is refused by name, with a message naming `properties.KEY`, as `@objectstack/spec`'s page component refuses it as mis-layered; the node declares the `dataSource` binding (the spec's `ElementDataSourceSchema`) and `responsiveStyles`; `children` and `body` are refused by name.
16+
- **`{ type: 'detail-section', fields, … }`** — `DetailSectionNodeSchema`, one field section of `@object-ui/plugin-detail` as a node: the ten `DetailViewSectionSchema` members its registration publishes (`title`, `description`, `icon`, `fields`, `collapsible`, `defaultCollapsed`, `columns`, `showBorder`, `headerColor`, `hideEmpty`), flat on the node, `fields` required. The plugin-detail README's `detail-view` tab with a `detail-section` content validates. `children` and `body` are refused by name.
17+
18+
The strict authoring face (`StrictAnyComponentSchema`) derives from these arms and accepts the same documents, closed to undeclared keys.
19+
20+
**The TypeScript face.** `@object-ui/types` exports `ObjectPivotBlockNode` and `EmbeddableFormBlockNode`, the two arms' authoring inputs, derived by reference as the other `ObjectQLPublicBlockNode` members are, so they are in `AuthoringNode` and `SchemaRenderer`'s `schema` prop accepts them. Each bag is required and closed: a misspelled bag key, a missing bag and a prop written flat do not compile. The page kinds were already typed (`PageDocumentNode`, the spec's own page input).
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@object-ui/plugin-dashboard': minor
3+
---
4+
5+
The `object-pivot` registration publishes `drillDown` as an input (objectui#11440).
6+
7+
`ObjectPivotTable` has always read `drillDown` (whether the drill is on, its filter, title, target, columns, row cap and report), and the `pivot` node's refusal of `drillDown` names `object-pivot` as where a pivot drill is authored. The registration did not list it, so the `sdui-parser` manifest and the designer did not offer it. Its description says `drillDown.mode` does not apply: every click point on a pivot is an aggregated bucket, so the pivot always drills through. `@object-ui/types/zod`'s new `object-pivot` arm declares the same member (`.changeset/11440-arm-passing-types.md`). Nothing else about the block changes.

‎.changeset/11468-bag-carrier-node-types.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,12 @@ accepted before is still accepted. Reading is a different matter: on a union, a
3535
key that one closed member does not declare is TS2339, so code that reads a key
3636
off `SchemaRendererProps['schema']` or off `toRenderableSchema`'s return needs
3737
to narrow first.
38+
39+
⚠️ **Dated note, 2026-10-02 — two more members — objectui#11440.**
40+
Later in this same release `ObjectQLPublicBlockComponentSchema` gained the `object-pivot` and
41+
`embeddable-form` arms, so `ObjectQLPublicBlockNode` derives ten members, and "The eight members
42+
are also exported by name" above reads ten: `ObjectPivotBlockNode` and `EmbeddableFormBlockNode`
43+
join the list. Their bags have no row either (each is built from the block's registration
44+
inputs) and are REQUIRED, and the derivation closes a required bag as it closes an optional one.
45+
`.changeset/11440-arm-passing-types.md` states what ships. The rest of this entry is kept as the
46+
reading of this change.

‎.changeset/7352-drill-down-config-mirror.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,13 @@ change's reading, not the release's, and that mirror's `drillDown` member refuse
6868
the key rather than mirroring it. `.changeset/10932-pivot-drilldown-retired.md`
6969
(PR objectui#10972) states what ships; the text above is kept as the reading of
7070
this change.
71+
72+
⚠️ **Dated note, 2026-10-02 — `object-pivot`'s arm references this mirror — objectui#11440.**
73+
Later in this same release `object-pivot` gained an arm in `@object-ui/types/zod`
74+
(`ObjectPivotBlockSchema`), whose `properties.drillDown` extends this entry's
75+
`DrillDownConfigSchema` per block, with `mode` refused by name (objectui#10685). So "for the
76+
release the referencing declarations are the opening paragraph's two" in the 2026-09-29 note
77+
above no longer holds: they are three, `ChartSchema`, `ObjectDataTableSchema` and the
78+
`object-pivot` arm, the last two each extending the mirror per block.
79+
`.changeset/11440-arm-passing-types.md` states what ships. The rest of this entry is kept as the
80+
reading of this change.

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

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,19 @@ import { validate } from '../commands/validate.js';
7676
* more, see `UNREGISTERED_10859_BATCH_8_PHASE_2C`; objectui#11441, run as one
7777
* more batch of this card: minus `navigation-renderer` and `responsive-grid`,
7878
* see `UNREGISTERED_11441`; batch 8 phase 2d: minus the ten `sidebar-*`
79-
* primitives, see `UNREGISTERED_10859_BATCH_8_PHASE_2D`). LOWER it when a batch
80-
* arms or retires more keys; never raise it.
79+
* primitives, see `UNREGISTERED_10859_BATCH_8_PHASE_2D`; objectui#11440: minus
80+
* the seven keys its first pull request armed, see `ARMED_11440`). LOWER it
81+
* when a batch arms or retires more keys; never raise it.
8182
*
82-
* The 8 that remain are named in `STILL_REFUSED_10859`, and the row below
83+
* The 1 that remains is named in `STILL_REFUSED_10859`, and the row below
8384
* pins the refused set to exactly that list, so this comment cannot drift from
84-
* the measurement. They are the eight the seat ruling left registered —
85-
* `object-pivot`, `embeddable-form`, `detail-section`, `home`, `record`,
86-
* `utility`, `spec-report` and `app-schema-renderer` (objectui#11440, which
87-
* arms them).
85+
* the measurement. It is `spec-report`, the eighth key the seat ruling left
86+
* registered: an alias of `report`, retired there in the ruling's step order
87+
* (`report` declares the wrapper shape `spec-report` carries, the dashboard's
88+
* drill drawer moves to `report`, then the alias goes), in objectui#11440's
89+
* second pull request. The other seven — `object-pivot`, `embeddable-form`,
90+
* `detail-section`, `home`, `record`, `utility` and `app-schema-renderer` —
91+
* are armed.
8892
*
8993
* `navigation-renderer` and `responsive-grid`, the two the seat sent to the
9094
* maintainer, were ruled B / B on objectui#11441 (record `5950208338`) and are
@@ -110,7 +114,7 @@ import { validate } from '../commands/validate.js';
110114
* last 2 waiting on objectui#11441. That card ran first, so the count is 8
111115
* after phase 2d, and objectui#11440 then takes it to 0.
112116
*/
113-
const REFUSED_AT_TYPE = 8;
117+
const REFUSED_AT_TYPE = 1;
114118

115119
/**
116120
* The head's refused count over the NAMESPACED keys (objectui#10872 batch 1:
@@ -132,7 +136,8 @@ const REFUSED_AT_TYPE = 8;
132136
* twins of the two keys it unregistered; objectui#10859 batch 8 phase 2d: minus
133137
* the ten `ui:sidebar-*` twins of the ten `sidebar-*` primitives it
134138
* unregistered). LOWER it when a batch arms or retires more keys; never raise
135-
* it.
139+
* it. objectui#11440 armed seven bare keys and none of their namespaced twins
140+
* (`ARMED_11440` maps each to its twins), so it does not move this pin.
136141
*/
137142
const NAMESPACED_REFUSED_AT_TYPE = 362;
138143

@@ -250,14 +255,28 @@ const UNREGISTERED_10859_BATCH_8_PHASE_2D = {
250255
} as const;
251256

252257
/**
253-
* Every bare key still refused at `type` after objectui#11441 and objectui#10859
254-
* batch 8 phase 2d — the `REFUSED_AT_TYPE` docblock says why each is still
255-
* here. Alphabetical.
258+
* The seven bare keys objectui#11440's first pull request ARMED (the seat
259+
* ruling `5945530142`, as amended by `5945583855`), each mapped to its
260+
* namespaced twins. A twin is a separate registry key with its own literal, so
261+
* arming the bare key does not arm it: each twin is still refused at `type`,
262+
* and `NAMESPACED_REFUSED_AT_TYPE` does not move. The three page kinds are
263+
* registered in the `ui` namespace by `@object-ui/components`.
256264
*/
257-
const STILL_REFUSED_10859 = [
258-
'app-schema-renderer', 'detail-section', 'embeddable-form', 'home',
259-
'object-pivot', 'record', 'spec-report', 'utility',
260-
] as const;
265+
const ARMED_11440 = {
266+
home: ['ui:home'],
267+
record: ['ui:record'],
268+
utility: ['ui:utility'],
269+
'app-schema-renderer': ['layout:app-schema-renderer'],
270+
'object-pivot': ['plugin-dashboard:object-pivot'],
271+
'embeddable-form': ['plugin-form:embeddable-form'],
272+
'detail-section': ['plugin-detail:detail-section'],
273+
} as const;
274+
275+
/**
276+
* Every bare key still refused at `type` after objectui#11440's first pull
277+
* request — the `REFUSED_AT_TYPE` docblock says why it is still here.
278+
*/
279+
const STILL_REFUSED_10859 = ['spec-report'] as const;
261280

262281
/** Is `type` unclaimed by every arm of the validator's root union? */
263282
function refusedAtType(type: string): boolean {
@@ -397,8 +416,8 @@ describe('registered component types refused at `type` — a ratchet (objectui#1
397416
}
398417
// Lit controls: `grid`, the spelling the `responsive-grid` retirement
399418
// points authors to, is registered and claimed; `app-schema-renderer`, the
400-
// whole-shell door navigation goes through, stays registered (and refused,
401-
// until objectui#11440 arms it).
419+
// whole-shell door navigation goes through, stays registered (armed since
420+
// objectui#11440).
402421
expect(BARE_KEYS).toContain('grid');
403422
expect(refusedAtType('grid')).toBe(false);
404423
expect(BARE_KEYS).toContain('app-schema-renderer');
@@ -422,6 +441,24 @@ describe('registered component types refused at `type` — a ratchet (objectui#1
422441
expect(NAMESPACED_KEYS).toContain('ui:sidebar');
423442
});
424443

444+
it('counts the seven keys objectui#11440 armed, and none of their namespaced twins (objectui#10859)', () => {
445+
expect(Object.keys(ARMED_11440)).toHaveLength(7);
446+
for (const [key, twins] of Object.entries(ARMED_11440)) {
447+
// Armed: still registered, and claimed by an arm.
448+
expect(BARE_KEYS, key).toContain(key);
449+
expect(refusedAtType(key), key).toBe(false);
450+
// The twin is its own literal, so the bare arm does not claim it.
451+
for (const twin of twins) {
452+
expect(NAMESPACED_KEYS, twin).toContain(twin);
453+
expect(refusedAtType(twin), twin).toBe(true);
454+
}
455+
}
456+
// Lit control: the eighth key, `spec-report`, is still registered and
457+
// still refused, until objectui#11440's second pull request retires it.
458+
expect(BARE_KEYS).toContain('spec-report');
459+
expect(refusedAtType('spec-report')).toBe(true);
460+
});
461+
425462
it('the refused set is exactly the named remainder — the pin comment cannot drift (objectui#10859 batch 8)', () => {
426463
const refused = BARE_KEYS.filter(refusedAtType).sort();
427464
expect(refused).toEqual([...STILL_REFUSED_10859]);

0 commit comments

Comments
 (0)