Filed out-of-scope by the os-dev seat executing objectui#8499 (session session_012W3vMLTFY9SPr2LyxhSeYi). ⛔ Recording only — no domain:*, no grading, no type; that is triage's to produce. ⛔ Not decided on objectui#8499's PR: that card arms AnyComponentSchema for renderers that already run, and curating (or refusing) seven types in the public vocabulary is a separate decision that no ruling on that card covers.
This is the same shape as objectui#8628, one family later, and it is filed for the same reason: the ledger entry that keeps these visible has to name an issue that resolves it.
How they arrived in the population, measured
The derived population objectui#6879 built for its forcing function is
LayoutSchema.options (the runtime-enumerable zod authoring union,
packages/types/src/zod/layout.zod.ts)
∩ ComponentRegistry meta isContainer === true
objectui#8499 adds SemanticElementSchema — one arm whose type is a z.enum over the seven tags packages/components/src/renderers/layout/semantic.tsx registers. Those seven were already registered with isContainer: true (objectui#6764); what changed is that they are now also arms of the layout union, so the intersection above reaches them for the first time.
Measured on this card's branch, the population goes from 7 members to 14:
| type |
in PUBLIC_BLOCKS? |
card · container · flex · grid · stack |
yes — the objectui#6764 control set |
box |
yes — curated by objectui#6879 |
aspect-ratio |
no — objectui#8628 |
article · aside · footer · header · main · nav · section |
no, and no card has ever said why |
Each of the seven registers category: 'layout', isContainer: true, and exactly one input (className).
Two further readings, so the decision is not taken on a guess:
- The spec-parity gate cannot see any of them.
Object.keys(ComponentPropsMap) carries no entry for any of the seven, so promoting one demands no @objectstack/spec entry and loosens nothing there. (Same answer objectui#6879 measured for box.)
- They are not unvalidated today.
renderers/layout/page.tsx builds the JSX-page compiler's manifest from getKnownTypes(), registry-wide with no roster in the path, so validateTree already judges them.
⚠️ Why promoting one is NOT a pure widening — the half that makes this a decision
⛔ CORRECTED 2026-09-09. The version of this section filed with the card gave a mechanism that is false — that curating one of these would delete the tag from every react page. It does not. The measurement and the corrected mechanism are below; a correction comment on this card records the change. ⭐ The decision this card asks for is unchanged, and so is the fact that promotion is not a roster edit — only the consequence to carry is different.
packages/components/src/renderers/layout/semantic.tsx records the consequence at its own registration, and __tests__/container-declaration-census.test.tsx pins the premise:
Safe on the second consumer, and that was MEASURED rather than assumed (objectui#6764): renderers/layout/react-page.tsx drops containers from the kind:'react' JSX scope, but it iterates getPublicConfigs(), and none of these seven tags is in the curated PUBLIC_BLOCKS contract -- so there is no injected Aside/Main/... identifier for this flag to remove. __tests__/container-declaration-census.test.tsx pins that premise, so promoting one of these into the public contract re-opens the question here instead of silently deleting a tag from every react page.
⚠️ That quote is accurate; the inference first drawn from it was not. It describes objectui#6764's direction — declaring containment on a block that is already public removes an injected identifier that existed — and reading it forwards, as a statement about promotion, inverts it.
⇒ Measured, on main:
| leg |
reading |
react-page.tsx:75-77 |
if (!tag || cfg.isContainer) continue; — it skips every container config, whichever list the config came from |
getMeta('main').isContainer / getConfig('main').isContainer |
true / true — the seven have carried the flag since objectui#6764 |
| injected identifiers today |
46 |
injected identifiers with main promoted (simulated) |
46 |
Main in either set |
absent from both; set difference [] |
ComponentRegistry references in packages/react-runtime/src |
0 (control: 21 React references in the same file) |
⇒ A lowercase main in a kind:'react' page is a DOM intrinsic before and after; nothing stops resolving, because there was never an injected identifier and promotion does not create one.
⇒ What promotion actually costs, and it is still enough to make this a decision rather than a roster edit:
- It widens the AI-authoring vocabulary,
sdui.manifest.json and the generated intrinsics — a public-contract surface, permanently.
- It turns
container-declaration-census.test.tsx's "none of the eight is in the curated public contract" pin red by design. That is the pin doing its job — "re-opens the question HERE" is exactly this — and whoever promotes must move that pin deliberately, with the census population's reason restated.
⛔ What it does not cost is any react page.
What the decision looks like
Two coherent answers, and this card needs only one of them chosen:
- Curate the family (or a named subset) into
PUBLIC_BLOCKS Tier B — accepting the vocabulary / manifest / intrinsics widening, and moving container-declaration-census.test.tsx deliberately rather than discovering it red. ⛔ No react-page.tsx change is required; that premise was the erratum.
- Refuse them on stated merits — e.g. "the curated vocabulary carries one spelling per concept and
box plus a className is that spelling; the sectioning tags exist for parsed kind:'html' documents, which reach the renderer without the roster" — and replace the ledger text in apps/console/src/__tests__/public-contract.test.ts with those merits.
Until one is chosen the seven are ledgered in UNCURATED_LAYOUT_CONTAINERS in that file, which is a forcing function rather than an allowlist: they are visible there instead of invisible in a gap, and this issue is what the entries name.
Reproduction
From the repo root, pnpm exec vitest run apps/console/src/__tests__/public-contract.test.ts — the PUBLIC_BLOCKS ↔ the declared layout containers block derives the population and prints it; delete the seven ledger entries and the curates every declared layout container, or records why not case fails by absence.
Generated by Claude Code
Filed out-of-scope by the
os-devseat executing objectui#8499 (sessionsession_012W3vMLTFY9SPr2LyxhSeYi). ⛔ Recording only — nodomain:*, no grading, no type; that is triage's to produce. ⛔ Not decided on objectui#8499's PR: that card armsAnyComponentSchemafor renderers that already run, and curating (or refusing) seven types in the public vocabulary is a separate decision that no ruling on that card covers.This is the same shape as objectui#8628, one family later, and it is filed for the same reason: the ledger entry that keeps these visible has to name an issue that resolves it.
How they arrived in the population, measured
The derived population objectui#6879 built for its forcing function is
objectui#8499 adds
SemanticElementSchema— one arm whosetypeis az.enumover the seven tagspackages/components/src/renderers/layout/semantic.tsxregisters. Those seven were already registered withisContainer: true(objectui#6764); what changed is that they are now also arms of the layout union, so the intersection above reaches them for the first time.Measured on this card's branch, the population goes from 7 members to 14:
PUBLIC_BLOCKS?card·container·flex·grid·stackboxaspect-ratioarticle·aside·footer·header·main·nav·sectionEach of the seven registers
category: 'layout',isContainer: true, and exactly one input (className).Two further readings, so the decision is not taken on a guess:
Object.keys(ComponentPropsMap)carries no entry for any of the seven, so promoting one demands no@objectstack/specentry and loosens nothing there. (Same answer objectui#6879 measured forbox.)renderers/layout/page.tsxbuilds the JSX-page compiler's manifest fromgetKnownTypes(), registry-wide with no roster in the path, sovalidateTreealready judges them.packages/components/src/renderers/layout/semantic.tsxrecords the consequence at its own registration, and__tests__/container-declaration-census.test.tsxpins the premise:⇒ Measured, on
main:react-page.tsx:75-77if (!tag || cfg.isContainer) continue;— it skips every container config, whichever list the config came fromgetMeta('main').isContainer/getConfig('main').isContainertrue/true— the seven have carried the flag since objectui#6764mainpromoted (simulated)Mainin either set[]ComponentRegistryreferences inpackages/react-runtime/srcReactreferences in the same file)⇒ A lowercase
mainin akind:'react'page is a DOM intrinsic before and after; nothing stops resolving, because there was never an injected identifier and promotion does not create one.⇒ What promotion actually costs, and it is still enough to make this a decision rather than a roster edit:
sdui.manifest.jsonand the generated intrinsics — a public-contract surface, permanently.container-declaration-census.test.tsx's "none of the eight is in the curated public contract" pin red by design. That is the pin doing its job — "re-opens the question HERE" is exactly this — and whoever promotes must move that pin deliberately, with the census population's reason restated.⛔ What it does not cost is any react page.
What the decision looks like
Two coherent answers, and this card needs only one of them chosen:
PUBLIC_BLOCKSTier B — accepting the vocabulary / manifest / intrinsics widening, and movingcontainer-declaration-census.test.tsxdeliberately rather than discovering it red. ⛔ Noreact-page.tsxchange is required; that premise was the erratum.boxplus aclassNameis that spelling; the sectioning tags exist for parsedkind:'html'documents, which reach the renderer without the roster" — and replace the ledger text inapps/console/src/__tests__/public-contract.test.tswith those merits.Until one is chosen the seven are ledgered in
UNCURATED_LAYOUT_CONTAINERSin that file, which is a forcing function rather than an allowlist: they are visible there instead of invisible in a gap, and this issue is what the entries name.Reproduction
From the repo root,
pnpm exec vitest run apps/console/src/__tests__/public-contract.test.ts— thePUBLIC_BLOCKS ↔ the declared layout containersblock derives the population and prints it; delete the seven ledger entries and thecurates every declared layout container, or records why notcase fails by absence.Generated by Claude Code