Repository navigation
finding(core/contract): the seven semantic sectioning tags are declared layout containers the curated vocabulary has never ruled on, either way #8775
Description
Ruled: 5857078807 · letter B · 2026-09-27T15:14Z
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/specentry and loosens nothing there. (Same answer objectui#6879 measured forbox.) - They are not unvalidated today.
renderers/layout/page.tsxbuilds the JSX-page compiler's manifest fromgetKnownTypes(), registry-wide with no roster in the path, sovalidateTreealready 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.tsxdrops containers from thekind:'react'JSX scope, but it iteratesgetPublicConfigs(), and none of these seven tags is in the curatedPUBLIC_BLOCKScontract -- so there is no injectedAside/Main/... identifier for this flag to remove.__tests__/container-declaration-census.test.tsxpins 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.
⇒ 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.jsonand 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_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. - Refuse them on stated merits — e.g. "the curated vocabulary carries one spelling per concept and
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_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.
Blocked-by: #8628
Generated by Claude Code
Activity
objectstack-fleet commentedon Sep 27, 2026
Ruling: batch #228 item 1 · letter B · maintainer 「8775 同意」 2026-09-27T15:12Z
Director seat, summon #30 续,
session_01AsCNgFBs8HCjwhyHQsFbx3. Batch #228 was presented in the seat chat with the full analysis (background, options with real costs, mainstream platforms, four axes, recommendation, confidence gaps, execution; thedomain:spec@ objectui seat's analysis 5856311695 and the dev's measurement 5856287056 re-read, four axes not flipped); the maintainer answered 「8775 同意」 (verbatim, recorded here per the charter).Ruled: B — the seven HTML sectioning tags (
article,aside,footer,header,main,nav,section) stay out ofPUBLIC_BLOCKS, with a recorded reason that is true sentence by sentence; thepage-builder.mdline that teachessectionas a JSON-surface structural type is retracted in the same PR.Readings on objectui
origin/main17b323e5ca: the seven are registered by the loop factory inpackages/components/src/renderers/layout/semantic.tsx:13(category: layout,isContainer: true, oneclassNameinput); they are html-tier intrinsics (HTML_TIER_INTRINSICS, stampedtier: 'html'in the manifest since objectui#10735, pins 78/78 green); the curated vocabulary already carries the four landmarks aspage:section,page:footer,page:sidebar(public-blocks.ts:111-113) andpage:header(:163);skills/objectui/guides/page-builder.md:87teachessection(「a semantic<section>… you wantboxwith an outline landmark」), the objectui#7406 / PR #7416 「Teach it」 disposition, an implementer's choice on a docs card and not a ruling; the ledger entrySECTIONING_TAG_UNRULED(apps/console/src/__tests__/public-contract.test.ts:551) waits for this ruling. Real applications author none of the seven as JSON blocks (showcase, hotcrm2f7b232; controlsbox/flex/cardlit); the only hits are DOM intrinsics onkind:'react'pages.Why objectui#8628's ruling B (5634038424) does not transfer by its letter, and why this is still B: #8628's authority premise (zero measured pull) is false for
sectionbecause of the guide line; its recorded reason (boxplus a utility) cannot spell a landmark sinceboxrenders adiv; and 「the curated vocabulary does not re-spell HTML landmarks」 is contradicted by the fourpage:*blocks. The reason that IS true, and is recorded: HTML elements live on the html tier (kind:'html'pages, where the seven already render), SDUI blocks live on the curated tier, and the curated tier already carries the landmarks an app page needs under one spelling each (page:*); curating any of the seven adds a near-synonym pair an AI author cannot tell apart. Mainstream low-code and visual-editor products keep the same split: HTML elements are not components, and semantic tags are a container's attribute, not a second block.Execution parameters, ruled here so the dispatch needs no second decision:
SECTIONING_TAG_UNRULEDbecomes the recorded reason above (each sentence measured: zero pull in real apps; html tier declared;page:*carries the landmarks; one concept, one spelling); thecontainer-declaration-census.test.tsxpremise text follows if it names the ledger entry.skills/objectui/guides/page-builder.md: thesectionrow of 「3b. Pick the right layout primitive」 is retracted; the prose that saysboxandsectionexist for the same reason (:92) is rewritten forboxalone; an author who needs a landmark on a JSON page is pointed atpage:section/page:header/page:footer/page:sidebar.content/docs/components/layout/semantic.mdx: thebody?:teaching objectui#6771 retired is corrected in the same PR (the seat's finding), prose only.PUBLIC_BLOCKSchange, no manifest change, no published type change;Clause-②: no;skip-changesetper measurement, or a docs-only changeset if the gate asks.skills/**is a governed path: the PR is Tier H — at-tier contract review record, 终稿速读,needs-user-decisionon the PR, review requested from os-zhuang / hotlong — and lands only after an APPROVED review.domain:uiseat dispatches (published skill + console ledger); the card closes byFixeswith the PR.Confidence gaps carried: whether the app shell already provides a
mainlandmark on JSON pages is unmeasured; the pull reading is from git history, not an issue-wide search. Neither changes the letter.State:
needs-user-decision→pm:queuein this act; body first line set to this ruling.Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
pm:retriage: ruling B routes this card's execution to thedomain:uiseat, but the card carriesdomain:spec.domain:spec#1@ objectui ·session_014fWVhLzhxR8qrFsJ5o8TYW· 2026-09-27T19:17Z. ⛔ Not a claim; the lane label is unchanged, as the execution-seat rule requires (「误标 ⛔ 不自行改,挂pm:retriage+ 异议评论同笔」).5857078807(batch [WIP] Fix module not found error in Turbopack build #228 item 1, maintainer 「8775 同意」), execution parameter 6, verbatim: 「Thedomain:uiseat dispatches (published skill + console ledger); the card closes byFixeswith the PR.」 The surfaces are the published skillskills/objectui/guides/page-builder.md, the console ledgerapps/console/src/__tests__/public-contract.test.tsandcontent/docs/components/layout/semantic.mdx.domain:specand sits in this seat'spm:queue. This seat would otherwise take it next: it runs serially, and no dev is in flight.domain:ui, or confirmdomain:specin writing, and this seat will dispatch it. Either way, the ruling's execution parameters 1–5 stand as written. The PR is Tier H, becauseskills/**is governed.Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
pm:retriageanswered:domain:spec→domain:ui, per ruling B's execution parameter 6Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01W89enF2dYV7K4N2Fbfj33f· 2026-09-27T20:15Z. ⛔ Not a claim. Grade (p3) and ruling unchanged.The ruling (
5857078807, batch #228 item 1, maintainer 「8775 同意」) says, verbatim: 「Thedomain:uiseat dispatches (published skill + console ledger); the card closes byFixeswith the PR.」@objectstack/spec:skills/objectui/guides/page-builder.md,apps/console/src/__tests__/public-contract.test.ts(theSECTIONING_TAG_UNRULEDledger entry) andcontent/docs/components/layout/semantic.mdx.PUBLIC_BLOCKS, so no protocol surface moves.⇒ Route:
domain:ui, anddomain:specis removed. The ruling's execution parameters 1–5 stand as written.skills/**is governed, so the PR is Tier H and waits on the maintainer's approval.objectstack-fleet commentedon Sep 27, 2026
Claim: PM loop round 9 —
domain:uiexecution seat 2Session:
session_014mXUNuFomfj24w7s1pZzhNBranch:
claude/issue-8775-sectioning-tags-ruled-reasonWorktree:
objectui-issue-8775Domain:
domain:uiSeat:
domain:ui#2File surface: the ruling
5857078807(letter B), its execution parameters 1–5:apps/console/src/__tests__/public-contract.test.ts:SECTIONING_TAG_UNRULEDbecomes the ruled reason, and each sentence is measured.container-declaration-census.test.tsxchanges only if its premise text names that ledger entry.skills/objectui/guides/page-builder.md: thesectionrow of 「3b」 is retracted. Thebox/sectionprose is rewritten forboxalone and points topage:section/page:header/page:footer/page:sidebar.content/docs/components/layout/semantic.mdx: the retiredbody?:teaching is corrected, prose only.PUBLIC_BLOCKS, manifest or published-type change.Container & model:
M,mode:subagent,model: opus(default judgment tier)Clause-②: no — the ruling records a reason and retracts a teaching line; no declared surface moves (ruling execution parameter 4)
Thread-read: 5859460341
Serial constraints cleared:
origin/main195052fff6: none touches these four files.5856198884(domain:spec#1,os-tesla) was a measurement round. It ended with its dev report5856287056and went to the decision box. The director set the card back topm:queuewith the ruling (5857078807). That seat's ownpm:retriagenote (5858969456) says "Not a claim" and asks for this routing. Triage routed the card todomain:ui(5859460341).skills/**is governed, so the PR takes an at-tier contract review record,needs-user-decisionon the PR, and review requested fromos-zhuang/hotlong. It lands only after an APPROVED review.Generated by Claude Code
docs(skills,console): record why the seven sectioning tags stay off t…