fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge - #6832

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key
Aug 30, 2026
Merged

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge#6832
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key

Conversation

@claude

@claudeclaudeBot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Fixes#6805
Fixes#6806

Folded deliberately: the two cards share a defect shape and not a repair key, and that
divergence is the point.

noderenderer readskey authored now
scroll-arearenderChildren(schema.children)children (7 nodes, the whole category)
badgeschema.label || renderChildren(schema.body)label (2 nodes)

⛔ A blind content to children sweep repairs #6805 and silently leaves both badges empty,
behind a green "no content left in the catalog" assertion. The shared method is author the
key this renderer actually reads
, never rename content to children.

⛔ Neither renderer was taught to read content. The renderer is the contract (AGENTS.md
#0.1), and a second dialect for one slot on a published surface is the opposite of the
repair. For badge, ⛔ not body either even though the renderer reads it: #6771 is
retiring body as a children dialect, and 31 badge nodes already author label.

What moved

  • examples/schema-catalog/src/schemas/components-complex-scroll-area/*.json — 7 files, root
    content becomes children. Nothing else in those files changed; the 167 nested content
    keys are all on text nodes and were left alone (see the exclusion list).
  • examples/schema-catalog/src/schemas/components-basic-sidebar/sidebar-with-badges.json
    the two badge nodes' content becomes label. Its five sibling text nodes still
    author content, untouched.
  • examples/schema-catalog/test/catalog-authored-key-6805-6806.test.tsx — new, 27 tests.
  • .changeset/6805-6806-catalog-authored-key.md — empty frontmatter (no release).

Measured through the real SchemaRenderer, the way the docs gallery renders:

entry before after
…scroll-area/tall-300px elements=5 leaked=1 text=(css) 36 0 …"Line 1Line 2Line 3"…
…scroll-area/chat-messages elements=5 leaked=1 text=(css) 81 0 …"AUser 1This is a…"…
…sidebar/sidebar-with-badges elements=22 leaked=2 22 0
text before "InboxDraftsSentTrashMain content area"
text after "Inbox12Drafts3SentTrashMain content area"

leaked counts elements carrying the authored value as the host attribute content — the
#5574 class. All seven scroll-area boxes drew nothing but Radix's own injected scrollbar
stylesheet before this change.

⑤ Exclusion list — named, and encoded in the test

text nodes authoring content are NOT members and were not touched.text.tsx
reads schema.content || schema.value, so they render correctly today.
sidebar-with-badges's own sibling text nodes prove it inside the very fixture #6806
repairs: one key, two renderers, opposite outcomes. A sweep that "fixed" them would break
working demos.

Corpus-wide, content is authored by: text 666 · untyped 30 · sheet 3 · markdown 3 ·
card 1 · tooltip 1 · hover-card 1
(walked over all 431 fixtures). ⛔ So the sweep was not
generalised to "every renderer" — a blanket rule reds on working fixtures. Whether it
generalises at all is #6810's open question; this PR covers only the two renderers these two
cards name.

The exclusion list is executable, not just prose. Three assertions carry it:

  • a text node authoring content draws its string (live control);
  • the corpus still authors content on 600+ text nodes (reads 0 if someone runs the
    blanket sweep);
  • inside both touched families, every remaining content key sits on a text node.

Also excluded: card nodes. One catalog card still authors content
(components-overlay-context-menu/basic-context-menu); that is #6788's fence, in flight on
its own branch, and sweeping it here would red this PR on someone else's work.

④ Per-member named assertions, and the ablation

Each member has its own named assertion naming its own key and asserting its own text
reaches the DOM. Ablated in three legs from the committed fix, reverting fixtures only —
git checkout HEAD -- ... restore proven by an empty git diff HEAD, and each mutation
proven on disk by counting the anchor text in both directions before running:

legfixtures revertedresult
Ascroll-area only (7 files)10 failed / 17 passed
Bbadge only (1 file)6 failed / 21 passed
Cboth15 failed / 12 passed
none (the PR)27 passed

⭐ The isolation is the whole argument for the fold: in leg A every badge assertion stays
green, and in leg B every scroll-area assertion stays green. Neither card's pin
covers the other. (A + B = 16, C = 15: the one overlap is the exclusion assertion, which
both legs break.)

Named failures in leg A — 7 of 10 are one per scroll-area member:

× no `scroll-area` node authors `content` any more — the defect these cards close
× every `scroll-area` node authors only keys `scroll-area.tsx` READS
× components-complex-scroll-area/chat-messages authors `children` (not `content`) and its own text reaches the DOM
× …/code-preview … × …/document-browser … × …/horizontal-scroll …
× …/short-150px … × …/tall-300px … × …/vertical-scroll …
× inside both touched families, every remaining `content` sits on a `text` node

Leg B, all 6 — two of them one per badge member:

× no `badge` node authors `content` any more — the defect these cards close
× every `badge` node authors only keys `badge.tsx` READS, except the #6829 ledger
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× the whole demo draws each count beside its own item — the card's acceptance criterion
× inside both touched families, every remaining `content` sits on a `text` node

⭐ The single most load-bearing assertion is the counter-probe named the WRONG
generalisation
: a badge repaired to children is measured to still draw an empty pill,
so the mistake this fold exists to prevent is now mechanically red rather than a comment.

Why the existing control did not catch either — two different holes

catalog-gallery-render.test.tsx renders both families today and passes. Its non-vacuity
control is elements > WRAPPER_ELEMENTS || text:

⚠️ Deliberately not addressed by raising a global non-vacuity threshold — that is out of
scope and would red on working fixtures. The per-renderer sweep is the right instrument, and
it is what both cards proposed themselves.

Deviation from the dispatch, stated rather than papered over

The dispatch asked to extend #6788's corpus sweep in
examples/schema-catalog/test/card-demo-content-6788.test.tsx. That file is not on
origin/main
— it exists only on the unmerged branch
claude/issue-6788-context-menu-demo-content. Creating it here would collide with that PR on
the same path. So the sweep was written as its own file in the same shape (the #6773/#6788
precedent), with a per-renderer table (RENDERERS) that a later PR can fold card into once
#6788 lands. The fold's stated purpose — one PR, one edit, no conflict between #6805 and
#6806 — is met: both renderers are swept in one file, once.

Out of fence, filed not fixed

#6829 — while censusing badge nodes to size this sweep, seven more were found
authoring children, which badge.tsx does not read (unlike card.tsx, which reads
children || body). Three demos in components-basic-span draw literally nothing;
core-schema-renderer/nested-schema-example silently drops two badges. That card is the same
class under a different key, and it is left open here for two reasons: the repair is a real
decision (re-author the fixtures to label, or widen the renderer's read set), and it is out
of this PR's fence. It is a fifth rediscovery of the class #6810 is deciding about, and
the first one the #5574 leak signature cannot find at all — those seven leak nothing.

Those seven are carried in this PR's badge sweep as an exact ledger
(BADGE_CHILDREN_LEDGER_6829), not a floor, so an eighth node turns the sweep red rather
than joining a growing allowance.

That census also measured a third hole in the same non-vacuity control, distinct from the
two above: an empty badge's own host element is the third element, so
elements=3 > WRAPPER_ELEMENTS=2 with text="" reads as drewSomething=true. Recorded on
#6829.

Verification

All of the below ran on a clean tree at ec115764f, this PR's final commit.

  • pnpm exec vitest run examples/schema-catalog/ from the repo root — 17 files, 1893
    tests, all passed
    . The 17 are the whole folder (vitest list enumerates them), and they
    include catalog-gallery-render.test.tsx and layout-dom-leak-5574.test.tsx, whose
    NODE_CENSUS pins the corpus node counts.
  • tsc --noEmit plus tsc -p tsconfig.test.json for the package — clean. --listFiles
    confirms 17 of 17 test files are in the type-check program, including the new one, so
    "typecheck clean" actually covers it.
  • check-control-bytesOK (scanned 5649 tracked text file(s)).
  • check-type-check-coverage41/41 packages compile their tests.
  • check-changeset-presenceNo source of a released package changed in this range, so no changeset is owed; a changeset with empty frontmatter is added anyway, declaring "no
    release" explicitly (objectui#3387's first-class form). check-changeset-no-major green.
  • regenerate:checkexamples/schema-catalog/src/index.ts is up to date (431 entries).
  • eslint: the repo-wide scan is CI's; this is a declared narrowing. eslint reads exactly
    1 of the 10 changed files (the other 9 are .json/.md and eslint itself reports them
    as ignored — that is eslint's own population, not an assumption), and that one file is
    0 errors, 0 warnings via --format json. No type-aware linting is configured
    (eslint.config.js declares no parserOptions.project or projectService), so this diff
    cannot move any rule's verdict on an untouched file.

⛔ The skip-changeset label is not applied: in this repo that label object exists but no
workflow reads it, and a real changeset is present.

Generated by Claude Code


Generated by Claude Code

…n` for scroll-area, `label` for badge
The seven `components-complex-scroll-area` demos authored their body under
`content`, which `scroll-area.tsx` never reads (it reads
`renderChildren(schema.children)`), so all seven boxes drew empty. The two
counts in `sidebar-with-badges` authored `content` too, which `badge.tsx`
never reads (it reads `schema.label || renderChildren(schema.body)`), so a
demo named `sidebar-with-badges` drew a sidebar with no badges.
The two members share a defect shape and NOT a repair key: `scroll-area` takes
`children`, `badge` takes `label`. A blind `content` -> `children` sweep
repairs the first and silently leaves both badges empty. Measured, and pinned
by a counter-probe.
`text` nodes authoring `content` are excluded by name: `text.tsx` reads
`schema.content || schema.value`, so those render correctly — including this
fixture's own sibling `text` nodes.
Adds a per-renderer corpus sweep in the shape #6788 established for `card`,
each renderer judged against its own read set.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRJge11jso9TpXRWFt1Z49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants

@os-sam@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge - #6832

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key
Aug 30, 2026
Merged

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge#6832
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key

Conversation

@claude

@claudeclaudeBot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Fixes#6805
Fixes#6806

Folded deliberately: the two cards share a defect shape and not a repair key, and that
divergence is the point.

noderenderer readskey authored now
scroll-arearenderChildren(schema.children)children (7 nodes, the whole category)
badgeschema.label || renderChildren(schema.body)label (2 nodes)

⛔ A blind content to children sweep repairs #6805 and silently leaves both badges empty,
behind a green "no content left in the catalog" assertion. The shared method is author the
key this renderer actually reads
, never rename content to children.

⛔ Neither renderer was taught to read content. The renderer is the contract (AGENTS.md
#0.1), and a second dialect for one slot on a published surface is the opposite of the
repair. For badge, ⛔ not body either even though the renderer reads it: #6771 is
retiring body as a children dialect, and 31 badge nodes already author label.

What moved

  • examples/schema-catalog/src/schemas/components-complex-scroll-area/*.json — 7 files, root
    content becomes children. Nothing else in those files changed; the 167 nested content
    keys are all on text nodes and were left alone (see the exclusion list).
  • examples/schema-catalog/src/schemas/components-basic-sidebar/sidebar-with-badges.json
    the two badge nodes' content becomes label. Its five sibling text nodes still
    author content, untouched.
  • examples/schema-catalog/test/catalog-authored-key-6805-6806.test.tsx — new, 27 tests.
  • .changeset/6805-6806-catalog-authored-key.md — empty frontmatter (no release).

Measured through the real SchemaRenderer, the way the docs gallery renders:

entry before after
…scroll-area/tall-300px elements=5 leaked=1 text=(css) 36 0 …"Line 1Line 2Line 3"…
…scroll-area/chat-messages elements=5 leaked=1 text=(css) 81 0 …"AUser 1This is a…"…
…sidebar/sidebar-with-badges elements=22 leaked=2 22 0
text before "InboxDraftsSentTrashMain content area"
text after "Inbox12Drafts3SentTrashMain content area"

leaked counts elements carrying the authored value as the host attribute content — the
#5574 class. All seven scroll-area boxes drew nothing but Radix's own injected scrollbar
stylesheet before this change.

⑤ Exclusion list — named, and encoded in the test

text nodes authoring content are NOT members and were not touched.text.tsx
reads schema.content || schema.value, so they render correctly today.
sidebar-with-badges's own sibling text nodes prove it inside the very fixture #6806
repairs: one key, two renderers, opposite outcomes. A sweep that "fixed" them would break
working demos.

Corpus-wide, content is authored by: text 666 · untyped 30 · sheet 3 · markdown 3 ·
card 1 · tooltip 1 · hover-card 1
(walked over all 431 fixtures). ⛔ So the sweep was not
generalised to "every renderer" — a blanket rule reds on working fixtures. Whether it
generalises at all is #6810's open question; this PR covers only the two renderers these two
cards name.

The exclusion list is executable, not just prose. Three assertions carry it:

  • a text node authoring content draws its string (live control);
  • the corpus still authors content on 600+ text nodes (reads 0 if someone runs the
    blanket sweep);
  • inside both touched families, every remaining content key sits on a text node.

Also excluded: card nodes. One catalog card still authors content
(components-overlay-context-menu/basic-context-menu); that is #6788's fence, in flight on
its own branch, and sweeping it here would red this PR on someone else's work.

④ Per-member named assertions, and the ablation

Each member has its own named assertion naming its own key and asserting its own text
reaches the DOM. Ablated in three legs from the committed fix, reverting fixtures only —
git checkout HEAD -- ... restore proven by an empty git diff HEAD, and each mutation
proven on disk by counting the anchor text in both directions before running:

legfixtures revertedresult
Ascroll-area only (7 files)10 failed / 17 passed
Bbadge only (1 file)6 failed / 21 passed
Cboth15 failed / 12 passed
none (the PR)27 passed

⭐ The isolation is the whole argument for the fold: in leg A every badge assertion stays
green, and in leg B every scroll-area assertion stays green. Neither card's pin
covers the other. (A + B = 16, C = 15: the one overlap is the exclusion assertion, which
both legs break.)

Named failures in leg A — 7 of 10 are one per scroll-area member:

× no `scroll-area` node authors `content` any more — the defect these cards close
× every `scroll-area` node authors only keys `scroll-area.tsx` READS
× components-complex-scroll-area/chat-messages authors `children` (not `content`) and its own text reaches the DOM
× …/code-preview … × …/document-browser … × …/horizontal-scroll …
× …/short-150px … × …/tall-300px … × …/vertical-scroll …
× inside both touched families, every remaining `content` sits on a `text` node

Leg B, all 6 — two of them one per badge member:

× no `badge` node authors `content` any more — the defect these cards close
× every `badge` node authors only keys `badge.tsx` READS, except the #6829 ledger
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× the whole demo draws each count beside its own item — the card's acceptance criterion
× inside both touched families, every remaining `content` sits on a `text` node

⭐ The single most load-bearing assertion is the counter-probe named the WRONG
generalisation
: a badge repaired to children is measured to still draw an empty pill,
so the mistake this fold exists to prevent is now mechanically red rather than a comment.

Why the existing control did not catch either — two different holes

catalog-gallery-render.test.tsx renders both families today and passes. Its non-vacuity
control is elements > WRAPPER_ELEMENTS || text:

⚠️ Deliberately not addressed by raising a global non-vacuity threshold — that is out of
scope and would red on working fixtures. The per-renderer sweep is the right instrument, and
it is what both cards proposed themselves.

Deviation from the dispatch, stated rather than papered over

The dispatch asked to extend #6788's corpus sweep in
examples/schema-catalog/test/card-demo-content-6788.test.tsx. That file is not on
origin/main
— it exists only on the unmerged branch
claude/issue-6788-context-menu-demo-content. Creating it here would collide with that PR on
the same path. So the sweep was written as its own file in the same shape (the #6773/#6788
precedent), with a per-renderer table (RENDERERS) that a later PR can fold card into once
#6788 lands. The fold's stated purpose — one PR, one edit, no conflict between #6805 and
#6806 — is met: both renderers are swept in one file, once.

Out of fence, filed not fixed

#6829 — while censusing badge nodes to size this sweep, seven more were found
authoring children, which badge.tsx does not read (unlike card.tsx, which reads
children || body). Three demos in components-basic-span draw literally nothing;
core-schema-renderer/nested-schema-example silently drops two badges. That card is the same
class under a different key, and it is left open here for two reasons: the repair is a real
decision (re-author the fixtures to label, or widen the renderer's read set), and it is out
of this PR's fence. It is a fifth rediscovery of the class #6810 is deciding about, and
the first one the #5574 leak signature cannot find at all — those seven leak nothing.

Those seven are carried in this PR's badge sweep as an exact ledger
(BADGE_CHILDREN_LEDGER_6829), not a floor, so an eighth node turns the sweep red rather
than joining a growing allowance.

That census also measured a third hole in the same non-vacuity control, distinct from the
two above: an empty badge's own host element is the third element, so
elements=3 > WRAPPER_ELEMENTS=2 with text="" reads as drewSomething=true. Recorded on
#6829.

Verification

All of the below ran on a clean tree at ec115764f, this PR's final commit.

  • pnpm exec vitest run examples/schema-catalog/ from the repo root — 17 files, 1893
    tests, all passed
    . The 17 are the whole folder (vitest list enumerates them), and they
    include catalog-gallery-render.test.tsx and layout-dom-leak-5574.test.tsx, whose
    NODE_CENSUS pins the corpus node counts.
  • tsc --noEmit plus tsc -p tsconfig.test.json for the package — clean. --listFiles
    confirms 17 of 17 test files are in the type-check program, including the new one, so
    "typecheck clean" actually covers it.
  • check-control-bytesOK (scanned 5649 tracked text file(s)).
  • check-type-check-coverage41/41 packages compile their tests.
  • check-changeset-presenceNo source of a released package changed in this range, so no changeset is owed; a changeset with empty frontmatter is added anyway, declaring "no
    release" explicitly (objectui#3387's first-class form). check-changeset-no-major green.
  • regenerate:checkexamples/schema-catalog/src/index.ts is up to date (431 entries).
  • eslint: the repo-wide scan is CI's; this is a declared narrowing. eslint reads exactly
    1 of the 10 changed files (the other 9 are .json/.md and eslint itself reports them
    as ignored — that is eslint's own population, not an assumption), and that one file is
    0 errors, 0 warnings via --format json. No type-aware linting is configured
    (eslint.config.js declares no parserOptions.project or projectService), so this diff
    cannot move any rule's verdict on an untouched file.

⛔ The skip-changeset label is not applied: in this repo that label object exists but no
workflow reads it, and a real changeset is present.

Generated by Claude Code


Generated by Claude Code

…n` for scroll-area, `label` for badge
The seven `components-complex-scroll-area` demos authored their body under
`content`, which `scroll-area.tsx` never reads (it reads
`renderChildren(schema.children)`), so all seven boxes drew empty. The two
counts in `sidebar-with-badges` authored `content` too, which `badge.tsx`
never reads (it reads `schema.label || renderChildren(schema.body)`), so a
demo named `sidebar-with-badges` drew a sidebar with no badges.
The two members share a defect shape and NOT a repair key: `scroll-area` takes
`children`, `badge` takes `label`. A blind `content` -> `children` sweep
repairs the first and silently leaves both badges empty. Measured, and pinned
by a counter-probe.
`text` nodes authoring `content` are excluded by name: `text.tsx` reads
`schema.content || schema.value`, so those render correctly — including this
fixture's own sibling `text` nodes.
Adds a per-renderer corpus sweep in the shape #6788 established for `card`,
each renderer judged against its own read set.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRJge11jso9TpXRWFt1Z49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants

@os-sam@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge - #6832

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key
Aug 30, 2026
Merged

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge#6832
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key

Conversation

@claude

@claudeclaudeBot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Fixes#6805
Fixes#6806

Folded deliberately: the two cards share a defect shape and not a repair key, and that
divergence is the point.

noderenderer readskey authored now
scroll-arearenderChildren(schema.children)children (7 nodes, the whole category)
badgeschema.label || renderChildren(schema.body)label (2 nodes)

⛔ A blind content to children sweep repairs #6805 and silently leaves both badges empty,
behind a green "no content left in the catalog" assertion. The shared method is author the
key this renderer actually reads
, never rename content to children.

⛔ Neither renderer was taught to read content. The renderer is the contract (AGENTS.md
#0.1), and a second dialect for one slot on a published surface is the opposite of the
repair. For badge, ⛔ not body either even though the renderer reads it: #6771 is
retiring body as a children dialect, and 31 badge nodes already author label.

What moved

  • examples/schema-catalog/src/schemas/components-complex-scroll-area/*.json — 7 files, root
    content becomes children. Nothing else in those files changed; the 167 nested content
    keys are all on text nodes and were left alone (see the exclusion list).
  • examples/schema-catalog/src/schemas/components-basic-sidebar/sidebar-with-badges.json
    the two badge nodes' content becomes label. Its five sibling text nodes still
    author content, untouched.
  • examples/schema-catalog/test/catalog-authored-key-6805-6806.test.tsx — new, 27 tests.
  • .changeset/6805-6806-catalog-authored-key.md — empty frontmatter (no release).

Measured through the real SchemaRenderer, the way the docs gallery renders:

entry before after
…scroll-area/tall-300px elements=5 leaked=1 text=(css) 36 0 …"Line 1Line 2Line 3"…
…scroll-area/chat-messages elements=5 leaked=1 text=(css) 81 0 …"AUser 1This is a…"…
…sidebar/sidebar-with-badges elements=22 leaked=2 22 0
text before "InboxDraftsSentTrashMain content area"
text after "Inbox12Drafts3SentTrashMain content area"

leaked counts elements carrying the authored value as the host attribute content — the
#5574 class. All seven scroll-area boxes drew nothing but Radix's own injected scrollbar
stylesheet before this change.

⑤ Exclusion list — named, and encoded in the test

text nodes authoring content are NOT members and were not touched.text.tsx
reads schema.content || schema.value, so they render correctly today.
sidebar-with-badges's own sibling text nodes prove it inside the very fixture #6806
repairs: one key, two renderers, opposite outcomes. A sweep that "fixed" them would break
working demos.

Corpus-wide, content is authored by: text 666 · untyped 30 · sheet 3 · markdown 3 ·
card 1 · tooltip 1 · hover-card 1
(walked over all 431 fixtures). ⛔ So the sweep was not
generalised to "every renderer" — a blanket rule reds on working fixtures. Whether it
generalises at all is #6810's open question; this PR covers only the two renderers these two
cards name.

The exclusion list is executable, not just prose. Three assertions carry it:

  • a text node authoring content draws its string (live control);
  • the corpus still authors content on 600+ text nodes (reads 0 if someone runs the
    blanket sweep);
  • inside both touched families, every remaining content key sits on a text node.

Also excluded: card nodes. One catalog card still authors content
(components-overlay-context-menu/basic-context-menu); that is #6788's fence, in flight on
its own branch, and sweeping it here would red this PR on someone else's work.

④ Per-member named assertions, and the ablation

Each member has its own named assertion naming its own key and asserting its own text
reaches the DOM. Ablated in three legs from the committed fix, reverting fixtures only —
git checkout HEAD -- ... restore proven by an empty git diff HEAD, and each mutation
proven on disk by counting the anchor text in both directions before running:

legfixtures revertedresult
Ascroll-area only (7 files)10 failed / 17 passed
Bbadge only (1 file)6 failed / 21 passed
Cboth15 failed / 12 passed
none (the PR)27 passed

⭐ The isolation is the whole argument for the fold: in leg A every badge assertion stays
green, and in leg B every scroll-area assertion stays green. Neither card's pin
covers the other. (A + B = 16, C = 15: the one overlap is the exclusion assertion, which
both legs break.)

Named failures in leg A — 7 of 10 are one per scroll-area member:

× no `scroll-area` node authors `content` any more — the defect these cards close
× every `scroll-area` node authors only keys `scroll-area.tsx` READS
× components-complex-scroll-area/chat-messages authors `children` (not `content`) and its own text reaches the DOM
× …/code-preview … × …/document-browser … × …/horizontal-scroll …
× …/short-150px … × …/tall-300px … × …/vertical-scroll …
× inside both touched families, every remaining `content` sits on a `text` node

Leg B, all 6 — two of them one per badge member:

× no `badge` node authors `content` any more — the defect these cards close
× every `badge` node authors only keys `badge.tsx` READS, except the #6829 ledger
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× the whole demo draws each count beside its own item — the card's acceptance criterion
× inside both touched families, every remaining `content` sits on a `text` node

⭐ The single most load-bearing assertion is the counter-probe named the WRONG
generalisation
: a badge repaired to children is measured to still draw an empty pill,
so the mistake this fold exists to prevent is now mechanically red rather than a comment.

Why the existing control did not catch either — two different holes

catalog-gallery-render.test.tsx renders both families today and passes. Its non-vacuity
control is elements > WRAPPER_ELEMENTS || text:

⚠️ Deliberately not addressed by raising a global non-vacuity threshold — that is out of
scope and would red on working fixtures. The per-renderer sweep is the right instrument, and
it is what both cards proposed themselves.

Deviation from the dispatch, stated rather than papered over

The dispatch asked to extend #6788's corpus sweep in
examples/schema-catalog/test/card-demo-content-6788.test.tsx. That file is not on
origin/main
— it exists only on the unmerged branch
claude/issue-6788-context-menu-demo-content. Creating it here would collide with that PR on
the same path. So the sweep was written as its own file in the same shape (the #6773/#6788
precedent), with a per-renderer table (RENDERERS) that a later PR can fold card into once
#6788 lands. The fold's stated purpose — one PR, one edit, no conflict between #6805 and
#6806 — is met: both renderers are swept in one file, once.

Out of fence, filed not fixed

#6829 — while censusing badge nodes to size this sweep, seven more were found
authoring children, which badge.tsx does not read (unlike card.tsx, which reads
children || body). Three demos in components-basic-span draw literally nothing;
core-schema-renderer/nested-schema-example silently drops two badges. That card is the same
class under a different key, and it is left open here for two reasons: the repair is a real
decision (re-author the fixtures to label, or widen the renderer's read set), and it is out
of this PR's fence. It is a fifth rediscovery of the class #6810 is deciding about, and
the first one the #5574 leak signature cannot find at all — those seven leak nothing.

Those seven are carried in this PR's badge sweep as an exact ledger
(BADGE_CHILDREN_LEDGER_6829), not a floor, so an eighth node turns the sweep red rather
than joining a growing allowance.

That census also measured a third hole in the same non-vacuity control, distinct from the
two above: an empty badge's own host element is the third element, so
elements=3 > WRAPPER_ELEMENTS=2 with text="" reads as drewSomething=true. Recorded on
#6829.

Verification

All of the below ran on a clean tree at ec115764f, this PR's final commit.

  • pnpm exec vitest run examples/schema-catalog/ from the repo root — 17 files, 1893
    tests, all passed
    . The 17 are the whole folder (vitest list enumerates them), and they
    include catalog-gallery-render.test.tsx and layout-dom-leak-5574.test.tsx, whose
    NODE_CENSUS pins the corpus node counts.
  • tsc --noEmit plus tsc -p tsconfig.test.json for the package — clean. --listFiles
    confirms 17 of 17 test files are in the type-check program, including the new one, so
    "typecheck clean" actually covers it.
  • check-control-bytesOK (scanned 5649 tracked text file(s)).
  • check-type-check-coverage41/41 packages compile their tests.
  • check-changeset-presenceNo source of a released package changed in this range, so no changeset is owed; a changeset with empty frontmatter is added anyway, declaring "no
    release" explicitly (objectui#3387's first-class form). check-changeset-no-major green.
  • regenerate:checkexamples/schema-catalog/src/index.ts is up to date (431 entries).
  • eslint: the repo-wide scan is CI's; this is a declared narrowing. eslint reads exactly
    1 of the 10 changed files (the other 9 are .json/.md and eslint itself reports them
    as ignored — that is eslint's own population, not an assumption), and that one file is
    0 errors, 0 warnings via --format json. No type-aware linting is configured
    (eslint.config.js declares no parserOptions.project or projectService), so this diff
    cannot move any rule's verdict on an untouched file.

⛔ The skip-changeset label is not applied: in this repo that label object exists but no
workflow reads it, and a real changeset is present.

Generated by Claude Code


Generated by Claude Code

…n` for scroll-area, `label` for badge
The seven `components-complex-scroll-area` demos authored their body under
`content`, which `scroll-area.tsx` never reads (it reads
`renderChildren(schema.children)`), so all seven boxes drew empty. The two
counts in `sidebar-with-badges` authored `content` too, which `badge.tsx`
never reads (it reads `schema.label || renderChildren(schema.body)`), so a
demo named `sidebar-with-badges` drew a sidebar with no badges.
The two members share a defect shape and NOT a repair key: `scroll-area` takes
`children`, `badge` takes `label`. A blind `content` -> `children` sweep
repairs the first and silently leaves both badges empty. Measured, and pinned
by a counter-probe.
`text` nodes authoring `content` are excluded by name: `text.tsx` reads
`schema.content || schema.value`, so those render correctly — including this
fixture's own sibling `text` nodes.
Adds a per-renderer corpus sweep in the shape #6788 established for `card`,
each renderer judged against its own read set.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRJge11jso9TpXRWFt1Z49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants

@os-sam@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge - #6832

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key
Aug 30, 2026
Merged

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge#6832
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key

Conversation

@claude

@claudeclaudeBot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Fixes#6805
Fixes#6806

Folded deliberately: the two cards share a defect shape and not a repair key, and that
divergence is the point.

noderenderer readskey authored now
scroll-arearenderChildren(schema.children)children (7 nodes, the whole category)
badgeschema.label || renderChildren(schema.body)label (2 nodes)

⛔ A blind content to children sweep repairs #6805 and silently leaves both badges empty,
behind a green "no content left in the catalog" assertion. The shared method is author the
key this renderer actually reads
, never rename content to children.

⛔ Neither renderer was taught to read content. The renderer is the contract (AGENTS.md
#0.1), and a second dialect for one slot on a published surface is the opposite of the
repair. For badge, ⛔ not body either even though the renderer reads it: #6771 is
retiring body as a children dialect, and 31 badge nodes already author label.

What moved

  • examples/schema-catalog/src/schemas/components-complex-scroll-area/*.json — 7 files, root
    content becomes children. Nothing else in those files changed; the 167 nested content
    keys are all on text nodes and were left alone (see the exclusion list).
  • examples/schema-catalog/src/schemas/components-basic-sidebar/sidebar-with-badges.json
    the two badge nodes' content becomes label. Its five sibling text nodes still
    author content, untouched.
  • examples/schema-catalog/test/catalog-authored-key-6805-6806.test.tsx — new, 27 tests.
  • .changeset/6805-6806-catalog-authored-key.md — empty frontmatter (no release).

Measured through the real SchemaRenderer, the way the docs gallery renders:

entry before after
…scroll-area/tall-300px elements=5 leaked=1 text=(css) 36 0 …"Line 1Line 2Line 3"…
…scroll-area/chat-messages elements=5 leaked=1 text=(css) 81 0 …"AUser 1This is a…"…
…sidebar/sidebar-with-badges elements=22 leaked=2 22 0
text before "InboxDraftsSentTrashMain content area"
text after "Inbox12Drafts3SentTrashMain content area"

leaked counts elements carrying the authored value as the host attribute content — the
#5574 class. All seven scroll-area boxes drew nothing but Radix's own injected scrollbar
stylesheet before this change.

⑤ Exclusion list — named, and encoded in the test

text nodes authoring content are NOT members and were not touched.text.tsx
reads schema.content || schema.value, so they render correctly today.
sidebar-with-badges's own sibling text nodes prove it inside the very fixture #6806
repairs: one key, two renderers, opposite outcomes. A sweep that "fixed" them would break
working demos.

Corpus-wide, content is authored by: text 666 · untyped 30 · sheet 3 · markdown 3 ·
card 1 · tooltip 1 · hover-card 1
(walked over all 431 fixtures). ⛔ So the sweep was not
generalised to "every renderer" — a blanket rule reds on working fixtures. Whether it
generalises at all is #6810's open question; this PR covers only the two renderers these two
cards name.

The exclusion list is executable, not just prose. Three assertions carry it:

  • a text node authoring content draws its string (live control);
  • the corpus still authors content on 600+ text nodes (reads 0 if someone runs the
    blanket sweep);
  • inside both touched families, every remaining content key sits on a text node.

Also excluded: card nodes. One catalog card still authors content
(components-overlay-context-menu/basic-context-menu); that is #6788's fence, in flight on
its own branch, and sweeping it here would red this PR on someone else's work.

④ Per-member named assertions, and the ablation

Each member has its own named assertion naming its own key and asserting its own text
reaches the DOM. Ablated in three legs from the committed fix, reverting fixtures only —
git checkout HEAD -- ... restore proven by an empty git diff HEAD, and each mutation
proven on disk by counting the anchor text in both directions before running:

legfixtures revertedresult
Ascroll-area only (7 files)10 failed / 17 passed
Bbadge only (1 file)6 failed / 21 passed
Cboth15 failed / 12 passed
none (the PR)27 passed

⭐ The isolation is the whole argument for the fold: in leg A every badge assertion stays
green, and in leg B every scroll-area assertion stays green. Neither card's pin
covers the other. (A + B = 16, C = 15: the one overlap is the exclusion assertion, which
both legs break.)

Named failures in leg A — 7 of 10 are one per scroll-area member:

× no `scroll-area` node authors `content` any more — the defect these cards close
× every `scroll-area` node authors only keys `scroll-area.tsx` READS
× components-complex-scroll-area/chat-messages authors `children` (not `content`) and its own text reaches the DOM
× …/code-preview … × …/document-browser … × …/horizontal-scroll …
× …/short-150px … × …/tall-300px … × …/vertical-scroll …
× inside both touched families, every remaining `content` sits on a `text` node

Leg B, all 6 — two of them one per badge member:

× no `badge` node authors `content` any more — the defect these cards close
× every `badge` node authors only keys `badge.tsx` READS, except the #6829 ledger
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× the whole demo draws each count beside its own item — the card's acceptance criterion
× inside both touched families, every remaining `content` sits on a `text` node

⭐ The single most load-bearing assertion is the counter-probe named the WRONG
generalisation
: a badge repaired to children is measured to still draw an empty pill,
so the mistake this fold exists to prevent is now mechanically red rather than a comment.

Why the existing control did not catch either — two different holes

catalog-gallery-render.test.tsx renders both families today and passes. Its non-vacuity
control is elements > WRAPPER_ELEMENTS || text:

⚠️ Deliberately not addressed by raising a global non-vacuity threshold — that is out of
scope and would red on working fixtures. The per-renderer sweep is the right instrument, and
it is what both cards proposed themselves.

Deviation from the dispatch, stated rather than papered over

The dispatch asked to extend #6788's corpus sweep in
examples/schema-catalog/test/card-demo-content-6788.test.tsx. That file is not on
origin/main
— it exists only on the unmerged branch
claude/issue-6788-context-menu-demo-content. Creating it here would collide with that PR on
the same path. So the sweep was written as its own file in the same shape (the #6773/#6788
precedent), with a per-renderer table (RENDERERS) that a later PR can fold card into once
#6788 lands. The fold's stated purpose — one PR, one edit, no conflict between #6805 and
#6806 — is met: both renderers are swept in one file, once.

Out of fence, filed not fixed

#6829 — while censusing badge nodes to size this sweep, seven more were found
authoring children, which badge.tsx does not read (unlike card.tsx, which reads
children || body). Three demos in components-basic-span draw literally nothing;
core-schema-renderer/nested-schema-example silently drops two badges. That card is the same
class under a different key, and it is left open here for two reasons: the repair is a real
decision (re-author the fixtures to label, or widen the renderer's read set), and it is out
of this PR's fence. It is a fifth rediscovery of the class #6810 is deciding about, and
the first one the #5574 leak signature cannot find at all — those seven leak nothing.

Those seven are carried in this PR's badge sweep as an exact ledger
(BADGE_CHILDREN_LEDGER_6829), not a floor, so an eighth node turns the sweep red rather
than joining a growing allowance.

That census also measured a third hole in the same non-vacuity control, distinct from the
two above: an empty badge's own host element is the third element, so
elements=3 > WRAPPER_ELEMENTS=2 with text="" reads as drewSomething=true. Recorded on
#6829.

Verification

All of the below ran on a clean tree at ec115764f, this PR's final commit.

  • pnpm exec vitest run examples/schema-catalog/ from the repo root — 17 files, 1893
    tests, all passed
    . The 17 are the whole folder (vitest list enumerates them), and they
    include catalog-gallery-render.test.tsx and layout-dom-leak-5574.test.tsx, whose
    NODE_CENSUS pins the corpus node counts.
  • tsc --noEmit plus tsc -p tsconfig.test.json for the package — clean. --listFiles
    confirms 17 of 17 test files are in the type-check program, including the new one, so
    "typecheck clean" actually covers it.
  • check-control-bytesOK (scanned 5649 tracked text file(s)).
  • check-type-check-coverage41/41 packages compile their tests.
  • check-changeset-presenceNo source of a released package changed in this range, so no changeset is owed; a changeset with empty frontmatter is added anyway, declaring "no
    release" explicitly (objectui#3387's first-class form). check-changeset-no-major green.
  • regenerate:checkexamples/schema-catalog/src/index.ts is up to date (431 entries).
  • eslint: the repo-wide scan is CI's; this is a declared narrowing. eslint reads exactly
    1 of the 10 changed files (the other 9 are .json/.md and eslint itself reports them
    as ignored — that is eslint's own population, not an assumption), and that one file is
    0 errors, 0 warnings via --format json. No type-aware linting is configured
    (eslint.config.js declares no parserOptions.project or projectService), so this diff
    cannot move any rule's verdict on an untouched file.

⛔ The skip-changeset label is not applied: in this repo that label object exists but no
workflow reads it, and a real changeset is present.

Generated by Claude Code


Generated by Claude Code

…n` for scroll-area, `label` for badge
The seven `components-complex-scroll-area` demos authored their body under
`content`, which `scroll-area.tsx` never reads (it reads
`renderChildren(schema.children)`), so all seven boxes drew empty. The two
counts in `sidebar-with-badges` authored `content` too, which `badge.tsx`
never reads (it reads `schema.label || renderChildren(schema.body)`), so a
demo named `sidebar-with-badges` drew a sidebar with no badges.
The two members share a defect shape and NOT a repair key: `scroll-area` takes
`children`, `badge` takes `label`. A blind `content` -> `children` sweep
repairs the first and silently leaves both badges empty. Measured, and pinned
by a counter-probe.
`text` nodes authoring `content` are excluded by name: `text.tsx` reads
`schema.content || schema.value`, so those render correctly — including this
fixture's own sibling `text` nodes.
Adds a per-renderer corpus sweep in the shape #6788 established for `card`,
each renderer judged against its own read set.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRJge11jso9TpXRWFt1Z49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants

@os-sam@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge - #6832

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key
Aug 30, 2026
Merged

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge#6832
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key

Conversation

@claude

@claudeclaudeBot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Fixes#6805
Fixes#6806

Folded deliberately: the two cards share a defect shape and not a repair key, and that
divergence is the point.

noderenderer readskey authored now
scroll-arearenderChildren(schema.children)children (7 nodes, the whole category)
badgeschema.label || renderChildren(schema.body)label (2 nodes)

⛔ A blind content to children sweep repairs #6805 and silently leaves both badges empty,
behind a green "no content left in the catalog" assertion. The shared method is author the
key this renderer actually reads
, never rename content to children.

⛔ Neither renderer was taught to read content. The renderer is the contract (AGENTS.md
#0.1), and a second dialect for one slot on a published surface is the opposite of the
repair. For badge, ⛔ not body either even though the renderer reads it: #6771 is
retiring body as a children dialect, and 31 badge nodes already author label.

What moved

  • examples/schema-catalog/src/schemas/components-complex-scroll-area/*.json — 7 files, root
    content becomes children. Nothing else in those files changed; the 167 nested content
    keys are all on text nodes and were left alone (see the exclusion list).
  • examples/schema-catalog/src/schemas/components-basic-sidebar/sidebar-with-badges.json
    the two badge nodes' content becomes label. Its five sibling text nodes still
    author content, untouched.
  • examples/schema-catalog/test/catalog-authored-key-6805-6806.test.tsx — new, 27 tests.
  • .changeset/6805-6806-catalog-authored-key.md — empty frontmatter (no release).

Measured through the real SchemaRenderer, the way the docs gallery renders:

entry before after
…scroll-area/tall-300px elements=5 leaked=1 text=(css) 36 0 …"Line 1Line 2Line 3"…
…scroll-area/chat-messages elements=5 leaked=1 text=(css) 81 0 …"AUser 1This is a…"…
…sidebar/sidebar-with-badges elements=22 leaked=2 22 0
text before "InboxDraftsSentTrashMain content area"
text after "Inbox12Drafts3SentTrashMain content area"

leaked counts elements carrying the authored value as the host attribute content — the
#5574 class. All seven scroll-area boxes drew nothing but Radix's own injected scrollbar
stylesheet before this change.

⑤ Exclusion list — named, and encoded in the test

text nodes authoring content are NOT members and were not touched.text.tsx
reads schema.content || schema.value, so they render correctly today.
sidebar-with-badges's own sibling text nodes prove it inside the very fixture #6806
repairs: one key, two renderers, opposite outcomes. A sweep that "fixed" them would break
working demos.

Corpus-wide, content is authored by: text 666 · untyped 30 · sheet 3 · markdown 3 ·
card 1 · tooltip 1 · hover-card 1
(walked over all 431 fixtures). ⛔ So the sweep was not
generalised to "every renderer" — a blanket rule reds on working fixtures. Whether it
generalises at all is #6810's open question; this PR covers only the two renderers these two
cards name.

The exclusion list is executable, not just prose. Three assertions carry it:

  • a text node authoring content draws its string (live control);
  • the corpus still authors content on 600+ text nodes (reads 0 if someone runs the
    blanket sweep);
  • inside both touched families, every remaining content key sits on a text node.

Also excluded: card nodes. One catalog card still authors content
(components-overlay-context-menu/basic-context-menu); that is #6788's fence, in flight on
its own branch, and sweeping it here would red this PR on someone else's work.

④ Per-member named assertions, and the ablation

Each member has its own named assertion naming its own key and asserting its own text
reaches the DOM. Ablated in three legs from the committed fix, reverting fixtures only —
git checkout HEAD -- ... restore proven by an empty git diff HEAD, and each mutation
proven on disk by counting the anchor text in both directions before running:

legfixtures revertedresult
Ascroll-area only (7 files)10 failed / 17 passed
Bbadge only (1 file)6 failed / 21 passed
Cboth15 failed / 12 passed
none (the PR)27 passed

⭐ The isolation is the whole argument for the fold: in leg A every badge assertion stays
green, and in leg B every scroll-area assertion stays green. Neither card's pin
covers the other. (A + B = 16, C = 15: the one overlap is the exclusion assertion, which
both legs break.)

Named failures in leg A — 7 of 10 are one per scroll-area member:

× no `scroll-area` node authors `content` any more — the defect these cards close
× every `scroll-area` node authors only keys `scroll-area.tsx` READS
× components-complex-scroll-area/chat-messages authors `children` (not `content`) and its own text reaches the DOM
× …/code-preview … × …/document-browser … × …/horizontal-scroll …
× …/short-150px … × …/tall-300px … × …/vertical-scroll …
× inside both touched families, every remaining `content` sits on a `text` node

Leg B, all 6 — two of them one per badge member:

× no `badge` node authors `content` any more — the defect these cards close
× every `badge` node authors only keys `badge.tsx` READS, except the #6829 ledger
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× the whole demo draws each count beside its own item — the card's acceptance criterion
× inside both touched families, every remaining `content` sits on a `text` node

⭐ The single most load-bearing assertion is the counter-probe named the WRONG
generalisation
: a badge repaired to children is measured to still draw an empty pill,
so the mistake this fold exists to prevent is now mechanically red rather than a comment.

Why the existing control did not catch either — two different holes

catalog-gallery-render.test.tsx renders both families today and passes. Its non-vacuity
control is elements > WRAPPER_ELEMENTS || text:

⚠️ Deliberately not addressed by raising a global non-vacuity threshold — that is out of
scope and would red on working fixtures. The per-renderer sweep is the right instrument, and
it is what both cards proposed themselves.

Deviation from the dispatch, stated rather than papered over

The dispatch asked to extend #6788's corpus sweep in
examples/schema-catalog/test/card-demo-content-6788.test.tsx. That file is not on
origin/main
— it exists only on the unmerged branch
claude/issue-6788-context-menu-demo-content. Creating it here would collide with that PR on
the same path. So the sweep was written as its own file in the same shape (the #6773/#6788
precedent), with a per-renderer table (RENDERERS) that a later PR can fold card into once
#6788 lands. The fold's stated purpose — one PR, one edit, no conflict between #6805 and
#6806 — is met: both renderers are swept in one file, once.

Out of fence, filed not fixed

#6829 — while censusing badge nodes to size this sweep, seven more were found
authoring children, which badge.tsx does not read (unlike card.tsx, which reads
children || body). Three demos in components-basic-span draw literally nothing;
core-schema-renderer/nested-schema-example silently drops two badges. That card is the same
class under a different key, and it is left open here for two reasons: the repair is a real
decision (re-author the fixtures to label, or widen the renderer's read set), and it is out
of this PR's fence. It is a fifth rediscovery of the class #6810 is deciding about, and
the first one the #5574 leak signature cannot find at all — those seven leak nothing.

Those seven are carried in this PR's badge sweep as an exact ledger
(BADGE_CHILDREN_LEDGER_6829), not a floor, so an eighth node turns the sweep red rather
than joining a growing allowance.

That census also measured a third hole in the same non-vacuity control, distinct from the
two above: an empty badge's own host element is the third element, so
elements=3 > WRAPPER_ELEMENTS=2 with text="" reads as drewSomething=true. Recorded on
#6829.

Verification

All of the below ran on a clean tree at ec115764f, this PR's final commit.

  • pnpm exec vitest run examples/schema-catalog/ from the repo root — 17 files, 1893
    tests, all passed
    . The 17 are the whole folder (vitest list enumerates them), and they
    include catalog-gallery-render.test.tsx and layout-dom-leak-5574.test.tsx, whose
    NODE_CENSUS pins the corpus node counts.
  • tsc --noEmit plus tsc -p tsconfig.test.json for the package — clean. --listFiles
    confirms 17 of 17 test files are in the type-check program, including the new one, so
    "typecheck clean" actually covers it.
  • check-control-bytesOK (scanned 5649 tracked text file(s)).
  • check-type-check-coverage41/41 packages compile their tests.
  • check-changeset-presenceNo source of a released package changed in this range, so no changeset is owed; a changeset with empty frontmatter is added anyway, declaring "no
    release" explicitly (objectui#3387's first-class form). check-changeset-no-major green.
  • regenerate:checkexamples/schema-catalog/src/index.ts is up to date (431 entries).
  • eslint: the repo-wide scan is CI's; this is a declared narrowing. eslint reads exactly
    1 of the 10 changed files (the other 9 are .json/.md and eslint itself reports them
    as ignored — that is eslint's own population, not an assumption), and that one file is
    0 errors, 0 warnings via --format json. No type-aware linting is configured
    (eslint.config.js declares no parserOptions.project or projectService), so this diff
    cannot move any rule's verdict on an untouched file.

⛔ The skip-changeset label is not applied: in this repo that label object exists but no
workflow reads it, and a real changeset is present.

Generated by Claude Code


Generated by Claude Code

…n` for scroll-area, `label` for badge
The seven `components-complex-scroll-area` demos authored their body under
`content`, which `scroll-area.tsx` never reads (it reads
`renderChildren(schema.children)`), so all seven boxes drew empty. The two
counts in `sidebar-with-badges` authored `content` too, which `badge.tsx`
never reads (it reads `schema.label || renderChildren(schema.body)`), so a
demo named `sidebar-with-badges` drew a sidebar with no badges.
The two members share a defect shape and NOT a repair key: `scroll-area` takes
`children`, `badge` takes `label`. A blind `content` -> `children` sweep
repairs the first and silently leaves both badges empty. Measured, and pinned
by a counter-probe.
`text` nodes authoring `content` are excluded by name: `text.tsx` reads
`schema.content || schema.value`, so those render correctly — including this
fixture's own sibling `text` nodes.
Adds a per-renderer corpus sweep in the shape #6788 established for `card`,
each renderer judged against its own read set.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRJge11jso9TpXRWFt1Z49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants

@os-sam@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge - #6832

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key
Aug 30, 2026
Merged

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge#6832
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key

Conversation

@claude

@claudeclaudeBot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Fixes#6805
Fixes#6806

Folded deliberately: the two cards share a defect shape and not a repair key, and that
divergence is the point.

noderenderer readskey authored now
scroll-arearenderChildren(schema.children)children (7 nodes, the whole category)
badgeschema.label || renderChildren(schema.body)label (2 nodes)

⛔ A blind content to children sweep repairs #6805 and silently leaves both badges empty,
behind a green "no content left in the catalog" assertion. The shared method is author the
key this renderer actually reads
, never rename content to children.

⛔ Neither renderer was taught to read content. The renderer is the contract (AGENTS.md
#0.1), and a second dialect for one slot on a published surface is the opposite of the
repair. For badge, ⛔ not body either even though the renderer reads it: #6771 is
retiring body as a children dialect, and 31 badge nodes already author label.

What moved

  • examples/schema-catalog/src/schemas/components-complex-scroll-area/*.json — 7 files, root
    content becomes children. Nothing else in those files changed; the 167 nested content
    keys are all on text nodes and were left alone (see the exclusion list).
  • examples/schema-catalog/src/schemas/components-basic-sidebar/sidebar-with-badges.json
    the two badge nodes' content becomes label. Its five sibling text nodes still
    author content, untouched.
  • examples/schema-catalog/test/catalog-authored-key-6805-6806.test.tsx — new, 27 tests.
  • .changeset/6805-6806-catalog-authored-key.md — empty frontmatter (no release).

Measured through the real SchemaRenderer, the way the docs gallery renders:

entry before after
…scroll-area/tall-300px elements=5 leaked=1 text=(css) 36 0 …"Line 1Line 2Line 3"…
…scroll-area/chat-messages elements=5 leaked=1 text=(css) 81 0 …"AUser 1This is a…"…
…sidebar/sidebar-with-badges elements=22 leaked=2 22 0
text before "InboxDraftsSentTrashMain content area"
text after "Inbox12Drafts3SentTrashMain content area"

leaked counts elements carrying the authored value as the host attribute content — the
#5574 class. All seven scroll-area boxes drew nothing but Radix's own injected scrollbar
stylesheet before this change.

⑤ Exclusion list — named, and encoded in the test

text nodes authoring content are NOT members and were not touched.text.tsx
reads schema.content || schema.value, so they render correctly today.
sidebar-with-badges's own sibling text nodes prove it inside the very fixture #6806
repairs: one key, two renderers, opposite outcomes. A sweep that "fixed" them would break
working demos.

Corpus-wide, content is authored by: text 666 · untyped 30 · sheet 3 · markdown 3 ·
card 1 · tooltip 1 · hover-card 1
(walked over all 431 fixtures). ⛔ So the sweep was not
generalised to "every renderer" — a blanket rule reds on working fixtures. Whether it
generalises at all is #6810's open question; this PR covers only the two renderers these two
cards name.

The exclusion list is executable, not just prose. Three assertions carry it:

  • a text node authoring content draws its string (live control);
  • the corpus still authors content on 600+ text nodes (reads 0 if someone runs the
    blanket sweep);
  • inside both touched families, every remaining content key sits on a text node.

Also excluded: card nodes. One catalog card still authors content
(components-overlay-context-menu/basic-context-menu); that is #6788's fence, in flight on
its own branch, and sweeping it here would red this PR on someone else's work.

④ Per-member named assertions, and the ablation

Each member has its own named assertion naming its own key and asserting its own text
reaches the DOM. Ablated in three legs from the committed fix, reverting fixtures only —
git checkout HEAD -- ... restore proven by an empty git diff HEAD, and each mutation
proven on disk by counting the anchor text in both directions before running:

legfixtures revertedresult
Ascroll-area only (7 files)10 failed / 17 passed
Bbadge only (1 file)6 failed / 21 passed
Cboth15 failed / 12 passed
none (the PR)27 passed

⭐ The isolation is the whole argument for the fold: in leg A every badge assertion stays
green, and in leg B every scroll-area assertion stays green. Neither card's pin
covers the other. (A + B = 16, C = 15: the one overlap is the exclusion assertion, which
both legs break.)

Named failures in leg A — 7 of 10 are one per scroll-area member:

× no `scroll-area` node authors `content` any more — the defect these cards close
× every `scroll-area` node authors only keys `scroll-area.tsx` READS
× components-complex-scroll-area/chat-messages authors `children` (not `content`) and its own text reaches the DOM
× …/code-preview … × …/document-browser … × …/horizontal-scroll …
× …/short-150px … × …/tall-300px … × …/vertical-scroll …
× inside both touched families, every remaining `content` sits on a `text` node

Leg B, all 6 — two of them one per badge member:

× no `badge` node authors `content` any more — the defect these cards close
× every `badge` node authors only keys `badge.tsx` READS, except the #6829 ledger
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× the whole demo draws each count beside its own item — the card's acceptance criterion
× inside both touched families, every remaining `content` sits on a `text` node

⭐ The single most load-bearing assertion is the counter-probe named the WRONG
generalisation
: a badge repaired to children is measured to still draw an empty pill,
so the mistake this fold exists to prevent is now mechanically red rather than a comment.

Why the existing control did not catch either — two different holes

catalog-gallery-render.test.tsx renders both families today and passes. Its non-vacuity
control is elements > WRAPPER_ELEMENTS || text:

⚠️ Deliberately not addressed by raising a global non-vacuity threshold — that is out of
scope and would red on working fixtures. The per-renderer sweep is the right instrument, and
it is what both cards proposed themselves.

Deviation from the dispatch, stated rather than papered over

The dispatch asked to extend #6788's corpus sweep in
examples/schema-catalog/test/card-demo-content-6788.test.tsx. That file is not on
origin/main
— it exists only on the unmerged branch
claude/issue-6788-context-menu-demo-content. Creating it here would collide with that PR on
the same path. So the sweep was written as its own file in the same shape (the #6773/#6788
precedent), with a per-renderer table (RENDERERS) that a later PR can fold card into once
#6788 lands. The fold's stated purpose — one PR, one edit, no conflict between #6805 and
#6806 — is met: both renderers are swept in one file, once.

Out of fence, filed not fixed

#6829 — while censusing badge nodes to size this sweep, seven more were found
authoring children, which badge.tsx does not read (unlike card.tsx, which reads
children || body). Three demos in components-basic-span draw literally nothing;
core-schema-renderer/nested-schema-example silently drops two badges. That card is the same
class under a different key, and it is left open here for two reasons: the repair is a real
decision (re-author the fixtures to label, or widen the renderer's read set), and it is out
of this PR's fence. It is a fifth rediscovery of the class #6810 is deciding about, and
the first one the #5574 leak signature cannot find at all — those seven leak nothing.

Those seven are carried in this PR's badge sweep as an exact ledger
(BADGE_CHILDREN_LEDGER_6829), not a floor, so an eighth node turns the sweep red rather
than joining a growing allowance.

That census also measured a third hole in the same non-vacuity control, distinct from the
two above: an empty badge's own host element is the third element, so
elements=3 > WRAPPER_ELEMENTS=2 with text="" reads as drewSomething=true. Recorded on
#6829.

Verification

All of the below ran on a clean tree at ec115764f, this PR's final commit.

  • pnpm exec vitest run examples/schema-catalog/ from the repo root — 17 files, 1893
    tests, all passed
    . The 17 are the whole folder (vitest list enumerates them), and they
    include catalog-gallery-render.test.tsx and layout-dom-leak-5574.test.tsx, whose
    NODE_CENSUS pins the corpus node counts.
  • tsc --noEmit plus tsc -p tsconfig.test.json for the package — clean. --listFiles
    confirms 17 of 17 test files are in the type-check program, including the new one, so
    "typecheck clean" actually covers it.
  • check-control-bytesOK (scanned 5649 tracked text file(s)).
  • check-type-check-coverage41/41 packages compile their tests.
  • check-changeset-presenceNo source of a released package changed in this range, so no changeset is owed; a changeset with empty frontmatter is added anyway, declaring "no
    release" explicitly (objectui#3387's first-class form). check-changeset-no-major green.
  • regenerate:checkexamples/schema-catalog/src/index.ts is up to date (431 entries).
  • eslint: the repo-wide scan is CI's; this is a declared narrowing. eslint reads exactly
    1 of the 10 changed files (the other 9 are .json/.md and eslint itself reports them
    as ignored — that is eslint's own population, not an assumption), and that one file is
    0 errors, 0 warnings via --format json. No type-aware linting is configured
    (eslint.config.js declares no parserOptions.project or projectService), so this diff
    cannot move any rule's verdict on an untouched file.

⛔ The skip-changeset label is not applied: in this repo that label object exists but no
workflow reads it, and a real changeset is present.

Generated by Claude Code


Generated by Claude Code

…n` for scroll-area, `label` for badge
The seven `components-complex-scroll-area` demos authored their body under
`content`, which `scroll-area.tsx` never reads (it reads
`renderChildren(schema.children)`), so all seven boxes drew empty. The two
counts in `sidebar-with-badges` authored `content` too, which `badge.tsx`
never reads (it reads `schema.label || renderChildren(schema.body)`), so a
demo named `sidebar-with-badges` drew a sidebar with no badges.
The two members share a defect shape and NOT a repair key: `scroll-area` takes
`children`, `badge` takes `label`. A blind `content` -> `children` sweep
repairs the first and silently leaves both badges empty. Measured, and pinned
by a counter-probe.
`text` nodes authoring `content` are excluded by name: `text.tsx` reads
`schema.content || schema.value`, so those render correctly — including this
fixture's own sibling `text` nodes.
Adds a per-renderer corpus sweep in the shape #6788 established for `card`,
each renderer judged against its own read set.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRJge11jso9TpXRWFt1Z49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants

@os-sam@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge - #6832

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key
Aug 30, 2026
Merged

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge#6832
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key

Conversation

@claude

@claudeclaudeBot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Fixes#6805
Fixes#6806

Folded deliberately: the two cards share a defect shape and not a repair key, and that
divergence is the point.

noderenderer readskey authored now
scroll-arearenderChildren(schema.children)children (7 nodes, the whole category)
badgeschema.label || renderChildren(schema.body)label (2 nodes)

⛔ A blind content to children sweep repairs #6805 and silently leaves both badges empty,
behind a green "no content left in the catalog" assertion. The shared method is author the
key this renderer actually reads
, never rename content to children.

⛔ Neither renderer was taught to read content. The renderer is the contract (AGENTS.md
#0.1), and a second dialect for one slot on a published surface is the opposite of the
repair. For badge, ⛔ not body either even though the renderer reads it: #6771 is
retiring body as a children dialect, and 31 badge nodes already author label.

What moved

  • examples/schema-catalog/src/schemas/components-complex-scroll-area/*.json — 7 files, root
    content becomes children. Nothing else in those files changed; the 167 nested content
    keys are all on text nodes and were left alone (see the exclusion list).
  • examples/schema-catalog/src/schemas/components-basic-sidebar/sidebar-with-badges.json
    the two badge nodes' content becomes label. Its five sibling text nodes still
    author content, untouched.
  • examples/schema-catalog/test/catalog-authored-key-6805-6806.test.tsx — new, 27 tests.
  • .changeset/6805-6806-catalog-authored-key.md — empty frontmatter (no release).

Measured through the real SchemaRenderer, the way the docs gallery renders:

entry before after
…scroll-area/tall-300px elements=5 leaked=1 text=(css) 36 0 …"Line 1Line 2Line 3"…
…scroll-area/chat-messages elements=5 leaked=1 text=(css) 81 0 …"AUser 1This is a…"…
…sidebar/sidebar-with-badges elements=22 leaked=2 22 0
text before "InboxDraftsSentTrashMain content area"
text after "Inbox12Drafts3SentTrashMain content area"

leaked counts elements carrying the authored value as the host attribute content — the
#5574 class. All seven scroll-area boxes drew nothing but Radix's own injected scrollbar
stylesheet before this change.

⑤ Exclusion list — named, and encoded in the test

text nodes authoring content are NOT members and were not touched.text.tsx
reads schema.content || schema.value, so they render correctly today.
sidebar-with-badges's own sibling text nodes prove it inside the very fixture #6806
repairs: one key, two renderers, opposite outcomes. A sweep that "fixed" them would break
working demos.

Corpus-wide, content is authored by: text 666 · untyped 30 · sheet 3 · markdown 3 ·
card 1 · tooltip 1 · hover-card 1
(walked over all 431 fixtures). ⛔ So the sweep was not
generalised to "every renderer" — a blanket rule reds on working fixtures. Whether it
generalises at all is #6810's open question; this PR covers only the two renderers these two
cards name.

The exclusion list is executable, not just prose. Three assertions carry it:

  • a text node authoring content draws its string (live control);
  • the corpus still authors content on 600+ text nodes (reads 0 if someone runs the
    blanket sweep);
  • inside both touched families, every remaining content key sits on a text node.

Also excluded: card nodes. One catalog card still authors content
(components-overlay-context-menu/basic-context-menu); that is #6788's fence, in flight on
its own branch, and sweeping it here would red this PR on someone else's work.

④ Per-member named assertions, and the ablation

Each member has its own named assertion naming its own key and asserting its own text
reaches the DOM. Ablated in three legs from the committed fix, reverting fixtures only —
git checkout HEAD -- ... restore proven by an empty git diff HEAD, and each mutation
proven on disk by counting the anchor text in both directions before running:

legfixtures revertedresult
Ascroll-area only (7 files)10 failed / 17 passed
Bbadge only (1 file)6 failed / 21 passed
Cboth15 failed / 12 passed
none (the PR)27 passed

⭐ The isolation is the whole argument for the fold: in leg A every badge assertion stays
green, and in leg B every scroll-area assertion stays green. Neither card's pin
covers the other. (A + B = 16, C = 15: the one overlap is the exclusion assertion, which
both legs break.)

Named failures in leg A — 7 of 10 are one per scroll-area member:

× no `scroll-area` node authors `content` any more — the defect these cards close
× every `scroll-area` node authors only keys `scroll-area.tsx` READS
× components-complex-scroll-area/chat-messages authors `children` (not `content`) and its own text reaches the DOM
× …/code-preview … × …/document-browser … × …/horizontal-scroll …
× …/short-150px … × …/tall-300px … × …/vertical-scroll …
× inside both touched families, every remaining `content` sits on a `text` node

Leg B, all 6 — two of them one per badge member:

× no `badge` node authors `content` any more — the defect these cards close
× every `badge` node authors only keys `badge.tsx` READS, except the #6829 ledger
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× the whole demo draws each count beside its own item — the card's acceptance criterion
× inside both touched families, every remaining `content` sits on a `text` node

⭐ The single most load-bearing assertion is the counter-probe named the WRONG
generalisation
: a badge repaired to children is measured to still draw an empty pill,
so the mistake this fold exists to prevent is now mechanically red rather than a comment.

Why the existing control did not catch either — two different holes

catalog-gallery-render.test.tsx renders both families today and passes. Its non-vacuity
control is elements > WRAPPER_ELEMENTS || text:

⚠️ Deliberately not addressed by raising a global non-vacuity threshold — that is out of
scope and would red on working fixtures. The per-renderer sweep is the right instrument, and
it is what both cards proposed themselves.

Deviation from the dispatch, stated rather than papered over

The dispatch asked to extend #6788's corpus sweep in
examples/schema-catalog/test/card-demo-content-6788.test.tsx. That file is not on
origin/main
— it exists only on the unmerged branch
claude/issue-6788-context-menu-demo-content. Creating it here would collide with that PR on
the same path. So the sweep was written as its own file in the same shape (the #6773/#6788
precedent), with a per-renderer table (RENDERERS) that a later PR can fold card into once
#6788 lands. The fold's stated purpose — one PR, one edit, no conflict between #6805 and
#6806 — is met: both renderers are swept in one file, once.

Out of fence, filed not fixed

#6829 — while censusing badge nodes to size this sweep, seven more were found
authoring children, which badge.tsx does not read (unlike card.tsx, which reads
children || body). Three demos in components-basic-span draw literally nothing;
core-schema-renderer/nested-schema-example silently drops two badges. That card is the same
class under a different key, and it is left open here for two reasons: the repair is a real
decision (re-author the fixtures to label, or widen the renderer's read set), and it is out
of this PR's fence. It is a fifth rediscovery of the class #6810 is deciding about, and
the first one the #5574 leak signature cannot find at all — those seven leak nothing.

Those seven are carried in this PR's badge sweep as an exact ledger
(BADGE_CHILDREN_LEDGER_6829), not a floor, so an eighth node turns the sweep red rather
than joining a growing allowance.

That census also measured a third hole in the same non-vacuity control, distinct from the
two above: an empty badge's own host element is the third element, so
elements=3 > WRAPPER_ELEMENTS=2 with text="" reads as drewSomething=true. Recorded on
#6829.

Verification

All of the below ran on a clean tree at ec115764f, this PR's final commit.

  • pnpm exec vitest run examples/schema-catalog/ from the repo root — 17 files, 1893
    tests, all passed
    . The 17 are the whole folder (vitest list enumerates them), and they
    include catalog-gallery-render.test.tsx and layout-dom-leak-5574.test.tsx, whose
    NODE_CENSUS pins the corpus node counts.
  • tsc --noEmit plus tsc -p tsconfig.test.json for the package — clean. --listFiles
    confirms 17 of 17 test files are in the type-check program, including the new one, so
    "typecheck clean" actually covers it.
  • check-control-bytesOK (scanned 5649 tracked text file(s)).
  • check-type-check-coverage41/41 packages compile their tests.
  • check-changeset-presenceNo source of a released package changed in this range, so no changeset is owed; a changeset with empty frontmatter is added anyway, declaring "no
    release" explicitly (objectui#3387's first-class form). check-changeset-no-major green.
  • regenerate:checkexamples/schema-catalog/src/index.ts is up to date (431 entries).
  • eslint: the repo-wide scan is CI's; this is a declared narrowing. eslint reads exactly
    1 of the 10 changed files (the other 9 are .json/.md and eslint itself reports them
    as ignored — that is eslint's own population, not an assumption), and that one file is
    0 errors, 0 warnings via --format json. No type-aware linting is configured
    (eslint.config.js declares no parserOptions.project or projectService), so this diff
    cannot move any rule's verdict on an untouched file.

⛔ The skip-changeset label is not applied: in this repo that label object exists but no
workflow reads it, and a real changeset is present.

Generated by Claude Code


Generated by Claude Code

…n` for scroll-area, `label` for badge
The seven `components-complex-scroll-area` demos authored their body under
`content`, which `scroll-area.tsx` never reads (it reads
`renderChildren(schema.children)`), so all seven boxes drew empty. The two
counts in `sidebar-with-badges` authored `content` too, which `badge.tsx`
never reads (it reads `schema.label || renderChildren(schema.body)`), so a
demo named `sidebar-with-badges` drew a sidebar with no badges.
The two members share a defect shape and NOT a repair key: `scroll-area` takes
`children`, `badge` takes `label`. A blind `content` -> `children` sweep
repairs the first and silently leaves both badges empty. Measured, and pinned
by a counter-probe.
`text` nodes authoring `content` are excluded by name: `text.tsx` reads
`schema.content || schema.value`, so those render correctly — including this
fixture's own sibling `text` nodes.
Adds a per-renderer corpus sweep in the shape #6788 established for `card`,
each renderer judged against its own read set.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRJge11jso9TpXRWFt1Z49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants

@os-sam@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge - #6832

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key
Aug 30, 2026
Merged

fix(examples): author the key each renderer actually reads — children for scroll-area, label for badge#6832
os-sam merged 1 commit into
mainfrom
claude/issue-6805-catalog-authored-key

Conversation

@claude

@claudeclaudeBot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Fixes#6805
Fixes#6806

Folded deliberately: the two cards share a defect shape and not a repair key, and that
divergence is the point.

noderenderer readskey authored now
scroll-arearenderChildren(schema.children)children (7 nodes, the whole category)
badgeschema.label || renderChildren(schema.body)label (2 nodes)

⛔ A blind content to children sweep repairs #6805 and silently leaves both badges empty,
behind a green "no content left in the catalog" assertion. The shared method is author the
key this renderer actually reads
, never rename content to children.

⛔ Neither renderer was taught to read content. The renderer is the contract (AGENTS.md
#0.1), and a second dialect for one slot on a published surface is the opposite of the
repair. For badge, ⛔ not body either even though the renderer reads it: #6771 is
retiring body as a children dialect, and 31 badge nodes already author label.

What moved

  • examples/schema-catalog/src/schemas/components-complex-scroll-area/*.json — 7 files, root
    content becomes children. Nothing else in those files changed; the 167 nested content
    keys are all on text nodes and were left alone (see the exclusion list).
  • examples/schema-catalog/src/schemas/components-basic-sidebar/sidebar-with-badges.json
    the two badge nodes' content becomes label. Its five sibling text nodes still
    author content, untouched.
  • examples/schema-catalog/test/catalog-authored-key-6805-6806.test.tsx — new, 27 tests.
  • .changeset/6805-6806-catalog-authored-key.md — empty frontmatter (no release).

Measured through the real SchemaRenderer, the way the docs gallery renders:

entry before after
…scroll-area/tall-300px elements=5 leaked=1 text=(css) 36 0 …"Line 1Line 2Line 3"…
…scroll-area/chat-messages elements=5 leaked=1 text=(css) 81 0 …"AUser 1This is a…"…
…sidebar/sidebar-with-badges elements=22 leaked=2 22 0
text before "InboxDraftsSentTrashMain content area"
text after "Inbox12Drafts3SentTrashMain content area"

leaked counts elements carrying the authored value as the host attribute content — the
#5574 class. All seven scroll-area boxes drew nothing but Radix's own injected scrollbar
stylesheet before this change.

⑤ Exclusion list — named, and encoded in the test

text nodes authoring content are NOT members and were not touched.text.tsx
reads schema.content || schema.value, so they render correctly today.
sidebar-with-badges's own sibling text nodes prove it inside the very fixture #6806
repairs: one key, two renderers, opposite outcomes. A sweep that "fixed" them would break
working demos.

Corpus-wide, content is authored by: text 666 · untyped 30 · sheet 3 · markdown 3 ·
card 1 · tooltip 1 · hover-card 1
(walked over all 431 fixtures). ⛔ So the sweep was not
generalised to "every renderer" — a blanket rule reds on working fixtures. Whether it
generalises at all is #6810's open question; this PR covers only the two renderers these two
cards name.

The exclusion list is executable, not just prose. Three assertions carry it:

  • a text node authoring content draws its string (live control);
  • the corpus still authors content on 600+ text nodes (reads 0 if someone runs the
    blanket sweep);
  • inside both touched families, every remaining content key sits on a text node.

Also excluded: card nodes. One catalog card still authors content
(components-overlay-context-menu/basic-context-menu); that is #6788's fence, in flight on
its own branch, and sweeping it here would red this PR on someone else's work.

④ Per-member named assertions, and the ablation

Each member has its own named assertion naming its own key and asserting its own text
reaches the DOM. Ablated in three legs from the committed fix, reverting fixtures only —
git checkout HEAD -- ... restore proven by an empty git diff HEAD, and each mutation
proven on disk by counting the anchor text in both directions before running:

legfixtures revertedresult
Ascroll-area only (7 files)10 failed / 17 passed
Bbadge only (1 file)6 failed / 21 passed
Cboth15 failed / 12 passed
none (the PR)27 passed

⭐ The isolation is the whole argument for the fold: in leg A every badge assertion stays
green, and in leg B every scroll-area assertion stays green. Neither card's pin
covers the other. (A + B = 16, C = 15: the one overlap is the exclusion assertion, which
both legs break.)

Named failures in leg A — 7 of 10 are one per scroll-area member:

× no `scroll-area` node authors `content` any more — the defect these cards close
× every `scroll-area` node authors only keys `scroll-area.tsx` READS
× components-complex-scroll-area/chat-messages authors `children` (not `content`) and its own text reaches the DOM
× …/code-preview … × …/document-browser … × …/horizontal-scroll …
× …/short-150px … × …/tall-300px … × …/vertical-scroll …
× inside both touched families, every remaining `content` sits on a `text` node

Leg B, all 6 — two of them one per badge member:

× no `badge` node authors `content` any more — the defect these cards close
× every `badge` node authors only keys `badge.tsx` READS, except the #6829 ledger
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× badge …sidebar-with-badges.body[0]…body[1] authors `label` (not `content`) and that count reaches the DOM
× the whole demo draws each count beside its own item — the card's acceptance criterion
× inside both touched families, every remaining `content` sits on a `text` node

⭐ The single most load-bearing assertion is the counter-probe named the WRONG
generalisation
: a badge repaired to children is measured to still draw an empty pill,
so the mistake this fold exists to prevent is now mechanically red rather than a comment.

Why the existing control did not catch either — two different holes

catalog-gallery-render.test.tsx renders both families today and passes. Its non-vacuity
control is elements > WRAPPER_ELEMENTS || text:

⚠️ Deliberately not addressed by raising a global non-vacuity threshold — that is out of
scope and would red on working fixtures. The per-renderer sweep is the right instrument, and
it is what both cards proposed themselves.

Deviation from the dispatch, stated rather than papered over

The dispatch asked to extend #6788's corpus sweep in
examples/schema-catalog/test/card-demo-content-6788.test.tsx. That file is not on
origin/main
— it exists only on the unmerged branch
claude/issue-6788-context-menu-demo-content. Creating it here would collide with that PR on
the same path. So the sweep was written as its own file in the same shape (the #6773/#6788
precedent), with a per-renderer table (RENDERERS) that a later PR can fold card into once
#6788 lands. The fold's stated purpose — one PR, one edit, no conflict between #6805 and
#6806 — is met: both renderers are swept in one file, once.

Out of fence, filed not fixed

#6829 — while censusing badge nodes to size this sweep, seven more were found
authoring children, which badge.tsx does not read (unlike card.tsx, which reads
children || body). Three demos in components-basic-span draw literally nothing;
core-schema-renderer/nested-schema-example silently drops two badges. That card is the same
class under a different key, and it is left open here for two reasons: the repair is a real
decision (re-author the fixtures to label, or widen the renderer's read set), and it is out
of this PR's fence. It is a fifth rediscovery of the class #6810 is deciding about, and
the first one the #5574 leak signature cannot find at all — those seven leak nothing.

Those seven are carried in this PR's badge sweep as an exact ledger
(BADGE_CHILDREN_LEDGER_6829), not a floor, so an eighth node turns the sweep red rather
than joining a growing allowance.

That census also measured a third hole in the same non-vacuity control, distinct from the
two above: an empty badge's own host element is the third element, so
elements=3 > WRAPPER_ELEMENTS=2 with text="" reads as drewSomething=true. Recorded on
#6829.

Verification

All of the below ran on a clean tree at ec115764f, this PR's final commit.

  • pnpm exec vitest run examples/schema-catalog/ from the repo root — 17 files, 1893
    tests, all passed
    . The 17 are the whole folder (vitest list enumerates them), and they
    include catalog-gallery-render.test.tsx and layout-dom-leak-5574.test.tsx, whose
    NODE_CENSUS pins the corpus node counts.
  • tsc --noEmit plus tsc -p tsconfig.test.json for the package — clean. --listFiles
    confirms 17 of 17 test files are in the type-check program, including the new one, so
    "typecheck clean" actually covers it.
  • check-control-bytesOK (scanned 5649 tracked text file(s)).
  • check-type-check-coverage41/41 packages compile their tests.
  • check-changeset-presenceNo source of a released package changed in this range, so no changeset is owed; a changeset with empty frontmatter is added anyway, declaring "no
    release" explicitly (objectui#3387's first-class form). check-changeset-no-major green.
  • regenerate:checkexamples/schema-catalog/src/index.ts is up to date (431 entries).
  • eslint: the repo-wide scan is CI's; this is a declared narrowing. eslint reads exactly
    1 of the 10 changed files (the other 9 are .json/.md and eslint itself reports them
    as ignored — that is eslint's own population, not an assumption), and that one file is
    0 errors, 0 warnings via --format json. No type-aware linting is configured
    (eslint.config.js declares no parserOptions.project or projectService), so this diff
    cannot move any rule's verdict on an untouched file.

⛔ The skip-changeset label is not applied: in this repo that label object exists but no
workflow reads it, and a real changeset is present.

Generated by Claude Code


Generated by Claude Code

…n` for scroll-area, `label` for badge
The seven `components-complex-scroll-area` demos authored their body under
`content`, which `scroll-area.tsx` never reads (it reads
`renderChildren(schema.children)`), so all seven boxes drew empty. The two
counts in `sidebar-with-badges` authored `content` too, which `badge.tsx`
never reads (it reads `schema.label || renderChildren(schema.body)`), so a
demo named `sidebar-with-badges` drew a sidebar with no badges.
The two members share a defect shape and NOT a repair key: `scroll-area` takes
`children`, `badge` takes `label`. A blind `content` -> `children` sweep
repairs the first and silently leaves both badges empty. Measured, and pinned
by a counter-probe.
`text` nodes authoring `content` are excluded by name: `text.tsx` reads
`schema.content || schema.value`, so those render correctly — including this
fixture's own sibling `text` nodes.
Adds a per-renderer corpus sweep in the shape #6788 established for `card`,
each renderer judged against its own read set.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRJge11jso9TpXRWFt1Z49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants

@os-sam@claude