Repository navigation
fix(components,types): eight renderers stop reading undeclared node keys; input-otp and loading read their declared spellings (objectui#11347) - #11360
Conversation
… keys; loading and input-otp read their declared spellings (objectui#11347)
The display, feedback and form units of objectui#11347, the components
preparation card for objectui#8347's BaseSchema index-signature removal.
Each key was measured on its own: the installed @objectstack/spec (17.5.0)
has no row for `list`, `loading` or `input-otp`, and no catalog entry,
example, docs page, package README or objectstack-shipped document authors
any of the three read keys.
- list: the `title` heading read is retired (rule 3: no spec row, no
producer). Its registration input goes with it.
- loading: the undeclared `text` read moves to the declared
`LoadingSchema.label` ("Loading text/message" on both faces), which was
inert. One spelling, no alias.
- input-otp: the undeclared `maxLength` read moves to the declared
`InputOTPSchema.length`, the spelling all five catalog entries and the
docs page author. Measured through the real SchemaRenderer, `length: 4`
and `length: 8` drew six slots before and draw four and eight now.
The body/children tombstone texts on the zod mirror and the matching
docblocks are brought in line with what the renderers now read.
Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
…ive` (objectui#11347) The navigation unit of objectui#11347. `active` was read off a `schema` typed `BaseSchema` itself: no type declares it for `sidebar-menu-button`, the installed @objectstack/spec has no row for the node, and no catalog entry, example, docs page or README authors it (the five catalog sidebars author none). With no spec row and no producer, the read is retired along with its registration input; `isActive` is no longer driven by the node. Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT Co-authored-by: Claude <noreply@anthropic.com>
…reading undeclared keys (objectui#11347) The overlay unit of objectui#11347. The installed @objectstack/spec has no row for any of these four nodes, and no catalog entry, example, docs page, README or objectstack-shipped document authors any of the keys, so each named read is retired (rule 3): - drawer: `shouldScaleBackground`, `footer` and `showClose`. The drawer draws no footer and no Close button from the node any more. - dropdown-menu, popover, sheet: `modal`. Their registration inputs (and sheet's `modal` seed) go with them. Measured through the real SchemaRenderer: an authored `modal` or `shouldScaleBackground` still reaches the Radix / vaul root through the prop spread, which the strict authoring face owns; the named reads were redundant on that path. Collateral, each forced by a verdict here: - `node-slot-numeric-falsy.test.tsx` loses its `drawer footer` row (the slot no longer exists). - `CRUDDialogSchema.showClose`'s docblock and its pin in `layout-default-jsdoc-7361.test.ts` named the drawer's look-alike read as live; both now record it retired. - the pending objectui#8318 changeset gets a dated correction for the same sentence; its frontmatter is unchanged. Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT Co-authored-by: Claude <noreply@anthropic.com>
…objectui#11347) 64 of the 94 test errors objectui#8347's P1 mutation raises in this package are literals checked against `BaseSchema` (the `renderComponent` parameter, `SchemaRenderer`'s `schema`, or a `SchemaNode` child slot) that author keys their concrete node type declares. Each is now typed as that node through a small typed identity per file (`actionBar`, `textNode`, `selectNode`, ...), which keeps excess-property checking against the concrete type rather than switching it off. The button variant / size and input type tables are typed with the declared unions, which the concrete type now demands. Left as they are, on purpose: - 29 literals author a node no `@object-ui/types` declaration covers: the public blocks' `properties` bag (`element:text`, `element:text_input`, `element:record_picker`), `page:tabs`' `items`, `action:button`'s executor keys, the page kinds' `kind`, the legacy `props` alias bag and a test-registered probe type. Handed back on objectui#11347 rather than cast or widened. - `label`'s retired `content` spelling is a deliberate probe; it is objectui#8347's to flip. Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT Co-authored-by: Claude <noreply@anthropic.com>
…derers read (objectui#11347) The input-otp page's schema block shows `length` as the optional slot count it is (default 6) and says the renderer now reads it; the loading page gains the declared `label`. Plus the changeset for objectui#11347. Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT Co-authored-by: Claude <noreply@anthropic.com>
|
changeset-claim-re-read
|
✅ Console Performance Budget
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
Size Limits
|
Contract reviewServed-tier: Rendered 2026-10-01T08:46Z from card #11347 (body and all three comments), PR #11360 (body, the 32-file list, the diff against ① Derived judgmentsSpec row, all eight node types — RIGHT. At Producer census, re-grepped at
The two read moves — RIGHT. Each honours a declared key, and no fallback reads the old spelling (AGENTS.md §5 #0.1).
The retirements — what the renderer now draws for an authored value.
Published-surface narrowing — judged on all of it, not on two rows. Seven
Pins — RIGHT, not vacuous.
Docs — RIGHT for the three named faces. Tests — no widening. The 23 typed-identity helpers are monomorphic, each annotated with one concrete node type and returning its argument; the diff adds no ② Semver level
③ Boundary flags
Remedy for the one FAIL item (small; nothing else need move). On the same branch: (a) correct the producer under AGENTS.md #0.1 — drop Implemented-by: VERDICT: FAIL |
…a.title (objectui#11347, round 2) The at-tier record on PR #11360 found a producer the census missed: the published `skills/objectui` expressions guide authors `{ "type": "list", "title": "Team", ... }` and says `title` is read off the node. The seat ruled under the card's rule 2, by 未声明键随实现, 文档随之: `list` has no row in the installed @objectstack/spec, the renderer reads `title` and the registration publishes it, so the key is declared rather than retired. - `list` draws `schema.title` as its heading again, and the registration's `title` input returns. - `ListSchema.title?: string` is declared on the TypeScript face and on the zod mirror. A non-string `title` is now refused by name where `.passthrough()` used to keep it unjudged. Both faces gain the key together, so no zod-mirror-parity row opens. - The list docs page documents `title`. The refusal messages and docblocks no longer call it retired. - `list-title-declared-11347.test.tsx` pins it on three readers: the guide's literal drawn through the real SchemaRenderer (with a no-title control), the zod mirror, and the compiler (`@ts-expect-error` on a numeric title). - The changeset names the guide as `list.title`'s producer. It also stops claiming that `dropdown-menu` lost a `modal` input it never published. Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT Co-authored-by: Claude <noreply@anthropic.com>
… a fresh literal (objectui#11347) `satisfies` keeps a literal fresh, so under objectui#8347's P1 mutation the control's literal was still checked against `SchemaRenderer`'s `BaseSchema` and `title` read as an excess key. A typed const carries the declaration. Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT Co-authored-by: Claude <noreply@anthropic.com>
…rrection to its tree (objectui#11347) The `changeset-claim-re-read` comment flagged `renderers/overlay/drawer.tsx:38` in `.changeset/8318-jsdoc-default-tag-corrections.md`, because this branch rewrites that line. The flagged address is in the original bullet, inside the append-only prefix, so it stays byte-identical. The appended correction now binds the same address, in its own sentence, to `33f4a1980` (the commit that added this changeset), where line 38 is the `drawer` registration's `showClose` read. It also names that read by what it is, so the published CHANGELOG line is anchored. The first 2879 bytes still `cmp` equal to origin/main. Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
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
Size Limits
|
✅ Console Performance Budget
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
Size Limits
|
Contract reviewServed-tier: Delta record, round 2, rendered 2026-10-01T09:49Z. It supersedes record ① Derived judgments1. The round-1 FAIL item is closed; docs and snippets agree with 2. The declaration is right on both faces, matches the registration's published type, and is not a declaration against the spec. 3. Every round-2 changed line, by commit.
4. The 5. The append-only prefix still holds. 6. Pending changesets naming ② Semver level
③ Boundary flags
Remedy for the one FAIL item (one word and one sentence; nothing else on this head need move): in Implemented-by: VERDICT: FAIL |
…a member (objectui#11347, round 3)
Round 2 declared `ListSchema.title` on the shipped `.d.ts` and on the
mirror's `.shape`, and the mirror now refuses a non-string `title` by
name. objectui#7722 graded that class on this same schema `minor` ("the
published face gains ... members"), so `patch` was wrong. The Types
section now says so, in the same words 7722 uses.
Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
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
Size Limits
|
Contract reviewServed-tier: Delta record, round 3, rendered 2026-10-01T10:06Z. It supersedes record ① Derived judgments1. The delta is exactly one file. 2. The level and the new sentence are right, and every other sentence in the file still holds. Line 3 now reads 3. The PR body is unchanged and states no types level. Its ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
Takes c82ff39 (objectui#11347, PR #11360), which touches none of this branch's files. Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh Co-authored-by: Claude <noreply@anthropic.com>
…l key under the one spelling GridFieldMetadata declares (objectui#11070 round 8) (objectstack-ai#11369) Refs objectstack-ai#11070 Clause-②: yes (narrowing). `GridFieldMetadata.allow_reorder` either retires or becomes the read key, and the undeclared reads are declared or retired. It is priced in the changeset. As landed: `allow_reorder` became the read key. The published `GridFieldMetadata` face widens by two optional members (`total_field`, `add_label`), and what the grid widget honours narrows by five retired reads (`reorderable`, `amount_field`, `amountField`, `allow_duplicate`, `show_line_numbers`). Priced minor with the break stated in `.changeset/11070-grid-field-keys-round8.md` (the fixed group never declares `major`). Round 8 of objectui#11070: the grid widget's field-level keys, the round the round-7 seat review (`5926481945`) answered A. Claim `5927734710`, session `session_01TdiauJaVCHuj45EzZGUxHh`. Measured first; each key now has one spelling, and that spelling is declared and read. ## Per-key table (base `5638529e` → head) Readers and producers are cited by symbol, not by line address (AGENTS.md objectstack-ai#11). | Key | Declared (before → after) | Produced (measured at base) | Read by `GridField` (before → after) | |---|---|---|---| | `allow_reorder` | `GridFieldMetadata` → same | no producer; taught by `grid.mdx`'s typed fence | not read → **read** | | `reorderable` | none | none in either repository | read → **retired** | | `total_field` | none → **`GridFieldMetadata`** | `MasterDetailForm` `field={…}` (from the detail's `amountField`), `LineItemsPanel` `gridField` (from `amountField`), console `DevLookup` dev page | read (the one spelling) | | `amount_field` | none | none | read → **retired** | | `amountField` (on the widget) | none | nothing writes it into the widget's config | read → **retired** | | `add_label` | none → **`GridFieldMetadata`** | `MasterDetailForm` (from `subforms[].addLabel` / `details[].addLabel`; `detail.add` in form mode) | read | | `allow_duplicate` | none | none | read → **retired** (duplicate follows `allow_add`) | | `show_line_numbers` | none | none | read → **retired** (the column always shows) | | `sort_field` | none → none (**held**, see below) | `MasterDetailForm` from `d.sortField`, which is the authored `details[].sortField` or else `deriveDetail`'s derived sort field | read (now named in `UndeclaredGridKeys`) | | `min_rows` / `max_rows` | `GridFieldMetadata` | both adapters (from `minRows` / `maxRows`) | read (unchanged) | | `allow_add` / `allow_delete` | `GridFieldMetadata` | `LineItemsPanel` (from `readonly`) | read (unchanged) | `GridField` now reads its config as a `Partial` of `GridFieldMetadata` plus the one named undeclared key, so a read of an undeclared key fails to compile (ablation A5 below). ## Where the keys come from (measured) - **No `grid` field type in the spec.** Installed `@objectstack/spec` 17.5.0 `FieldType` has no `grid`, and neither has objectstack `origin/main` `7a606a9a`. Every `type: 'grid'` hit in objectstack (examples, platform objects, `cli` generators, `metadata-protocol`) is a LIST VIEW, not a field. - **Spec carriers that feed the widget:** `FieldSchema.inlineEdit: 'grid'` with `inlineTitle` / `inlineColumns` / `inlineAmountField`, which `attachInlineSubforms` (app-shell `MetadataProvider`) turns into derived `subforms[]` entries (`childObject`, `relationshipField`, `inlineMode`, `title`, `columns`, `amountField`; no `sortField`); the strict form `subforms[]` entry (`childObject`, `relationshipField`, `columns`, `amountField`, `totalField`, `title`, `addLabel`, `minRows`, `maxRows`, plus its folded aliases; no `sortField`); `object-master-detail-form` `details`, which the spec types `z.array(z.unknown())`; and `record:line_items`, which has no spec row (objectstack's showcase page writes `amountField` there, no `totalField`). - **The adapters** that build the widget's config: `MasterDetailForm` writes `columns`, `total_field`, `sort_field`, `min_rows`, `max_rows`, `add_label` (the six-key set `masterDetailDetailsMembers-8071` pins, unchanged); `LineItemsPanel` writes `columns`, `total_field`, `min_rows`, `max_rows`, `allow_add`, `allow_delete`. - **Authored paths to `type: 'grid'`:** objectui's `examples/schema-catalog/src/schemas/fields-grid/*` (three `form` documents whose `fields[]` entry is `type: 'grid'`; they write `name`, `label`, `type`, `columns` and `readonly`, no field-level key) and `content/docs/fields/grid.mdx`. objectstack: none. A census of all twelve snake_case keys across objectstack `origin/main` reads 0 files each (control: `amountField` hits 10). ## The two adapters' total, aligned `MasterDetailForm` showed the grid total whenever `amountField` was set; `LineItemsPanel` only when `totalField` was ALSO set. The alignment is the one `total_field:` line in `LineItemsPanel`'s `gridField`, now mapped exactly as `MasterDetailForm` maps it (`amountField`, else `'amount'` when only `totalField` is set). The widget key is the CHILD column summed (the spec's `amountField`), never the spec's `totalField`. ## Held at the key: `sort_field` (needs a seat answer) It has a live producer, so per the brief it stops at the key: no declaration, no retirement. `deriveDetail` derives the sort field from the child object's fields (`position` / `sort_order` / `sequence` / `line_no` / `line_number` / `sort`) when none is authored, and `MasterDetailForm` writes it as `sort_field`; objectui#11144 made that derivation reach details with authored columns. The strict `subforms[]` carrier does not declare `sortField`, and the spec has no inline sort-field key. Whether it becomes a declared key is in the report's `open_questions`. ## Reorder probe (`GridField`, two rows) | | no key | `allow_reorder: false` | `reorderable: false` | |---|---|---|---| | base `5638529e` | 2 handles | **2** handles | 0 handles | | head | 2 handles | **0** handles | 2 handles | The base row is the same new pin file run against base `GridField`: 6 red, 5 green. ## M3 (`scripts/measure-strict-authoring-face.mjs --json`) Base `5638529e` → head `f877cc6b` (pre-merge; the merge brought no corpus change to these keys): `totals` and every per-component row are identical (2229 nodes, 77 strict-refused, 46 strict-only, 31 red today; 601 documents, 72 strict-refused whole tree). Hypothesis H4 holds: the widget's config is built in code, and no corpus document writes a field-level key. Only two diagnostic fields moved: `rendererMentionsOfUndeclaredKeys` (a raw word count over renderer sources, moved by this diff's new comments and tests) and `unloadableModules` (an environmental reading: app-shell was unbuilt at the base run). ## Ablations (predictions written before each run) Each mutation went through objectstack's `scripts/ablation-replace.mjs` (anchor hit 1 → 0, blob changed, restored to HEAD with `git diff HEAD` empty). - **A1 reorder:** read `reorderable` again. Predicted 2 red; got 2 red, 9 green. - **A2 total:** read `total_field || amount_field || amountField` again. Predicted the two retired-spelling rows red; got 2 red, 9 green. - **A2b line-items total:** restore the base mapping in `LineItemsPanel`. Predicted "`amountField` alone shows the footer total" red; got 1 red, 1 green. - **A3 `allow_duplicate`:** read it again. Predicted 1 red; got 1 red, 10 green. - **A4 `show_line_numbers`:** gate the `#` header on it again. Predicted 1 red; got 1 red, 10 green. - **A5 declared face:** `GridFieldMetadata` loses `total_field` and `add_label` and gains the retired `reorderable`; `@object-ui/types` rebuilt, and the dist markers checked (planted 0 → 1, declared 1 → 0 each). Predicted and got: fields `tsc --noEmit` TS2339 on the `total_field` read and both `add_label` reads; fields `tsc -p tsconfig.test.json` TS2353 on the `declared` fixture and the two `grid({…})` calls, plus TS2578 (unused `@ts-expect-error`) on `retiredReorderable`; `check:doc-snippets` 1 failed block, `grid.mdx` TS2353 `total_field`. Restore leg: source restored, `@object-ui/types` rebuilt, markers back (0 / 1 / 1), `git status --porcelain` empty. ## Carried check (round-6 note): `formula.mdx`'s `addDays` / `daysBetween` Both evaluate. A probe through the installed `@objectstack/formula` 17.5.0 `ExpressionEngine.evaluate` (CEL, pinned `now`): `addDays(record.created_at, 7)` with `2026-09-28` gives `2026-10-05`; `daysBetween(record.start_date, record.end_date)` gives 29, and −29 with the arguments swapped. Control: an unregistered `addWeeks(…)` fails with `found no matching overload`. Both are registered in objectstack's `packages/formula` stdlib (`registerStdLib`). `formula.mdx` is unchanged. ## Local verification At head `1f2f81cf` (the merge of `origin/main` `ad58cc15`): - `@object-ui/types`, `@object-ui/layout` and `@object-ui/fields` builds; `type-check` green for `@object-ui/types`, `@object-ui/fields`, `@object-ui/plugin-form` and `@object-ui/example-schema-catalog`. Both new test files are in their packages' `tsconfig.test.json` programs (`--listFiles`). - Tests: `packages/types/` plus every `GridField*` and `LineItemsPanel*` test and `masterDetailDetailsMembers-8071`: 338 files, 8164 tests passed. Before the merge, at `f877cc6b`, the four whole packages (`packages/types/`, `packages/fields/`, `packages/plugin-form/`, `examples/schema-catalog/`) ran 733 files passed and 1 skipped, 15543 tests passed and 8 skipped. The merge touched none of `fields`, `plugin-form` or `schema-catalog`, nor any of their dependencies' sources; it touched three `types` tests, which the post-merge run covers. - `check:doc-snippets` (closure built through the lock): 768 of 768 blocks, 0 failed. `check:doc-fences`, `docs:check-links`, `check:control-bytes`, `check:i18n-keys`, the line-citation gate (0 new) and `check-type-check-coverage` are green. - Changeset gates: `check-changeset-presence`, `changeset:check`, `check-changeset-overwrite`, `check:changeset-claims` and `check:pending-changeset-literals` are green. The claims gate names two pending changesets that cite a touched file (`6357-basechema-bind-declaration`, which names `grid.mdx`; `6625-retire-fieldmeta-decimals`, which names `GridField.tsx`). Both paragraphs were read and still hold, so no dated note is owed. - Narrowed lint over the five changed `.ts`/`.tsx` files: 0 errors. The population is the eslint config's own `**/*.{ts,tsx}` blocks, and `.mdx`/`.md` match none of them. The file count, 5, is read from `--format json`. The config is not type-aware (`tseslint.configs.recommended`, no `parserOptions.project`, no custom rule reading the filesystem), so this diff cannot move a verdict on an untouched file. Warnings went 49 → 48 on the three modified files (the `cfg` `any` is gone), plus 1 on the new line-items test (its `as any` adapter mock, the sibling tests' convention). - `check-governed-queue-guard --test` over the changed paths: NOT GOVERNED. ## Acceptance notes - **The form-field face carries none of the field-level family.** `FormField` / `FormFieldSchema` declare none of `min_rows`, `max_rows`, `allow_add`, `allow_delete`, `allow_reorder`, nor now `total_field` / `add_label`. So a `form` `fields[]` grid entry that writes one reaches the widget at runtime, and the strict face refuses it by name. This predates the round for the first five keys; no corpus document writes any of them (M3 does not count them). Mirroring is `form.ts` / `form.zod.ts` territory (open PR objectstack-ai#11360), excluded by the claim, and is raised in the report's `open_questions`, not done here. - **`docs/adr/0001-master-detail-subform.md`** (governed, Tier H) still names `allow_reorder` as future work and `sort_field`. An ADR records the decision taken; left as is, the seat's standing disposition for ADRs. - **`grid.mdx`'s "Features" list** claims a "Pagination Preview … Showing X of Y" that `GridField` does not render. That prose is outside this round's key set; noted, not changed. - **`fields-grid` fixtures are unchanged:** they write no field-level key, so they already carry only declared and read keys. - **`sort_field`** is described above and left as is. --- _Generated by [Claude Code](https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Refs #11347
Clause-②: yes (narrowing)
This PR is the
@object-ui/componentspreparation unit of objectui#8347. objectui#8347 is the parent: it removesBaseSchema's index signature, and it lands last. This PR settles the twelve un-cast reads in eight renderers that objectui#8347's P1 mutation reddens, measuring each key on its own:list.title(two reads), is kept and declared.The Clause-② arm covers both directions:
inputsand the keys the renderers honour.ListSchema.titledeclaration widens the TypeScript face. On the zod mirror it newly refuses a non-stringtitle.64 of the package's 94 test errors are retyped to their concrete node types. The other 30 stay:
Refs.Round 2 answers the at-tier record (FAIL, one item) and the seat's ruling on the card. My census missed a producer of
list.title: the publishedskills/objectuiexpressions guide (guides/schema-expressions.md, underos:check). It authors{ "type": "list", "title": "Team", ... }, and it saystitleis read off the node. Under the card's rule 2 the seat ruled to keep the read and declare the key, by 「未声明键随实现,文档随之」.listhas no spec row, the renderer readstitle, and the registration publishes it. Nothing underskills/changes.This body was refreshed at 2026-10-01T09:41Z. It answers every comment posted on this PR since the first body:
5927953448, in this section;changeset-claim-re-readcomment5927656616, under Files outside the claimed surface and Gates;5927699774,5928383897,5928726380). They report PASS and ask nothing.Per-key verdicts
Spec row. The installed
@objectstack/specis 17.5.0. ItsComponentPropsMaphas 55 entries, and none of them is any of the eight node types. A grep of the whole installed package forshouldScaleBackground,showClose,input-otpanddropdown-menureturns zero hits. The controlmodalCloseButtonhits three files, so the grep was reading.Producers. The census covered:
examples/schema-catalog, every entry of these eight node types, key by key;examples/*andcontent/docs/**;packages/types/examples;skills/;origin/main2742e5370, its examples and packages.One producer exists: the
skills/objectuiexpressions guide authorstitleon alistnode. This was missed in round 1 and caught by the at-tier record. None of the producers authors any of the other eight read keys. The objectstack hits fortype: 'list'andtype: 'drawer'are page types, flow variables and a form-view type, not component nodes. The registrations name these keys in their owninputs/defaultProps. None of the eight types is inPUBLIC_BLOCKS, so those names never reach the publishedsdui.manifest.json.Runtime. Each key was rendered through the real
SchemaRendererand registry, before and after, in a scratch probe that is not committed.SchemaRenderer, before then afterlisttitle(2)ListSchema.title?: stringon both facesloadingtext(2)LoadingSchema.label;textretired with no aliastext: a message, then none.label: none, then a messageinput-otpmaxLength(TS2551)InputOTPSchema.lengthlength: 4: 6 slots, then 4.length: 8: 6, then 8.maxLength: 4: 4, then 6sidebar-menu-buttonactive(read offBaseSchemaitself; no node type exists)data-active="true", then"false"drawerfooterdrawershowClosedrawershouldScaleBackgroundfalseboth before and after, through the prop spreaddropdown-menu,popover,sheetmodal(1 each)list.titleis declared on both faces at once, so nozod-mirror-parityrow opens. A non-stringtitlethat.passthrough()used to keep unjudged is now refused attitle. That follows thewrapperClassprecedent from objectui#7722.H3: the near spellings. For
input-otp, all five catalog entries and the docs page spell the keylength. The type declareslengthon both faces with@default 6, so the read moved and the declaration stayed. Before this change, the published 4-digit and 8-digit examples drew six slots. Forloading,LoadingSchema.labelwas declared but inert while an undeclaredtextwas read. The seat accepted moving the read tolabelover a plain retirement.Inputs. Where a registration published a retired key as an input, the input goes too:
activeonsidebar-menu-button;drawerkeys;modalonpopoverand onsheet.dropdown-menunever publishedmodal.sheet'sdefaultPropsalso loses itsmodalseed.list'stitleinput stays.An independent instrument.
pnpm check:component-surface-parity(objectui#4631, report-only) was read at base770cc5ba4in a detached control worktree, and again at the head:interface-missing-keyfalls from 59 to 49. The ten rows that go are exactly these keys;list.titlegoes because it is declared now.interface-extra-keyfalls from 121 to 120, becauseinput-otp.lengthis now read.P1 measurement (H1)
The un-chained recipe is
tsc --noEmit, thentsc -p tsconfig.test.json. It ran with objectui#8347's mutation applied throughablation-replace, and with@object-ui/typesrebuilt. The restore was proven after every run:git diff HEADonbase.tsis empty;770cc5ba4, mutated61be40730, mutatedlistreads compile againstListSchema.titleThe tests (H4)
64 literals are now typed as the nodes they are, each through a small typed identity per file (
actionBar,textNode,selectNode, ...). Excess-property checking therefore runs against the concrete type, not againstBaseSchema. The helpers are not generic, so they cannot widen anything. The button variant and size tables and the inputinputTypetable are typed with the declared unions.list-title-declared-11347.test.tsxpinslist.titleon three readers:SchemaRenderer, drawsTeamas the heading. A list with no title draws none.title.@ts-expect-errorsits on a numerictitle, andtsc -p tsconfig.test.jsonreads it.The 30 left, by file:
form-renderers.test.tsx, wherelabelauthors its retiredcontentspelling on purpose.propertiesbag ofelement:text,element:text_inputandelement:record_picker:page-variables.test.tsx(5, one of themdataSource)text-input.test.tsx(5)text-input-description-association.test.tsx(1)required-marker-markup.test.tsx(1)record-picker-label-association.test.tsx(1)itemsonpage:tabs:page-tabs-builtin-label-i18n-4645.test.tsx(2) andchrome-i18n-no-provider-fallback.test.tsx(1).action:button:action-params-templates-7867.test.tsx(5).homewithkind:html-page-lazy-blocks.test.tsx(1) andreact-page-scope.test.tsx(1).propsalias:alias-precedence-cross-channel.test.tsx(4).properties:degenerate-config-bag.test.tsx(2).Files outside the claimed surface
Each was forced by a verdict, and the at-tier record judged each one.
packages/types/src/crud.ts: theCRUDDialogSchema.showClosedocblock no longer names the drawer's retired look-alike read as live.packages/types/src/__tests__/layout-default-jsdoc-7361.test.ts: the test now pins that retirement, with a firing control.packages/components/src/renderers/__tests__/node-slot-numeric-falsy.test.tsx: thedrawer footerrow is removed..changeset/8318-jsdoc-default-tag-corrections.md: a dated correction is appended, and the frontmatter is byte-identical.changeset-claim-re-readflaggedrenderers/overlay/drawer.tsx:38in this changeset. That address sits in the original bullet, inside the append-only prefix.33f4a1980, the commit that added this changeset. I read that tree through a REST contents read, and line 38 there is thedrawerregistration'sshowCloseread. The correction also names that read by what it is.origin/main(cmp -n 2879exits 0). The gate still reports the prefix occurrence, which is report-only.The branch merges cleanly with
origin/main(git merge-treeexit 0).Gates
The PR head is
57c757ffa. It differs from61be40730by one.changeset/*.mdfile and touches no TypeScript program.61be40730:pnpm --filter '@object-ui/components^...' buildexits 0.57c757ffa: the scopedturbo run buildthatcheck:skill-examples --build-filternames succeeds on all 29 tasks.61be40730,pnpm --filter @object-ui/types type-checkandpnpm --filter @object-ui/components type-checkboth exit 0, with 0error TSlines. That run also consumes the new pin's@ts-expect-error.61be40730:vitest run packages/types/+ the new pin passed 310 files and 7822 tests.57c757ffa:vitest run packages/components/gave 349 files passed and 1 skipped (350); 3481 tests passed and 24 skipped; the lock reported VERDICT command-exit 0.bba957503:examples/schema-catalog/+widget-dom-leak-sweep+registry-inputs-spec-paritypassed 38 files and 2677 tests.check:skill-examplesat57c757ffaexits 0 after the scoped build: 18 of 18 ts fences are judged with 0 failed, 70 json fences parse with 0 failed, and the gate reports "Every marked skill example holds up against the built types."check:new-line-citations,check:control-bytes,check:changeset-claims(report-only; see the note below),check:pending-changeset-literals,check:doc-types,check:doc-fences,check:doc-example-ids,check:component-surface-parity,check:test-path-roots,check:vi-mock-specifiers,check:phantom-deps,check:self-import,check:prompt-keys,check:handler-key-reads,check:skills-paths, changeset presence and no-major.check-governed-queue-guard --testreads NOT GOVERNED.list.tsx,drawer.tsx,data-display.ts,data-display.zod.ts). There are 13 of them: 5926, 6349 batch 1, 6881, 6940, 6951, 6972, 7352, 7694, 7722, 8331, 8478, 9256-list, and the 5350 table one. 8318 is flagged separately, by its line address. None is made false by this branch except 8318, which carries the correction.listrenderer readswrapperClassand that the key was undeclared; both still hold.body/childrenread files underListSchema; that still holds..describe()strings gain no line address..ts/.tsxfiles gave 0 errors and 73 warnings, against the same 0 and 73 for those files at base. Type-aware linting is off and no custom rule reads the disk, so the narrowed run speaks for the untouched files too. Round 2 adds one test file, which CI'sLintreads.Acceptance notes (observed, not filed)
SchemaRendererstill spreads unread node keys onto the component. An authoredmodalorshouldScaleBackgroundtherefore still reaches the Radix or vaul root. An authoredtitleon a list also lands as a DOMtitleattribute on theol/ul, andtext/labelonloadingland on its wrapper. These belong to the strict face (objectui#8345, objectui#11070) and to the bare-spread family.DialogSchemaandContextMenuSchemadeclaremodal, andSheetSchemaandDialogSchemadeclarefooter. Rule 3 retires the same keys on these four nodes, because no producer authors them there.LoadingSchema.size. The declared union is'sm' | 'default' | 'lg', and the renderer branches onsm/md/lg/xl. NOT MEASURED, carried for the next touch.input-otpseparatordraws nothing. It is filed as objectui#11365.Session:
https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERTGenerated by Claude Code