Repository navigation
fix(sdui-parser,components,layout,types): containment is the declared children slot, not isContainer (objectui#9910) - #10267
Conversation
… `children` slot, not `isContainer` (objectui#9910)
`validateTree`'s `not-a-container` reads exactly one declaration: an input
named `children` in the registration `inputs` (`acceptsChildren`,
`CHILD_LIST_KEY` exported). No `isContainer` fallback; the retired `body`
dialect follows the same predicate. Every renderer under components + layout
that puts `schema.children` on the page declares `{ name: 'children', type:
'slot' }` (65 measured bare, plus `sidebar` / `sidebar-menu-button` in their
provider context). `isContainer` is kept and re-described as LAYOUT
containment on every published face. The runtime census is re-pointed from
the flag to the input in both directions, with named context probes; the
containment ledger is at zero.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BP8CMtACxTdLjqR6rhd33C
…ldren-slot-declare-and-pin
|
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
|
|
Red: Whose it is: this PR's. It is not red on the base branch, and the census reads a file this diff touches. What happens next: this PR is being superseded. One of its commits carries a co-author trailer naming a model, and the squash would append that trailer to Generated by Claude Code |
|
Superseded by objectui#10270. objectui#10270 carries the identical tree over this PR's 42 paths. The seat verified that the only byte difference from this PR's head Generated by Claude Code |
… `children` slot, not `isContainer` (objectui#9910, re-cut) (objectstack-ai#10270) Part of objectstack-ai#9910 — the code half of the maintainer ruling (Q1-A declare-and-pin, Q2-A re-description, Q3-A same release). It does not close the card on merge: the `skills/objectui/**` wording for Q2 is a governed surface and rides its own Tier H draft PR on branch `claude/issue-9910-skills-iscontainer-wording`, and the objectstack save-gate port is objectstack#19969 (Blocked-by this card). The seat marks the card done once both objectui PRs have landed; objectstack-ai#9910 stays open until then. **Supersedes objectui#10267:** same tree, re-cut as one commit (`c80eac97f` on branch `claude/issue-9910-children-slot-declare-and-pin-r2`, from `origin/main` at `88a4ef616`) because a commit there carried a model-named co-author trailer that the squash would have appended to `main`. Proof on the re-cut: the 42 changed paths are the same set, `git diff` against the superseded head over those paths is 0 bytes, and no file outside them differs from `origin/main`. The Verification section below is quoted verbatim from objectstack-ai#10267 and its head references name that PR's tree; the re-verification on this head is posted as an `os-dev-report` addendum on the card. objectstack-ai#10267 remains open for the seat to close as superseded. Clause-②: yes ## The ruling this executes (issue comment 5814512168, operative text quoted in full) > ## Q1 — **A: declare-and-pin** > Every renderer that reads `schema.children` declares `{ name: 'children', type: 'slot' }` in its registration `inputs`, the shape ten registrations already carry. The containment diagnostic in `packages/sdui-parser/src/validate.ts` reads **only** that input. ⛔ There is no fallback to `isContainer`. The existing runtime census is re-pointed from the flag to the input **in both directions** (declared ⇔ actually renders children). Its red-when-fixed pins flip to the new truth, and `container-declaration-baseline.json`'s `excluded` / `undeclared` rows are deleted as they clear. Scope: the ~67 registrations under `packages/components` + `packages/layout`, including `tooltip`, the five page kinds, `sidebar` and `sidebar-menu-button`. ⛔ B is not taken: it adds a published derived field plus a DOM render step, and it is measured blind to context and portal registrations. > > ## Q2 — **A: `isContainer` means layout container** > Keep the field. Re-describe it on every published face (`ComponentMeta.isContainer`, `ComponentMetaSchema`, the plugin guide) as **layout containment**: excluded from the react-page scope and the public layout ledger, ⛔ no longer "can have children". `react-page.tsx`'s `buildComponentScope` keeps reading it. The `skills/**` wording is a governed surface and lands as its own Tier H PR. > > ## Q3 — **A: same release, same pin bump** > objectstack's save-gate copy of the parser moves in the same release and pin bump, so saving and rendering never disagree (objectstack#12719). The port card is filed in this stroke, carrying `Blocked-by: objectstack-ai#9910`. > > **Confidence gap carried to the claim:** measure the effect on `registry-inputs-spec-parity` and objectstack `check:react-declaration-parity` first. ## What changed **The tier (`@object-ui/sdui-parser`).** `validateTree`'s containment branch reads one declaration — `acceptsChildren(comp)`, true iff `comp.inputs` carries an entry named `children` — and nothing else; `isContainer` is not consulted and is not a fallback. `checkRetiredBodyDialect` (the retired `body` spelling, second containment site) is handed the same predicate instead of `comp.isContainer`, so the two spellings cannot disagree (assumption A3, see below). New exports: `CHILD_LIST_KEY` (`'children'`) and `acceptsChildren`. `ManifestComponent.isContainer` / `RegistryConfigLike.isContainer` are re-described as layout containment and still projected into the manifest (the objectstack save gate reads it there until objectstack#19969 lands). **The declarations (`@object-ui/components`, `@object-ui/layout`).** Every renderer that puts `schema.children` on the page now declares `{ name: 'children', type: 'slot' }`: - the 34 flow/inline HTML tags of `basic/html-elements.tsx` (declared inside the factory, skipped for the three void tags `img` / `hr` / `br`); - the seven sectioning tags of `layout/semantic.tsx`; - `aspect-ratio`, `box`, `card`, `container`, `flex`, `grid`, `stack`; - `alert`, `badge`, `button`, `toggle`, `span` (label / description / value fallbacks), `div`, `form`, `scroll-area`; - the ten `sidebar-*` parts that render children (`sidebar-trigger` renders none and declares none); - `page-header` in `@object-ui/layout`. That is 65 registrations measured bare plus `sidebar` and `sidebar-menu-button`, which only mount inside `sidebar-provider` — 67, the ruling's number. `tooltip`, the four `page:*` containers and the five page kinds already carried the input. No `isContainer` value moved anywhere. **The published descriptions (`@object-ui/types`, docs).** `ComponentMeta.isContainer`, `WidgetManifest.isContainer` and `ComponentMetaSchema`'s `describe` now say layout containment (react-page scope, public layout ledger), not "can have children", and point at the slot. `content/docs/guide/react-pages.md` and `content/docs/guide/component-registry.md` say the same. `react-page.tsx`'s `buildComponentScope` is untouched (assumption A4). **The census.** `container-declaration-ratchet.test.tsx` is re-pointed from the flag to the input in both directions over every known key (namespaced spellings included, since `sidebar` and `page:sidebar` are different registrations): renders ⇒ declares; declares ⇒ renders, with named context probes for `sidebar`, `sidebar-menu-button` (inside `sidebar-provider`) and `tooltip` (open, read from the portal), each pinned as still needed; and `not-a-container` fires on exactly the undeclared, whatever the flag says — `page:tabs` / `page:accordion` (flagged, no slot) keep the TRUE warning, `button` (slot, no flag, public) draws none. A layout-side census (`packages/layout/src/__tests__/containment-declared-slot-9910.test.tsx`) holds the same contract for that package's five registrations. The red-when-fixed pins on the objectui#6804 population flipped to the new truth, `scripts/container-declaration-baseline.json` is at zero rows (the file stays as the ruled admission ledger, with its note rewritten), and `packages/sdui-parser/src/__tests__/containment-declared-slot-9910.test.ts` pins the tier's 2×2 (flag × slot) reading of a manifest. **Fixture triage.** Parser fixtures that nested children under an `isContainer: true` manifest entry now declare the slot too (`compile`, `render`, `type-attribute-collision-7235`, `body-dialect-6771`, `verify.ts`); the `layout-containers-declare-containment` childless control moved from `badge` (which now declares the slot it renders) to `tabs`; the layout `page-header-authorable-keys` dialect guard licenses `children` by name as the protocol's base key (`PROTOCOL_LICENSED`, read from `CHILD_LIST_KEY`) and asserts the spec does not carry it. ## Measurements (the PM's assumptions, each taken before editing) - **A1 — the ruling's confidence gap.** objectui `registry-inputs-spec-parity`: of the registrations that render children, the only ones in the gate's `covered` set are `page:card` / `page:footer` / `page:section` / `page:sidebar`, all of which already declared `children` and whose spec shapes carry it (`specCoveredRenders` in the probe) — the forward direction is unaffected; measured by running the test after the change: green (in the 21-file / 498-test run and again on the final head). objectstack `check:react-declaration-parity` judges the `REACT_BLOCKS` and `object-*` blocks only, none of which renders children; measured by dumping the public manifest from this tree before and after (the way `apps/console/dev/manifest-dump.tsx` builds it) and running the gate read-only against each from a scratchpad shared clone of objectstack at `2c1011b` with `@objectstack/spec`'s deps installed (the shared checkout has no `node_modules`): control exit 0, after exit 0, output byte-identical, "no new DECLARATION divergence vs accepted baseline". Manifest delta: exactly nine public blocks gain a `children` input — `flex`, `grid`, `stack`, `card`, `container`, `box`, `badge`, `alert`, `button`; 59 keys before and after; zero `isContainer` values changed. A `slot` input emits no JSX attribute (`emitInterface` filters slot arms), so `sdui-intrinsics.d.ts` is unchanged. ⇒ neither gate goes red; no fork to report. - **A2 — the population.** A temporary full-registry probe (16 console modules + `@object-ui/layout`, 498 known keys, never committed) at the base: 144 keys render an authored child (bare + `ui:` twins), 16 declare the input; bare violators = 65 distinct registrations; `sidebar` and `sidebar-menu-button` render only inside the provider; `tooltip` renders only open, in a portal. After the change: violators = 0, the six context/portal keys are the only declared-but-bare-silent ones and each renders in its context, `flaggedNotRendering` = `page:tabs`, `page:accordion`, `responsive-grid`, `app-schema-renderer`, `layout:page:card` (layout containers that render a React `children` prop or `items[].children`, never `schema.children` — they now draw the TRUE diagnostic the flag used to silence). - **A3 — the second `validate.ts` site.** Read as covered by "reads only that input": the body-dialect check emits the same `not-a-container` code for a `body` list, so leaving it on the flag would make the two spellings disagree about which components take a list. Both sites read `acceptsChildren(comp)`; the parameter is renamed `acceptsChildren`. Recorded under Deviations. - **A4 — react-page.** `buildComponentScope` still reads `cfg.isContainer`; not edited. `react-page-scope.test.tsx` and `public-contract.test.ts` pass unchanged. - **Falsified detail in the brief:** `packages/core/src/registry/Registry.ts` has zero `isContainer` hits on `origin/main` (`grep -c` = 0); the description faces are `packages/types/src/base.ts`, `packages/types/src/widget.ts` and `packages/types/src/zod/base.zod.ts`, which is where the re-description landed. Nothing in `packages/core` needed to change. ## Verification (exit codes read from files, never from a pipe) Every vitest / build / type-check run went through `scripts/pm/os-verify-lock.sh` (slot `issue-9910-dev`) and reads `VERDICT command-exit 0`. Final head of this branch: `f34809934` (a merge of `origin/main` at `fa5fbd9dc`; the eight newer files on `origin/main` since then touch none of this surface). | check | reading | |---|---| | `pnpm exec vitest run packages/sdui-parser/` + ratchet, census, layout-containers, page-container, react-page-scope, registration-uniqueness, layout page-header + containment census, examples pageheader-with-actions | exit 0 — 26 files, 335 tests passed | | console `registry-inputs-spec-parity`, `htmlTierDataSourceInputDeclaration-6678`, `public-contract`; components record-picker / toast-sonner / action-type html-tier pins; types `zod-mirror-parity`, `component-meta-single-declaration`, `default-children-retired-contract-twins`; `packages/core/src/registry/`; kanban / detail / grid manifest pins | exit 0 — 21 files, 498 tests passed | | re-run on the final head `f34809934`: ratchet, census, layout census, layout page-header, parser 2×2 pin, console parity | exit 0 — 6 files, 271 tests passed | | `turbo run build --filter=@object-ui/layout^... --filter=@object-ui/sdui-parser --concurrency=2` | exit 0 — 8 tasks successful | | `pnpm --filter @object-ui/{sdui-parser,types,layout,components} type-check` (script name echoed as `type-check`; `tsconfig.test.json` includes `src/**/*.test.tsx`, so the new tests are in the program) | exit 0, 0, 0, 0 | | `eslint --no-inline-config` over the 38 touched `.ts` / `.tsx` files | exit 0 — 0 errors, 141 pre-existing `no-explicit-any` warnings | | `check-changeset-presence` (35 published source files, 4 released packages, 1 changeset) · `check-changeset-no-major` · `check-control-bytes` · `check-new-cross-file-line-citations` (0 new) · `check-governed-queue-guard --test` over the 42 paths (NOT GOVERNED) · `check-registry-bare-name-collisions` · `check-test-path-roots` · `check-pending-changeset-literals` · `check-changeset-claims` · `check-doc-fence-languages` · `check-doc-component-types` | exit 0 each | | objectstack `check:react-declaration-parity --baseline --strict` against the BASE manifest (control) and the AFTER manifest | exit 0 and exit 0, identical output | | NOT MEASURED locally, declared to CI: `check:sdui-registration-pins` (needs a console build), the full `pnpm lint` and `pnpm test` farm, `dispatch-gates.mjs` (refuses this repo; the list above is hand-derived from `package.json` and `.github/workflows/`) | — | **Reverse verification (type level, consumer side).** After the build, the layout test that imports `CHILD_LIST_KEY` was mutated by appending `const __ablationBad: number = CHILD_LIST_KEY;` (marker count 0 → 1 on disk), `pnpm --filter @object-ui/layout type-check` went red with `TS2322: Type 'string' is not assignable to type 'number'` at the mutated line (exit 2), and the file was restored with `git checkout HEAD -- FILE`: marker count back to 0, `git hash-object` equal to the HEAD blob `cddf5764…`, `git diff HEAD` empty. This proves the consumer type-check reads the rebuilt `sdui-parser` `.d.ts` (an old one has no such export). The runtime direction is the census itself: the flag-only rows (`page:tabs`, `page:accordion`, `responsive-grid`) draw the diagnostic, which the previous predicate could not produce. ## Deviations - Commit `748fc2484` carries a `Co-Authored-By` trailer with a model name, written from the harness attribution reminder before the role file's model-free rule was applied; force-push is forbidden in this repo, so the history is not rewritten and it is reported here. The later commits use a model-free trailer; the squash merge writes this PR's title and body. - A3: both `validate.ts` containment sites re-pointed together (the body-dialect parameter renamed `acceptsChildren`), reading "reads only that input" as covering the retired spelling's containment sentence. - The five page kinds keep `{ name: 'children', type: 'array', itemType: 'component' }` — the predicate is name-based, the ruling counted them among the ten that already carry the declaration, and the parser pin asserts an `array`-typed `children` counts. - `scripts/container-declaration-baseline.json` is kept at zero rows rather than deleted (the ruling says rows are deleted as they clear); its note is rewritten to the new predicate and the census asserts the note's contract phrases. - Two new published exports on `@object-ui/sdui-parser` (`CHILD_LIST_KEY`, `acceptsChildren`) so the census, the layout dialect guard and the objectstack port read one spelling of the predicate; `Clause-②: yes` already covers a published-type change. - `checkRetiredBodyDialect`'s fourth parameter narrows from `boolean | undefined` to `boolean` (it is always handed the predicate's boolean now). - `Part of objectstack-ai#9910`, not a closing keyword: the governed half and the objectstack port remain. ## Acceptance notes (observed, not filed — no class a/b/c finding) - `containers.tsx`'s `page:section` / `page:footer` / `page:sidebar` still read `schema?.children || schema?.body`; the read-site comment records it as a back-compat read for stored documents, not an authorable key. Unchanged here. - `codegen.ts`'s `generateBlockList` "container" column still reads `isContainer`; under Q2 that column now literally means layout container. Unchanged here. - `check-skill-examples` for the governed PR answers PRECONDITION NOT MET (exit 2, needs a 12-package scoped build) in the skills worktree — declared to that PR's CI, not a red. - Dedupe words, should the seat want a card on the first two: `page-container body read` · `generateBlockList container column` · `isContainer layout`. --- _Generated by [Claude Code](https://claude.ai/code/session_01BP8CMtACxTdLjqR6rhd33C)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Part of #9910 — the code half of the maintainer ruling (Q1-A declare-and-pin, Q2-A re-description, Q3-A same release). It does not close the card on merge: the
skills/objectui/**wording for Q2 is a governed surface and rides its own Tier H draft PR on branchclaude/issue-9910-skills-iscontainer-wording, and the objectstack save-gate port is objectstack#19969 (Blocked-by this card). The seat marks the card done once both objectui PRs have landed; #9910 stays open until then.Clause-②: yes
The ruling this executes (issue comment 5814512168, operative text quoted in full)
What changed
The tier (
@object-ui/sdui-parser).validateTree's containment branch reads one declaration —acceptsChildren(comp), true iffcomp.inputscarries an entry namedchildren— and nothing else;isContaineris not consulted and is not a fallback.checkRetiredBodyDialect(the retiredbodyspelling, second containment site) is handed the same predicate instead ofcomp.isContainer, so the two spellings cannot disagree (assumption A3, see below). New exports:CHILD_LIST_KEY('children') andacceptsChildren.ManifestComponent.isContainer/RegistryConfigLike.isContainerare re-described as layout containment and still projected into the manifest (the objectstack save gate reads it there until objectstack#19969 lands).The declarations (
@object-ui/components,@object-ui/layout). Every renderer that putsschema.childrenon the page now declares{ name: 'children', type: 'slot' }:basic/html-elements.tsx(declared inside the factory, skipped for the three void tagsimg/hr/br);layout/semantic.tsx;aspect-ratio,box,card,container,flex,grid,stack;alert,badge,button,toggle,span(label / description / value fallbacks),div,form,scroll-area;sidebar-*parts that render children (sidebar-triggerrenders none and declares none);page-headerin@object-ui/layout.That is 65 registrations measured bare plus
sidebarandsidebar-menu-button, which only mount insidesidebar-provider— 67, the ruling's number.tooltip, the fourpage:*containers and the five page kinds already carried the input. NoisContainervalue moved anywhere.The published descriptions (
@object-ui/types, docs).ComponentMeta.isContainer,WidgetManifest.isContainerandComponentMetaSchema'sdescribenow say layout containment (react-page scope, public layout ledger), not "can have children", and point at the slot.content/docs/guide/react-pages.mdandcontent/docs/guide/component-registry.mdsay the same.react-page.tsx'sbuildComponentScopeis untouched (assumption A4).The census.
container-declaration-ratchet.test.tsxis re-pointed from the flag to the input in both directions over every known key (namespaced spellings included, sincesidebarandpage:sidebarare different registrations): renders ⇒ declares; declares ⇒ renders, with named context probes forsidebar,sidebar-menu-button(insidesidebar-provider) andtooltip(open, read from the portal), each pinned as still needed; andnot-a-containerfires on exactly the undeclared, whatever the flag says —page:tabs/page:accordion(flagged, no slot) keep the TRUE warning,button(slot, no flag, public) draws none. A layout-side census (packages/layout/src/__tests__/containment-declared-slot-9910.test.tsx) holds the same contract for that package's five registrations. The red-when-fixed pins on the objectui#6804 population flipped to the new truth,scripts/container-declaration-baseline.jsonis at zero rows (the file stays as the ruled admission ledger, with its note rewritten), andpackages/sdui-parser/src/__tests__/containment-declared-slot-9910.test.tspins the tier's 2×2 (flag × slot) reading of a manifest.Fixture triage. Parser fixtures that nested children under an
isContainer: truemanifest entry now declare the slot too (compile,render,type-attribute-collision-7235,body-dialect-6771,verify.ts); thelayout-containers-declare-containmentchildless control moved frombadge(which now declares the slot it renders) totabs; the layoutpage-header-authorable-keysdialect guard licenseschildrenby name as the protocol's base key (PROTOCOL_LICENSED, read fromCHILD_LIST_KEY) and asserts the spec does not carry it.Measurements (the PM's assumptions, each taken before editing)
registry-inputs-spec-parity: of the registrations that render children, the only ones in the gate'scoveredset arepage:card/page:footer/page:section/page:sidebar, all of which already declaredchildrenand whose spec shapes carry it (specCoveredRendersin the probe) — the forward direction is unaffected; measured by running the test after the change: green (in the 21-file / 498-test run and again on the final head). objectstackcheck:react-declaration-parityjudges theREACT_BLOCKSandobject-*blocks only, none of which renders children; measured by dumping the public manifest from this tree before and after (the wayapps/console/dev/manifest-dump.tsxbuilds it) and running the gate read-only against each from a scratchpad shared clone of objectstack at2c1011bwith@objectstack/spec's deps installed (the shared checkout has nonode_modules): control exit 0, after exit 0, output byte-identical, "no new DECLARATION divergence vs accepted baseline". Manifest delta: exactly nine public blocks gain achildreninput —flex,grid,stack,card,container,box,badge,alert,button; 59 keys before and after; zeroisContainervalues changed. Aslotinput emits no JSX attribute (emitInterfacefilters slot arms), sosdui-intrinsics.d.tsis unchanged. ⇒ neither gate goes red; no fork to report.@object-ui/layout, 498 known keys, never committed) at the base: 144 keys render an authored child (bare +ui:twins), 16 declare the input; bare violators = 65 distinct registrations;sidebarandsidebar-menu-buttonrender only inside the provider;tooltiprenders only open, in a portal. After the change: violators = 0, the six context/portal keys are the only declared-but-bare-silent ones and each renders in its context,flaggedNotRendering=page:tabs,page:accordion,responsive-grid,app-schema-renderer,layout:page:card(layout containers that render a Reactchildrenprop oritems[].children, neverschema.children— they now draw the TRUE diagnostic the flag used to silence).validate.tssite. Read as covered by "reads only that input": the body-dialect check emits the samenot-a-containercode for abodylist, so leaving it on the flag would make the two spellings disagree about which components take a list. Both sites readacceptsChildren(comp); the parameter is renamedacceptsChildren. Recorded under Deviations.buildComponentScopestill readscfg.isContainer; not edited.react-page-scope.test.tsxandpublic-contract.test.tspass unchanged.packages/core/src/registry/Registry.tshas zeroisContainerhits onorigin/main(grep -c= 0); the description faces arepackages/types/src/base.ts,packages/types/src/widget.tsandpackages/types/src/zod/base.zod.ts, which is where the re-description landed. Nothing inpackages/coreneeded to change.Verification (exit codes read from files, never from a pipe)
Every vitest / build / type-check run went through
scripts/pm/os-verify-lock.sh(slotissue-9910-dev) and readsVERDICT command-exit 0. Final head of this branch:f34809934(a merge oforigin/mainatfa5fbd9dc; the eight newer files onorigin/mainsince then touch none of this surface).pnpm exec vitest run packages/sdui-parser/+ ratchet, census, layout-containers, page-container, react-page-scope, registration-uniqueness, layout page-header + containment census, examples pageheader-with-actionsregistry-inputs-spec-parity,htmlTierDataSourceInputDeclaration-6678,public-contract; components record-picker / toast-sonner / action-type html-tier pins; typeszod-mirror-parity,component-meta-single-declaration,default-children-retired-contract-twins;packages/core/src/registry/; kanban / detail / grid manifest pinsf34809934: ratchet, census, layout census, layout page-header, parser 2×2 pin, console parityturbo run build --filter=@object-ui/layout^... --filter=@object-ui/sdui-parser --concurrency=2pnpm --filter @object-ui/{sdui-parser,types,layout,components} type-check(script name echoed astype-check;tsconfig.test.jsonincludessrc/**/*.test.tsx, so the new tests are in the program)eslint --no-inline-configover the 38 touched.ts/.tsxfilesno-explicit-anywarningscheck-changeset-presence(35 published source files, 4 released packages, 1 changeset) ·check-changeset-no-major·check-control-bytes·check-new-cross-file-line-citations(0 new) ·check-governed-queue-guard --testover the 42 paths (NOT GOVERNED) ·check-registry-bare-name-collisions·check-test-path-roots·check-pending-changeset-literals·check-changeset-claims·check-doc-fence-languages·check-doc-component-typescheck:react-declaration-parity --baseline --strictagainst the BASE manifest (control) and the AFTER manifestcheck:sdui-registration-pins(needs a console build), the fullpnpm lintandpnpm testfarm,dispatch-gates.mjs(refuses this repo; the list above is hand-derived frompackage.jsonand.github/workflows/)Reverse verification (type level, consumer side). After the build, the layout test that imports
CHILD_LIST_KEYwas mutated by appendingconst __ablationBad: number = CHILD_LIST_KEY;(marker count 0 → 1 on disk),pnpm --filter @object-ui/layout type-checkwent red withTS2322: Type 'string' is not assignable to type 'number'at the mutated line (exit 2), and the file was restored withgit checkout HEAD -- FILE: marker count back to 0,git hash-objectequal to the HEAD blobcddf5764…,git diff HEADempty. This proves the consumer type-check reads the rebuiltsdui-parser.d.ts(an old one has no such export). The runtime direction is the census itself: the flag-only rows (page:tabs,page:accordion,responsive-grid) draw the diagnostic, which the previous predicate could not produce.Deviations
748fc2484carries aCo-Authored-Bytrailer with a model name, written from the harness attribution reminder before the role file's model-free rule was applied; force-push is forbidden in this repo, so the history is not rewritten and it is reported here. The later commits use a model-free trailer; the squash merge writes this PR's title and body.validate.tscontainment sites re-pointed together (the body-dialect parameter renamedacceptsChildren), reading "reads only that input" as covering the retired spelling's containment sentence.{ name: 'children', type: 'array', itemType: 'component' }— the predicate is name-based, the ruling counted them among the ten that already carry the declaration, and the parser pin asserts anarray-typedchildrencounts.scripts/container-declaration-baseline.jsonis kept at zero rows rather than deleted (the ruling says rows are deleted as they clear); its note is rewritten to the new predicate and the census asserts the note's contract phrases.@object-ui/sdui-parser(CHILD_LIST_KEY,acceptsChildren) so the census, the layout dialect guard and the objectstack port read one spelling of the predicate;Clause-②: yesalready covers a published-type change.checkRetiredBodyDialect's fourth parameter narrows fromboolean | undefinedtoboolean(it is always handed the predicate's boolean now).Part of #9910, not a closing keyword: the governed half and the objectstack port remain.Acceptance notes (observed, not filed — no class a/b/c finding)
containers.tsx'spage:section/page:footer/page:sidebarstill readschema?.children || schema?.body; the read-site comment records it as a back-compat read for stored documents, not an authorable key. Unchanged here.codegen.ts'sgenerateBlockList"container" column still readsisContainer; under Q2 that column now literally means layout container. Unchanged here.check-skill-examplesfor the governed PR answers PRECONDITION NOT MET (exit 2, needs a 12-package scoped build) in the skills worktree — declared to that PR's CI, not a red.page-container body read·generateBlockList container column·isContainer layout.Generated by Claude Code