docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node - #5937

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth
Aug 24, 2026
Merged

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node#5937
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth

Conversation

@os-zhuang

@os-zhuangos-zhuang commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Part of #4895

Carries the maintainer ruling recorded on that card (comment 5339693569, 2026-08-19, 「全部接受」): Option B — docs-truth fix, the block family re-scoped as type-level. Option A (build renderers) was rejected on zero pull; Option C (retire the family) is deferred. Neither is implemented here, and neither is argued for.

Reference keyword is deliberately Part of, not a closing keyword: the ruling leaves Option C open, to be revisited if a later liveness pass shows the published types inert externally. The measurement below strengthens that future case rather than settling it, so the card should stay open for the PM seat to judge.

What was wrong

content/docs/blocks/block-schema.mdx:254 taught this inside BlockSchema.template:

{
type: 'slot',
name: 'content'
}

template is typed SchemaNode | SchemaNode[], so that snippet sat on the render path — and nothing registers slot. A reader who copied it got the renderer's OBJUI-001 "Unknown component type" panel. That node is deleted; the page now teaches slotContent, the key BlockSchema and BlockInstanceSchema actually declare.

The measurement that changed how this is written

The ruling says "teach slotContent". Before writing that, I measured whether slotContent is consumed by anything — a renderer, a hook, an adapter. It is not.

Every occurrence in the repository, excluding node_modules and dist:

sitewhat it is
packages/types/src/blocks.ts:201BlockSchema.slotContent declaration
packages/types/src/blocks.ts:377BlockInstanceSchema.slotContent declaration
packages/types/src/zod/blocks.zod.ts:77Zod mirror
packages/types/src/zod/blocks.zod.ts:134Zod mirror
content/docs/blocks/block-schema.mdx:297this page
scripts/check-doc-component-types.mjs:283the exemption reason this PR deletes

Zero renderers, zero hooks, zero adapters. The same holds for slots and template on this family — outside packages/types (the interfaces, the Zod mirror, and that package's own parse test) nothing reads them.

So the page does not present slotContent as the working path. It documents it as the declared path and states plainly that it is not yet consumed. Rewriting one phantom into a second phantom is the failure this card exists to close, so the page carries a status callout up front rather than implying a runtime that does not exist.

Two supporting measurements are written into the page because they are what a confused reader needs:

  • The four SchemaExample demos the page embeds are ordinary registered component trees (card, flex, stack, text, icon, button, badge). None carries type: 'block', slots or slotContent — nothing on the page was ever exercising the block vocabulary.
  • The slot system that is wired end to end is a different family: slotted record pages (kind: "slotted", page.slots), consumed by usePageAssignment, PageBlockCanvas and PageBlockInspector, documented at content/docs/guide/slotted-pages.md. The page now links there so the two slots spellings are not conflated.

Full vocabulary audit, not just slot

Every type literal the page teaches, checked against the gate's derived registered-key universe (659 keys). Line numbers are pre-change.

valuelinesregistered?vocabularyaction
block44, 140noBlockSchema discriminantkeep, exempted — a definition
block-editor356noBlockEditorSchema discriminantkeep, exempted
block-instance283noBlockInstanceSchema discriminantkeep, exempted
block-library320, 374noBlockLibrarySchema discriminantkeep, exempted
boolean62, 192yesBlockVariable.type data typeno change
button258yesSDUI nodeno change
card223yesSDUI nodeno change
div75, 227, 239yesSDUI nodeno change
icon231yesSDUI nodeno change
list300yesSDUI nodeno change
slot254nonothing — phantom, on the render pathdeleted
string56, 162, 170, 177, 185, 199noBlockVariable.type data typekeep, exempted
text243, 249yesSDUI nodeno change

slot is the only unregistered value that named a renderable node. The other five unregistered values are each a discriminant of a declared interface or a variable data type, and each keeps a live exemption. So there was no second slot-shaped defect to sweep up.

One incidental observation, not a defect on this page and not changed: boolean at :62 and :192 is a BlockVariable.type data type that happens to collide with a registered component key, so it passes the gate's flat rule without needing an exemption. The page is correct; the gate's judgment there is a coincidence rather than a check.

Gate calibration — proving the silence is a measurement

Removing the exemption is only meaningful if check-doc-component-types.mjs actually reads this file. A gate that is silent because it never looked is not a passing gate, so this was calibrated rather than assumed. The mutation was confirmed on disk by grep count, never by the editing tool's exit code, and the script carried a trap ... EXIT INT TERM restore.

Mutation leg — rewrite the registered type: 'card' node at :223 to a value nothing registers:

"type: 'card'," 1 -> 0 (deleted text)
'objui-4895-calibration-probe' 0 -> 1 (injected text)

Gate exit code, captured before any pipe: 1

content/docs/blocks/block-schema.mdx:223 [unregistered-doc-type] type 'objui-4895-calibration-probe' (plaintext)
type: 'objui-4895-calibration-probe',

Restore leg — probe absent, anchor back, git status clean for the file. Gate exit code: 0, ✅ Every documented component type is registered.

The gate reads this page.

The two halves are a matched pair

Both failure directions were exercised, so neither half is passing by accident:

  • Doc corrected but the exemption put backstale-exemption: content/docs/blocks/block-schema.mdx -> slot no code block in that file spells this type any more.
  • Exemption dropped but the phantom node put back → exit 1, content/docs/blocks/block-schema.mdx:305 [unregistered-doc-type] type 'slot' (plaintext)

Only both halves together are green.

Gate ledger

The slot entry in DOC_TYPE_EXEMPTIONS pointed at this card and is dropped, as the ruling requires. With the phantom node gone it would itself report as stale-exemption, so it is deleted rather than re-pointed; a comment records why, so the next reader does not re-add it. The three terse family reasons (block-instance / block-library / block-editor) are re-pointed at the ruling's framing — each now records that the schema is absent from AnySchema and has no renderer, which is the fact the gate's own standard asks an exemption reason to name.

Counters move coherently: exempted 139 → 138 (one entry gone), registered 743 → 744 (the new type: 'text' site), code blocks 1055 → 1056.

Verification — union run at ca35295a9 (final commit)

Exit codes captured before any pipe; verdicts quoted from each gate's own output.

gateexitverdict line
check-doc-component-types0Every documented component type is registered.
check-doc-links0Links are valid across 13 scan roots.
check-control-bytes0check-control-bytes: OK (scanned 4929 tracked text file(s); skipped 85 binary).
check-changeset-presence0No source of a released package changed in this range, so no changeset is owed.
check-changeset-fixed0All workspace packages are in the changeset fixed group.
check-changeset-no-major0No changeset declares a major bump.

scripts/__tests__/check-doc-component-types.test.ts34 passed (34), run at ca35295a9 from the repo root. Its three live-table assertions were also evaluated directly against this diff: stale-exemption findings []; exempted 138 (asserted > 50); declaration count 82 → 81 (asserted > 20).

eslint scripts/check-doc-component-types.mjs --no-inline-config --format json — 1 file linted, 0 errors, 0 warnings.

Declared narrowing: check-doc-snippet-types

Not run to completion — it needs a full monorepo build. The narrowing is a measurement, taken with that gate's own scanner, not an assumption:

  1. Its fence-language set is TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']) (its own source, line 224).
  2. scanFences() on this page returns blocks=0, markers=0 in both states — at HEAD~1 and on the working tree. Every fence on the page is plaintext (10 → 11), and the one I added is plaintext too.
  3. The page is not named in UNGATED_DOCS, so no ledger entry of mine can go stale.

The gate collects zero blocks from this page either way, so this diff cannot change its verdict. CI runs it in full regardless.

Changeset

.changeset/block-schema-docs-truth-4895.md, empty frontmatter — docs and a gate ledger only, no released package src/ is touched, declared explicitly rather than left undeclared. objectui has no skip-changeset label; the empty-frontmatter changeset is this repo's declaration mechanism.

Deliberately not done

  • No renderers built for block-library / block-editor / block-instance (Option A, rejected on zero pull).
  • Nothing retired from packages/types (Option C, deferred). No published type is touched.
  • Best Practices and Use Cases left as-is. Both still read somewhat aspirationally, but the status callout now governs the whole page, and gutting them is beyond the ruling's scope. Flagging rather than silently widening the diff.

Generated by Claude Code

)
Carries the recorded maintainer ruling on #4895 (Option B: docs-truth fix,
family re-scoped as type-level).
- Delete the phantom `type: 'slot'` node from the Complete Example template.
Nothing registers `slot`, so a reader who copied it got OBJUI-001.
- Teach the declared `slotContent` path instead, and state plainly that it is
declared-but-not-yet-consumed: nothing reads `slots` / `slotContent` /
`template` at runtime.
- Frame `BlockSchema` as a definition and drop the component-schema framing for
`block-library` / `block-editor` / `block-instance`.
- Disambiguate from the slot system that IS wired end to end (slotted record
pages), which is an unrelated vocabulary.
- Drop the now-stale `slot` entry from DOC_TYPE_EXEMPTIONS in
scripts/check-doc-component-types.mjs, and re-point the three terse family
reasons at the ruling's framing.
Option A (build renderers) is rejected on zero pull; Option C (retire the
family) is deferred. Neither is implemented here.
Empty frontmatter: docs and a gate ledger only, no released package `src/`
is touched. Declared explicitly rather than left undeclared.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@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

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node - #5937

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth
Aug 24, 2026
Merged

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node#5937
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth

Conversation

@os-zhuang

@os-zhuangos-zhuang commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Part of #4895

Carries the maintainer ruling recorded on that card (comment 5339693569, 2026-08-19, 「全部接受」): Option B — docs-truth fix, the block family re-scoped as type-level. Option A (build renderers) was rejected on zero pull; Option C (retire the family) is deferred. Neither is implemented here, and neither is argued for.

Reference keyword is deliberately Part of, not a closing keyword: the ruling leaves Option C open, to be revisited if a later liveness pass shows the published types inert externally. The measurement below strengthens that future case rather than settling it, so the card should stay open for the PM seat to judge.

What was wrong

content/docs/blocks/block-schema.mdx:254 taught this inside BlockSchema.template:

{
type: 'slot',
name: 'content'
}

template is typed SchemaNode | SchemaNode[], so that snippet sat on the render path — and nothing registers slot. A reader who copied it got the renderer's OBJUI-001 "Unknown component type" panel. That node is deleted; the page now teaches slotContent, the key BlockSchema and BlockInstanceSchema actually declare.

The measurement that changed how this is written

The ruling says "teach slotContent". Before writing that, I measured whether slotContent is consumed by anything — a renderer, a hook, an adapter. It is not.

Every occurrence in the repository, excluding node_modules and dist:

sitewhat it is
packages/types/src/blocks.ts:201BlockSchema.slotContent declaration
packages/types/src/blocks.ts:377BlockInstanceSchema.slotContent declaration
packages/types/src/zod/blocks.zod.ts:77Zod mirror
packages/types/src/zod/blocks.zod.ts:134Zod mirror
content/docs/blocks/block-schema.mdx:297this page
scripts/check-doc-component-types.mjs:283the exemption reason this PR deletes

Zero renderers, zero hooks, zero adapters. The same holds for slots and template on this family — outside packages/types (the interfaces, the Zod mirror, and that package's own parse test) nothing reads them.

So the page does not present slotContent as the working path. It documents it as the declared path and states plainly that it is not yet consumed. Rewriting one phantom into a second phantom is the failure this card exists to close, so the page carries a status callout up front rather than implying a runtime that does not exist.

Two supporting measurements are written into the page because they are what a confused reader needs:

  • The four SchemaExample demos the page embeds are ordinary registered component trees (card, flex, stack, text, icon, button, badge). None carries type: 'block', slots or slotContent — nothing on the page was ever exercising the block vocabulary.
  • The slot system that is wired end to end is a different family: slotted record pages (kind: "slotted", page.slots), consumed by usePageAssignment, PageBlockCanvas and PageBlockInspector, documented at content/docs/guide/slotted-pages.md. The page now links there so the two slots spellings are not conflated.

Full vocabulary audit, not just slot

Every type literal the page teaches, checked against the gate's derived registered-key universe (659 keys). Line numbers are pre-change.

valuelinesregistered?vocabularyaction
block44, 140noBlockSchema discriminantkeep, exempted — a definition
block-editor356noBlockEditorSchema discriminantkeep, exempted
block-instance283noBlockInstanceSchema discriminantkeep, exempted
block-library320, 374noBlockLibrarySchema discriminantkeep, exempted
boolean62, 192yesBlockVariable.type data typeno change
button258yesSDUI nodeno change
card223yesSDUI nodeno change
div75, 227, 239yesSDUI nodeno change
icon231yesSDUI nodeno change
list300yesSDUI nodeno change
slot254nonothing — phantom, on the render pathdeleted
string56, 162, 170, 177, 185, 199noBlockVariable.type data typekeep, exempted
text243, 249yesSDUI nodeno change

slot is the only unregistered value that named a renderable node. The other five unregistered values are each a discriminant of a declared interface or a variable data type, and each keeps a live exemption. So there was no second slot-shaped defect to sweep up.

One incidental observation, not a defect on this page and not changed: boolean at :62 and :192 is a BlockVariable.type data type that happens to collide with a registered component key, so it passes the gate's flat rule without needing an exemption. The page is correct; the gate's judgment there is a coincidence rather than a check.

Gate calibration — proving the silence is a measurement

Removing the exemption is only meaningful if check-doc-component-types.mjs actually reads this file. A gate that is silent because it never looked is not a passing gate, so this was calibrated rather than assumed. The mutation was confirmed on disk by grep count, never by the editing tool's exit code, and the script carried a trap ... EXIT INT TERM restore.

Mutation leg — rewrite the registered type: 'card' node at :223 to a value nothing registers:

"type: 'card'," 1 -> 0 (deleted text)
'objui-4895-calibration-probe' 0 -> 1 (injected text)

Gate exit code, captured before any pipe: 1

content/docs/blocks/block-schema.mdx:223 [unregistered-doc-type] type 'objui-4895-calibration-probe' (plaintext)
type: 'objui-4895-calibration-probe',

Restore leg — probe absent, anchor back, git status clean for the file. Gate exit code: 0, ✅ Every documented component type is registered.

The gate reads this page.

The two halves are a matched pair

Both failure directions were exercised, so neither half is passing by accident:

  • Doc corrected but the exemption put backstale-exemption: content/docs/blocks/block-schema.mdx -> slot no code block in that file spells this type any more.
  • Exemption dropped but the phantom node put back → exit 1, content/docs/blocks/block-schema.mdx:305 [unregistered-doc-type] type 'slot' (plaintext)

Only both halves together are green.

Gate ledger

The slot entry in DOC_TYPE_EXEMPTIONS pointed at this card and is dropped, as the ruling requires. With the phantom node gone it would itself report as stale-exemption, so it is deleted rather than re-pointed; a comment records why, so the next reader does not re-add it. The three terse family reasons (block-instance / block-library / block-editor) are re-pointed at the ruling's framing — each now records that the schema is absent from AnySchema and has no renderer, which is the fact the gate's own standard asks an exemption reason to name.

Counters move coherently: exempted 139 → 138 (one entry gone), registered 743 → 744 (the new type: 'text' site), code blocks 1055 → 1056.

Verification — union run at ca35295a9 (final commit)

Exit codes captured before any pipe; verdicts quoted from each gate's own output.

gateexitverdict line
check-doc-component-types0Every documented component type is registered.
check-doc-links0Links are valid across 13 scan roots.
check-control-bytes0check-control-bytes: OK (scanned 4929 tracked text file(s); skipped 85 binary).
check-changeset-presence0No source of a released package changed in this range, so no changeset is owed.
check-changeset-fixed0All workspace packages are in the changeset fixed group.
check-changeset-no-major0No changeset declares a major bump.

scripts/__tests__/check-doc-component-types.test.ts34 passed (34), run at ca35295a9 from the repo root. Its three live-table assertions were also evaluated directly against this diff: stale-exemption findings []; exempted 138 (asserted > 50); declaration count 82 → 81 (asserted > 20).

eslint scripts/check-doc-component-types.mjs --no-inline-config --format json — 1 file linted, 0 errors, 0 warnings.

Declared narrowing: check-doc-snippet-types

Not run to completion — it needs a full monorepo build. The narrowing is a measurement, taken with that gate's own scanner, not an assumption:

  1. Its fence-language set is TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']) (its own source, line 224).
  2. scanFences() on this page returns blocks=0, markers=0 in both states — at HEAD~1 and on the working tree. Every fence on the page is plaintext (10 → 11), and the one I added is plaintext too.
  3. The page is not named in UNGATED_DOCS, so no ledger entry of mine can go stale.

The gate collects zero blocks from this page either way, so this diff cannot change its verdict. CI runs it in full regardless.

Changeset

.changeset/block-schema-docs-truth-4895.md, empty frontmatter — docs and a gate ledger only, no released package src/ is touched, declared explicitly rather than left undeclared. objectui has no skip-changeset label; the empty-frontmatter changeset is this repo's declaration mechanism.

Deliberately not done

  • No renderers built for block-library / block-editor / block-instance (Option A, rejected on zero pull).
  • Nothing retired from packages/types (Option C, deferred). No published type is touched.
  • Best Practices and Use Cases left as-is. Both still read somewhat aspirationally, but the status callout now governs the whole page, and gutting them is beyond the ruling's scope. Flagging rather than silently widening the diff.

Generated by Claude Code

)
Carries the recorded maintainer ruling on #4895 (Option B: docs-truth fix,
family re-scoped as type-level).
- Delete the phantom `type: 'slot'` node from the Complete Example template.
Nothing registers `slot`, so a reader who copied it got OBJUI-001.
- Teach the declared `slotContent` path instead, and state plainly that it is
declared-but-not-yet-consumed: nothing reads `slots` / `slotContent` /
`template` at runtime.
- Frame `BlockSchema` as a definition and drop the component-schema framing for
`block-library` / `block-editor` / `block-instance`.
- Disambiguate from the slot system that IS wired end to end (slotted record
pages), which is an unrelated vocabulary.
- Drop the now-stale `slot` entry from DOC_TYPE_EXEMPTIONS in
scripts/check-doc-component-types.mjs, and re-point the three terse family
reasons at the ruling's framing.
Option A (build renderers) is rejected on zero pull; Option C (retire the
family) is deferred. Neither is implemented here.
Empty frontmatter: docs and a gate ledger only, no released package `src/`
is touched. Declared explicitly rather than left undeclared.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@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

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node - #5937

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth
Aug 24, 2026
Merged

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node#5937
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth

Conversation

@os-zhuang

@os-zhuangos-zhuang commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Part of #4895

Carries the maintainer ruling recorded on that card (comment 5339693569, 2026-08-19, 「全部接受」): Option B — docs-truth fix, the block family re-scoped as type-level. Option A (build renderers) was rejected on zero pull; Option C (retire the family) is deferred. Neither is implemented here, and neither is argued for.

Reference keyword is deliberately Part of, not a closing keyword: the ruling leaves Option C open, to be revisited if a later liveness pass shows the published types inert externally. The measurement below strengthens that future case rather than settling it, so the card should stay open for the PM seat to judge.

What was wrong

content/docs/blocks/block-schema.mdx:254 taught this inside BlockSchema.template:

{
type: 'slot',
name: 'content'
}

template is typed SchemaNode | SchemaNode[], so that snippet sat on the render path — and nothing registers slot. A reader who copied it got the renderer's OBJUI-001 "Unknown component type" panel. That node is deleted; the page now teaches slotContent, the key BlockSchema and BlockInstanceSchema actually declare.

The measurement that changed how this is written

The ruling says "teach slotContent". Before writing that, I measured whether slotContent is consumed by anything — a renderer, a hook, an adapter. It is not.

Every occurrence in the repository, excluding node_modules and dist:

sitewhat it is
packages/types/src/blocks.ts:201BlockSchema.slotContent declaration
packages/types/src/blocks.ts:377BlockInstanceSchema.slotContent declaration
packages/types/src/zod/blocks.zod.ts:77Zod mirror
packages/types/src/zod/blocks.zod.ts:134Zod mirror
content/docs/blocks/block-schema.mdx:297this page
scripts/check-doc-component-types.mjs:283the exemption reason this PR deletes

Zero renderers, zero hooks, zero adapters. The same holds for slots and template on this family — outside packages/types (the interfaces, the Zod mirror, and that package's own parse test) nothing reads them.

So the page does not present slotContent as the working path. It documents it as the declared path and states plainly that it is not yet consumed. Rewriting one phantom into a second phantom is the failure this card exists to close, so the page carries a status callout up front rather than implying a runtime that does not exist.

Two supporting measurements are written into the page because they are what a confused reader needs:

  • The four SchemaExample demos the page embeds are ordinary registered component trees (card, flex, stack, text, icon, button, badge). None carries type: 'block', slots or slotContent — nothing on the page was ever exercising the block vocabulary.
  • The slot system that is wired end to end is a different family: slotted record pages (kind: "slotted", page.slots), consumed by usePageAssignment, PageBlockCanvas and PageBlockInspector, documented at content/docs/guide/slotted-pages.md. The page now links there so the two slots spellings are not conflated.

Full vocabulary audit, not just slot

Every type literal the page teaches, checked against the gate's derived registered-key universe (659 keys). Line numbers are pre-change.

valuelinesregistered?vocabularyaction
block44, 140noBlockSchema discriminantkeep, exempted — a definition
block-editor356noBlockEditorSchema discriminantkeep, exempted
block-instance283noBlockInstanceSchema discriminantkeep, exempted
block-library320, 374noBlockLibrarySchema discriminantkeep, exempted
boolean62, 192yesBlockVariable.type data typeno change
button258yesSDUI nodeno change
card223yesSDUI nodeno change
div75, 227, 239yesSDUI nodeno change
icon231yesSDUI nodeno change
list300yesSDUI nodeno change
slot254nonothing — phantom, on the render pathdeleted
string56, 162, 170, 177, 185, 199noBlockVariable.type data typekeep, exempted
text243, 249yesSDUI nodeno change

slot is the only unregistered value that named a renderable node. The other five unregistered values are each a discriminant of a declared interface or a variable data type, and each keeps a live exemption. So there was no second slot-shaped defect to sweep up.

One incidental observation, not a defect on this page and not changed: boolean at :62 and :192 is a BlockVariable.type data type that happens to collide with a registered component key, so it passes the gate's flat rule without needing an exemption. The page is correct; the gate's judgment there is a coincidence rather than a check.

Gate calibration — proving the silence is a measurement

Removing the exemption is only meaningful if check-doc-component-types.mjs actually reads this file. A gate that is silent because it never looked is not a passing gate, so this was calibrated rather than assumed. The mutation was confirmed on disk by grep count, never by the editing tool's exit code, and the script carried a trap ... EXIT INT TERM restore.

Mutation leg — rewrite the registered type: 'card' node at :223 to a value nothing registers:

"type: 'card'," 1 -> 0 (deleted text)
'objui-4895-calibration-probe' 0 -> 1 (injected text)

Gate exit code, captured before any pipe: 1

content/docs/blocks/block-schema.mdx:223 [unregistered-doc-type] type 'objui-4895-calibration-probe' (plaintext)
type: 'objui-4895-calibration-probe',

Restore leg — probe absent, anchor back, git status clean for the file. Gate exit code: 0, ✅ Every documented component type is registered.

The gate reads this page.

The two halves are a matched pair

Both failure directions were exercised, so neither half is passing by accident:

  • Doc corrected but the exemption put backstale-exemption: content/docs/blocks/block-schema.mdx -> slot no code block in that file spells this type any more.
  • Exemption dropped but the phantom node put back → exit 1, content/docs/blocks/block-schema.mdx:305 [unregistered-doc-type] type 'slot' (plaintext)

Only both halves together are green.

Gate ledger

The slot entry in DOC_TYPE_EXEMPTIONS pointed at this card and is dropped, as the ruling requires. With the phantom node gone it would itself report as stale-exemption, so it is deleted rather than re-pointed; a comment records why, so the next reader does not re-add it. The three terse family reasons (block-instance / block-library / block-editor) are re-pointed at the ruling's framing — each now records that the schema is absent from AnySchema and has no renderer, which is the fact the gate's own standard asks an exemption reason to name.

Counters move coherently: exempted 139 → 138 (one entry gone), registered 743 → 744 (the new type: 'text' site), code blocks 1055 → 1056.

Verification — union run at ca35295a9 (final commit)

Exit codes captured before any pipe; verdicts quoted from each gate's own output.

gateexitverdict line
check-doc-component-types0Every documented component type is registered.
check-doc-links0Links are valid across 13 scan roots.
check-control-bytes0check-control-bytes: OK (scanned 4929 tracked text file(s); skipped 85 binary).
check-changeset-presence0No source of a released package changed in this range, so no changeset is owed.
check-changeset-fixed0All workspace packages are in the changeset fixed group.
check-changeset-no-major0No changeset declares a major bump.

scripts/__tests__/check-doc-component-types.test.ts34 passed (34), run at ca35295a9 from the repo root. Its three live-table assertions were also evaluated directly against this diff: stale-exemption findings []; exempted 138 (asserted > 50); declaration count 82 → 81 (asserted > 20).

eslint scripts/check-doc-component-types.mjs --no-inline-config --format json — 1 file linted, 0 errors, 0 warnings.

Declared narrowing: check-doc-snippet-types

Not run to completion — it needs a full monorepo build. The narrowing is a measurement, taken with that gate's own scanner, not an assumption:

  1. Its fence-language set is TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']) (its own source, line 224).
  2. scanFences() on this page returns blocks=0, markers=0 in both states — at HEAD~1 and on the working tree. Every fence on the page is plaintext (10 → 11), and the one I added is plaintext too.
  3. The page is not named in UNGATED_DOCS, so no ledger entry of mine can go stale.

The gate collects zero blocks from this page either way, so this diff cannot change its verdict. CI runs it in full regardless.

Changeset

.changeset/block-schema-docs-truth-4895.md, empty frontmatter — docs and a gate ledger only, no released package src/ is touched, declared explicitly rather than left undeclared. objectui has no skip-changeset label; the empty-frontmatter changeset is this repo's declaration mechanism.

Deliberately not done

  • No renderers built for block-library / block-editor / block-instance (Option A, rejected on zero pull).
  • Nothing retired from packages/types (Option C, deferred). No published type is touched.
  • Best Practices and Use Cases left as-is. Both still read somewhat aspirationally, but the status callout now governs the whole page, and gutting them is beyond the ruling's scope. Flagging rather than silently widening the diff.

Generated by Claude Code

)
Carries the recorded maintainer ruling on #4895 (Option B: docs-truth fix,
family re-scoped as type-level).
- Delete the phantom `type: 'slot'` node from the Complete Example template.
Nothing registers `slot`, so a reader who copied it got OBJUI-001.
- Teach the declared `slotContent` path instead, and state plainly that it is
declared-but-not-yet-consumed: nothing reads `slots` / `slotContent` /
`template` at runtime.
- Frame `BlockSchema` as a definition and drop the component-schema framing for
`block-library` / `block-editor` / `block-instance`.
- Disambiguate from the slot system that IS wired end to end (slotted record
pages), which is an unrelated vocabulary.
- Drop the now-stale `slot` entry from DOC_TYPE_EXEMPTIONS in
scripts/check-doc-component-types.mjs, and re-point the three terse family
reasons at the ruling's framing.
Option A (build renderers) is rejected on zero pull; Option C (retire the
family) is deferred. Neither is implemented here.
Empty frontmatter: docs and a gate ledger only, no released package `src/`
is touched. Declared explicitly rather than left undeclared.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@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

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node - #5937

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth
Aug 24, 2026
Merged

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node#5937
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth

Conversation

@os-zhuang

@os-zhuangos-zhuang commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Part of #4895

Carries the maintainer ruling recorded on that card (comment 5339693569, 2026-08-19, 「全部接受」): Option B — docs-truth fix, the block family re-scoped as type-level. Option A (build renderers) was rejected on zero pull; Option C (retire the family) is deferred. Neither is implemented here, and neither is argued for.

Reference keyword is deliberately Part of, not a closing keyword: the ruling leaves Option C open, to be revisited if a later liveness pass shows the published types inert externally. The measurement below strengthens that future case rather than settling it, so the card should stay open for the PM seat to judge.

What was wrong

content/docs/blocks/block-schema.mdx:254 taught this inside BlockSchema.template:

{
type: 'slot',
name: 'content'
}

template is typed SchemaNode | SchemaNode[], so that snippet sat on the render path — and nothing registers slot. A reader who copied it got the renderer's OBJUI-001 "Unknown component type" panel. That node is deleted; the page now teaches slotContent, the key BlockSchema and BlockInstanceSchema actually declare.

The measurement that changed how this is written

The ruling says "teach slotContent". Before writing that, I measured whether slotContent is consumed by anything — a renderer, a hook, an adapter. It is not.

Every occurrence in the repository, excluding node_modules and dist:

sitewhat it is
packages/types/src/blocks.ts:201BlockSchema.slotContent declaration
packages/types/src/blocks.ts:377BlockInstanceSchema.slotContent declaration
packages/types/src/zod/blocks.zod.ts:77Zod mirror
packages/types/src/zod/blocks.zod.ts:134Zod mirror
content/docs/blocks/block-schema.mdx:297this page
scripts/check-doc-component-types.mjs:283the exemption reason this PR deletes

Zero renderers, zero hooks, zero adapters. The same holds for slots and template on this family — outside packages/types (the interfaces, the Zod mirror, and that package's own parse test) nothing reads them.

So the page does not present slotContent as the working path. It documents it as the declared path and states plainly that it is not yet consumed. Rewriting one phantom into a second phantom is the failure this card exists to close, so the page carries a status callout up front rather than implying a runtime that does not exist.

Two supporting measurements are written into the page because they are what a confused reader needs:

  • The four SchemaExample demos the page embeds are ordinary registered component trees (card, flex, stack, text, icon, button, badge). None carries type: 'block', slots or slotContent — nothing on the page was ever exercising the block vocabulary.
  • The slot system that is wired end to end is a different family: slotted record pages (kind: "slotted", page.slots), consumed by usePageAssignment, PageBlockCanvas and PageBlockInspector, documented at content/docs/guide/slotted-pages.md. The page now links there so the two slots spellings are not conflated.

Full vocabulary audit, not just slot

Every type literal the page teaches, checked against the gate's derived registered-key universe (659 keys). Line numbers are pre-change.

valuelinesregistered?vocabularyaction
block44, 140noBlockSchema discriminantkeep, exempted — a definition
block-editor356noBlockEditorSchema discriminantkeep, exempted
block-instance283noBlockInstanceSchema discriminantkeep, exempted
block-library320, 374noBlockLibrarySchema discriminantkeep, exempted
boolean62, 192yesBlockVariable.type data typeno change
button258yesSDUI nodeno change
card223yesSDUI nodeno change
div75, 227, 239yesSDUI nodeno change
icon231yesSDUI nodeno change
list300yesSDUI nodeno change
slot254nonothing — phantom, on the render pathdeleted
string56, 162, 170, 177, 185, 199noBlockVariable.type data typekeep, exempted
text243, 249yesSDUI nodeno change

slot is the only unregistered value that named a renderable node. The other five unregistered values are each a discriminant of a declared interface or a variable data type, and each keeps a live exemption. So there was no second slot-shaped defect to sweep up.

One incidental observation, not a defect on this page and not changed: boolean at :62 and :192 is a BlockVariable.type data type that happens to collide with a registered component key, so it passes the gate's flat rule without needing an exemption. The page is correct; the gate's judgment there is a coincidence rather than a check.

Gate calibration — proving the silence is a measurement

Removing the exemption is only meaningful if check-doc-component-types.mjs actually reads this file. A gate that is silent because it never looked is not a passing gate, so this was calibrated rather than assumed. The mutation was confirmed on disk by grep count, never by the editing tool's exit code, and the script carried a trap ... EXIT INT TERM restore.

Mutation leg — rewrite the registered type: 'card' node at :223 to a value nothing registers:

"type: 'card'," 1 -> 0 (deleted text)
'objui-4895-calibration-probe' 0 -> 1 (injected text)

Gate exit code, captured before any pipe: 1

content/docs/blocks/block-schema.mdx:223 [unregistered-doc-type] type 'objui-4895-calibration-probe' (plaintext)
type: 'objui-4895-calibration-probe',

Restore leg — probe absent, anchor back, git status clean for the file. Gate exit code: 0, ✅ Every documented component type is registered.

The gate reads this page.

The two halves are a matched pair

Both failure directions were exercised, so neither half is passing by accident:

  • Doc corrected but the exemption put backstale-exemption: content/docs/blocks/block-schema.mdx -> slot no code block in that file spells this type any more.
  • Exemption dropped but the phantom node put back → exit 1, content/docs/blocks/block-schema.mdx:305 [unregistered-doc-type] type 'slot' (plaintext)

Only both halves together are green.

Gate ledger

The slot entry in DOC_TYPE_EXEMPTIONS pointed at this card and is dropped, as the ruling requires. With the phantom node gone it would itself report as stale-exemption, so it is deleted rather than re-pointed; a comment records why, so the next reader does not re-add it. The three terse family reasons (block-instance / block-library / block-editor) are re-pointed at the ruling's framing — each now records that the schema is absent from AnySchema and has no renderer, which is the fact the gate's own standard asks an exemption reason to name.

Counters move coherently: exempted 139 → 138 (one entry gone), registered 743 → 744 (the new type: 'text' site), code blocks 1055 → 1056.

Verification — union run at ca35295a9 (final commit)

Exit codes captured before any pipe; verdicts quoted from each gate's own output.

gateexitverdict line
check-doc-component-types0Every documented component type is registered.
check-doc-links0Links are valid across 13 scan roots.
check-control-bytes0check-control-bytes: OK (scanned 4929 tracked text file(s); skipped 85 binary).
check-changeset-presence0No source of a released package changed in this range, so no changeset is owed.
check-changeset-fixed0All workspace packages are in the changeset fixed group.
check-changeset-no-major0No changeset declares a major bump.

scripts/__tests__/check-doc-component-types.test.ts34 passed (34), run at ca35295a9 from the repo root. Its three live-table assertions were also evaluated directly against this diff: stale-exemption findings []; exempted 138 (asserted > 50); declaration count 82 → 81 (asserted > 20).

eslint scripts/check-doc-component-types.mjs --no-inline-config --format json — 1 file linted, 0 errors, 0 warnings.

Declared narrowing: check-doc-snippet-types

Not run to completion — it needs a full monorepo build. The narrowing is a measurement, taken with that gate's own scanner, not an assumption:

  1. Its fence-language set is TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']) (its own source, line 224).
  2. scanFences() on this page returns blocks=0, markers=0 in both states — at HEAD~1 and on the working tree. Every fence on the page is plaintext (10 → 11), and the one I added is plaintext too.
  3. The page is not named in UNGATED_DOCS, so no ledger entry of mine can go stale.

The gate collects zero blocks from this page either way, so this diff cannot change its verdict. CI runs it in full regardless.

Changeset

.changeset/block-schema-docs-truth-4895.md, empty frontmatter — docs and a gate ledger only, no released package src/ is touched, declared explicitly rather than left undeclared. objectui has no skip-changeset label; the empty-frontmatter changeset is this repo's declaration mechanism.

Deliberately not done

  • No renderers built for block-library / block-editor / block-instance (Option A, rejected on zero pull).
  • Nothing retired from packages/types (Option C, deferred). No published type is touched.
  • Best Practices and Use Cases left as-is. Both still read somewhat aspirationally, but the status callout now governs the whole page, and gutting them is beyond the ruling's scope. Flagging rather than silently widening the diff.

Generated by Claude Code

)
Carries the recorded maintainer ruling on #4895 (Option B: docs-truth fix,
family re-scoped as type-level).
- Delete the phantom `type: 'slot'` node from the Complete Example template.
Nothing registers `slot`, so a reader who copied it got OBJUI-001.
- Teach the declared `slotContent` path instead, and state plainly that it is
declared-but-not-yet-consumed: nothing reads `slots` / `slotContent` /
`template` at runtime.
- Frame `BlockSchema` as a definition and drop the component-schema framing for
`block-library` / `block-editor` / `block-instance`.
- Disambiguate from the slot system that IS wired end to end (slotted record
pages), which is an unrelated vocabulary.
- Drop the now-stale `slot` entry from DOC_TYPE_EXEMPTIONS in
scripts/check-doc-component-types.mjs, and re-point the three terse family
reasons at the ruling's framing.
Option A (build renderers) is rejected on zero pull; Option C (retire the
family) is deferred. Neither is implemented here.
Empty frontmatter: docs and a gate ledger only, no released package `src/`
is touched. Declared explicitly rather than left undeclared.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@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

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node - #5937

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth
Aug 24, 2026
Merged

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node#5937
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth

Conversation

@os-zhuang

@os-zhuangos-zhuang commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Part of #4895

Carries the maintainer ruling recorded on that card (comment 5339693569, 2026-08-19, 「全部接受」): Option B — docs-truth fix, the block family re-scoped as type-level. Option A (build renderers) was rejected on zero pull; Option C (retire the family) is deferred. Neither is implemented here, and neither is argued for.

Reference keyword is deliberately Part of, not a closing keyword: the ruling leaves Option C open, to be revisited if a later liveness pass shows the published types inert externally. The measurement below strengthens that future case rather than settling it, so the card should stay open for the PM seat to judge.

What was wrong

content/docs/blocks/block-schema.mdx:254 taught this inside BlockSchema.template:

{
type: 'slot',
name: 'content'
}

template is typed SchemaNode | SchemaNode[], so that snippet sat on the render path — and nothing registers slot. A reader who copied it got the renderer's OBJUI-001 "Unknown component type" panel. That node is deleted; the page now teaches slotContent, the key BlockSchema and BlockInstanceSchema actually declare.

The measurement that changed how this is written

The ruling says "teach slotContent". Before writing that, I measured whether slotContent is consumed by anything — a renderer, a hook, an adapter. It is not.

Every occurrence in the repository, excluding node_modules and dist:

sitewhat it is
packages/types/src/blocks.ts:201BlockSchema.slotContent declaration
packages/types/src/blocks.ts:377BlockInstanceSchema.slotContent declaration
packages/types/src/zod/blocks.zod.ts:77Zod mirror
packages/types/src/zod/blocks.zod.ts:134Zod mirror
content/docs/blocks/block-schema.mdx:297this page
scripts/check-doc-component-types.mjs:283the exemption reason this PR deletes

Zero renderers, zero hooks, zero adapters. The same holds for slots and template on this family — outside packages/types (the interfaces, the Zod mirror, and that package's own parse test) nothing reads them.

So the page does not present slotContent as the working path. It documents it as the declared path and states plainly that it is not yet consumed. Rewriting one phantom into a second phantom is the failure this card exists to close, so the page carries a status callout up front rather than implying a runtime that does not exist.

Two supporting measurements are written into the page because they are what a confused reader needs:

  • The four SchemaExample demos the page embeds are ordinary registered component trees (card, flex, stack, text, icon, button, badge). None carries type: 'block', slots or slotContent — nothing on the page was ever exercising the block vocabulary.
  • The slot system that is wired end to end is a different family: slotted record pages (kind: "slotted", page.slots), consumed by usePageAssignment, PageBlockCanvas and PageBlockInspector, documented at content/docs/guide/slotted-pages.md. The page now links there so the two slots spellings are not conflated.

Full vocabulary audit, not just slot

Every type literal the page teaches, checked against the gate's derived registered-key universe (659 keys). Line numbers are pre-change.

valuelinesregistered?vocabularyaction
block44, 140noBlockSchema discriminantkeep, exempted — a definition
block-editor356noBlockEditorSchema discriminantkeep, exempted
block-instance283noBlockInstanceSchema discriminantkeep, exempted
block-library320, 374noBlockLibrarySchema discriminantkeep, exempted
boolean62, 192yesBlockVariable.type data typeno change
button258yesSDUI nodeno change
card223yesSDUI nodeno change
div75, 227, 239yesSDUI nodeno change
icon231yesSDUI nodeno change
list300yesSDUI nodeno change
slot254nonothing — phantom, on the render pathdeleted
string56, 162, 170, 177, 185, 199noBlockVariable.type data typekeep, exempted
text243, 249yesSDUI nodeno change

slot is the only unregistered value that named a renderable node. The other five unregistered values are each a discriminant of a declared interface or a variable data type, and each keeps a live exemption. So there was no second slot-shaped defect to sweep up.

One incidental observation, not a defect on this page and not changed: boolean at :62 and :192 is a BlockVariable.type data type that happens to collide with a registered component key, so it passes the gate's flat rule without needing an exemption. The page is correct; the gate's judgment there is a coincidence rather than a check.

Gate calibration — proving the silence is a measurement

Removing the exemption is only meaningful if check-doc-component-types.mjs actually reads this file. A gate that is silent because it never looked is not a passing gate, so this was calibrated rather than assumed. The mutation was confirmed on disk by grep count, never by the editing tool's exit code, and the script carried a trap ... EXIT INT TERM restore.

Mutation leg — rewrite the registered type: 'card' node at :223 to a value nothing registers:

"type: 'card'," 1 -> 0 (deleted text)
'objui-4895-calibration-probe' 0 -> 1 (injected text)

Gate exit code, captured before any pipe: 1

content/docs/blocks/block-schema.mdx:223 [unregistered-doc-type] type 'objui-4895-calibration-probe' (plaintext)
type: 'objui-4895-calibration-probe',

Restore leg — probe absent, anchor back, git status clean for the file. Gate exit code: 0, ✅ Every documented component type is registered.

The gate reads this page.

The two halves are a matched pair

Both failure directions were exercised, so neither half is passing by accident:

  • Doc corrected but the exemption put backstale-exemption: content/docs/blocks/block-schema.mdx -> slot no code block in that file spells this type any more.
  • Exemption dropped but the phantom node put back → exit 1, content/docs/blocks/block-schema.mdx:305 [unregistered-doc-type] type 'slot' (plaintext)

Only both halves together are green.

Gate ledger

The slot entry in DOC_TYPE_EXEMPTIONS pointed at this card and is dropped, as the ruling requires. With the phantom node gone it would itself report as stale-exemption, so it is deleted rather than re-pointed; a comment records why, so the next reader does not re-add it. The three terse family reasons (block-instance / block-library / block-editor) are re-pointed at the ruling's framing — each now records that the schema is absent from AnySchema and has no renderer, which is the fact the gate's own standard asks an exemption reason to name.

Counters move coherently: exempted 139 → 138 (one entry gone), registered 743 → 744 (the new type: 'text' site), code blocks 1055 → 1056.

Verification — union run at ca35295a9 (final commit)

Exit codes captured before any pipe; verdicts quoted from each gate's own output.

gateexitverdict line
check-doc-component-types0Every documented component type is registered.
check-doc-links0Links are valid across 13 scan roots.
check-control-bytes0check-control-bytes: OK (scanned 4929 tracked text file(s); skipped 85 binary).
check-changeset-presence0No source of a released package changed in this range, so no changeset is owed.
check-changeset-fixed0All workspace packages are in the changeset fixed group.
check-changeset-no-major0No changeset declares a major bump.

scripts/__tests__/check-doc-component-types.test.ts34 passed (34), run at ca35295a9 from the repo root. Its three live-table assertions were also evaluated directly against this diff: stale-exemption findings []; exempted 138 (asserted > 50); declaration count 82 → 81 (asserted > 20).

eslint scripts/check-doc-component-types.mjs --no-inline-config --format json — 1 file linted, 0 errors, 0 warnings.

Declared narrowing: check-doc-snippet-types

Not run to completion — it needs a full monorepo build. The narrowing is a measurement, taken with that gate's own scanner, not an assumption:

  1. Its fence-language set is TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']) (its own source, line 224).
  2. scanFences() on this page returns blocks=0, markers=0 in both states — at HEAD~1 and on the working tree. Every fence on the page is plaintext (10 → 11), and the one I added is plaintext too.
  3. The page is not named in UNGATED_DOCS, so no ledger entry of mine can go stale.

The gate collects zero blocks from this page either way, so this diff cannot change its verdict. CI runs it in full regardless.

Changeset

.changeset/block-schema-docs-truth-4895.md, empty frontmatter — docs and a gate ledger only, no released package src/ is touched, declared explicitly rather than left undeclared. objectui has no skip-changeset label; the empty-frontmatter changeset is this repo's declaration mechanism.

Deliberately not done

  • No renderers built for block-library / block-editor / block-instance (Option A, rejected on zero pull).
  • Nothing retired from packages/types (Option C, deferred). No published type is touched.
  • Best Practices and Use Cases left as-is. Both still read somewhat aspirationally, but the status callout now governs the whole page, and gutting them is beyond the ruling's scope. Flagging rather than silently widening the diff.

Generated by Claude Code

)
Carries the recorded maintainer ruling on #4895 (Option B: docs-truth fix,
family re-scoped as type-level).
- Delete the phantom `type: 'slot'` node from the Complete Example template.
Nothing registers `slot`, so a reader who copied it got OBJUI-001.
- Teach the declared `slotContent` path instead, and state plainly that it is
declared-but-not-yet-consumed: nothing reads `slots` / `slotContent` /
`template` at runtime.
- Frame `BlockSchema` as a definition and drop the component-schema framing for
`block-library` / `block-editor` / `block-instance`.
- Disambiguate from the slot system that IS wired end to end (slotted record
pages), which is an unrelated vocabulary.
- Drop the now-stale `slot` entry from DOC_TYPE_EXEMPTIONS in
scripts/check-doc-component-types.mjs, and re-point the three terse family
reasons at the ruling's framing.
Option A (build renderers) is rejected on zero pull; Option C (retire the
family) is deferred. Neither is implemented here.
Empty frontmatter: docs and a gate ledger only, no released package `src/`
is touched. Declared explicitly rather than left undeclared.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@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

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node - #5937

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth
Aug 24, 2026
Merged

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node#5937
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth

Conversation

@os-zhuang

@os-zhuangos-zhuang commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Part of #4895

Carries the maintainer ruling recorded on that card (comment 5339693569, 2026-08-19, 「全部接受」): Option B — docs-truth fix, the block family re-scoped as type-level. Option A (build renderers) was rejected on zero pull; Option C (retire the family) is deferred. Neither is implemented here, and neither is argued for.

Reference keyword is deliberately Part of, not a closing keyword: the ruling leaves Option C open, to be revisited if a later liveness pass shows the published types inert externally. The measurement below strengthens that future case rather than settling it, so the card should stay open for the PM seat to judge.

What was wrong

content/docs/blocks/block-schema.mdx:254 taught this inside BlockSchema.template:

{
type: 'slot',
name: 'content'
}

template is typed SchemaNode | SchemaNode[], so that snippet sat on the render path — and nothing registers slot. A reader who copied it got the renderer's OBJUI-001 "Unknown component type" panel. That node is deleted; the page now teaches slotContent, the key BlockSchema and BlockInstanceSchema actually declare.

The measurement that changed how this is written

The ruling says "teach slotContent". Before writing that, I measured whether slotContent is consumed by anything — a renderer, a hook, an adapter. It is not.

Every occurrence in the repository, excluding node_modules and dist:

sitewhat it is
packages/types/src/blocks.ts:201BlockSchema.slotContent declaration
packages/types/src/blocks.ts:377BlockInstanceSchema.slotContent declaration
packages/types/src/zod/blocks.zod.ts:77Zod mirror
packages/types/src/zod/blocks.zod.ts:134Zod mirror
content/docs/blocks/block-schema.mdx:297this page
scripts/check-doc-component-types.mjs:283the exemption reason this PR deletes

Zero renderers, zero hooks, zero adapters. The same holds for slots and template on this family — outside packages/types (the interfaces, the Zod mirror, and that package's own parse test) nothing reads them.

So the page does not present slotContent as the working path. It documents it as the declared path and states plainly that it is not yet consumed. Rewriting one phantom into a second phantom is the failure this card exists to close, so the page carries a status callout up front rather than implying a runtime that does not exist.

Two supporting measurements are written into the page because they are what a confused reader needs:

  • The four SchemaExample demos the page embeds are ordinary registered component trees (card, flex, stack, text, icon, button, badge). None carries type: 'block', slots or slotContent — nothing on the page was ever exercising the block vocabulary.
  • The slot system that is wired end to end is a different family: slotted record pages (kind: "slotted", page.slots), consumed by usePageAssignment, PageBlockCanvas and PageBlockInspector, documented at content/docs/guide/slotted-pages.md. The page now links there so the two slots spellings are not conflated.

Full vocabulary audit, not just slot

Every type literal the page teaches, checked against the gate's derived registered-key universe (659 keys). Line numbers are pre-change.

valuelinesregistered?vocabularyaction
block44, 140noBlockSchema discriminantkeep, exempted — a definition
block-editor356noBlockEditorSchema discriminantkeep, exempted
block-instance283noBlockInstanceSchema discriminantkeep, exempted
block-library320, 374noBlockLibrarySchema discriminantkeep, exempted
boolean62, 192yesBlockVariable.type data typeno change
button258yesSDUI nodeno change
card223yesSDUI nodeno change
div75, 227, 239yesSDUI nodeno change
icon231yesSDUI nodeno change
list300yesSDUI nodeno change
slot254nonothing — phantom, on the render pathdeleted
string56, 162, 170, 177, 185, 199noBlockVariable.type data typekeep, exempted
text243, 249yesSDUI nodeno change

slot is the only unregistered value that named a renderable node. The other five unregistered values are each a discriminant of a declared interface or a variable data type, and each keeps a live exemption. So there was no second slot-shaped defect to sweep up.

One incidental observation, not a defect on this page and not changed: boolean at :62 and :192 is a BlockVariable.type data type that happens to collide with a registered component key, so it passes the gate's flat rule without needing an exemption. The page is correct; the gate's judgment there is a coincidence rather than a check.

Gate calibration — proving the silence is a measurement

Removing the exemption is only meaningful if check-doc-component-types.mjs actually reads this file. A gate that is silent because it never looked is not a passing gate, so this was calibrated rather than assumed. The mutation was confirmed on disk by grep count, never by the editing tool's exit code, and the script carried a trap ... EXIT INT TERM restore.

Mutation leg — rewrite the registered type: 'card' node at :223 to a value nothing registers:

"type: 'card'," 1 -> 0 (deleted text)
'objui-4895-calibration-probe' 0 -> 1 (injected text)

Gate exit code, captured before any pipe: 1

content/docs/blocks/block-schema.mdx:223 [unregistered-doc-type] type 'objui-4895-calibration-probe' (plaintext)
type: 'objui-4895-calibration-probe',

Restore leg — probe absent, anchor back, git status clean for the file. Gate exit code: 0, ✅ Every documented component type is registered.

The gate reads this page.

The two halves are a matched pair

Both failure directions were exercised, so neither half is passing by accident:

  • Doc corrected but the exemption put backstale-exemption: content/docs/blocks/block-schema.mdx -> slot no code block in that file spells this type any more.
  • Exemption dropped but the phantom node put back → exit 1, content/docs/blocks/block-schema.mdx:305 [unregistered-doc-type] type 'slot' (plaintext)

Only both halves together are green.

Gate ledger

The slot entry in DOC_TYPE_EXEMPTIONS pointed at this card and is dropped, as the ruling requires. With the phantom node gone it would itself report as stale-exemption, so it is deleted rather than re-pointed; a comment records why, so the next reader does not re-add it. The three terse family reasons (block-instance / block-library / block-editor) are re-pointed at the ruling's framing — each now records that the schema is absent from AnySchema and has no renderer, which is the fact the gate's own standard asks an exemption reason to name.

Counters move coherently: exempted 139 → 138 (one entry gone), registered 743 → 744 (the new type: 'text' site), code blocks 1055 → 1056.

Verification — union run at ca35295a9 (final commit)

Exit codes captured before any pipe; verdicts quoted from each gate's own output.

gateexitverdict line
check-doc-component-types0Every documented component type is registered.
check-doc-links0Links are valid across 13 scan roots.
check-control-bytes0check-control-bytes: OK (scanned 4929 tracked text file(s); skipped 85 binary).
check-changeset-presence0No source of a released package changed in this range, so no changeset is owed.
check-changeset-fixed0All workspace packages are in the changeset fixed group.
check-changeset-no-major0No changeset declares a major bump.

scripts/__tests__/check-doc-component-types.test.ts34 passed (34), run at ca35295a9 from the repo root. Its three live-table assertions were also evaluated directly against this diff: stale-exemption findings []; exempted 138 (asserted > 50); declaration count 82 → 81 (asserted > 20).

eslint scripts/check-doc-component-types.mjs --no-inline-config --format json — 1 file linted, 0 errors, 0 warnings.

Declared narrowing: check-doc-snippet-types

Not run to completion — it needs a full monorepo build. The narrowing is a measurement, taken with that gate's own scanner, not an assumption:

  1. Its fence-language set is TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']) (its own source, line 224).
  2. scanFences() on this page returns blocks=0, markers=0 in both states — at HEAD~1 and on the working tree. Every fence on the page is plaintext (10 → 11), and the one I added is plaintext too.
  3. The page is not named in UNGATED_DOCS, so no ledger entry of mine can go stale.

The gate collects zero blocks from this page either way, so this diff cannot change its verdict. CI runs it in full regardless.

Changeset

.changeset/block-schema-docs-truth-4895.md, empty frontmatter — docs and a gate ledger only, no released package src/ is touched, declared explicitly rather than left undeclared. objectui has no skip-changeset label; the empty-frontmatter changeset is this repo's declaration mechanism.

Deliberately not done

  • No renderers built for block-library / block-editor / block-instance (Option A, rejected on zero pull).
  • Nothing retired from packages/types (Option C, deferred). No published type is touched.
  • Best Practices and Use Cases left as-is. Both still read somewhat aspirationally, but the status callout now governs the whole page, and gutting them is beyond the ruling's scope. Flagging rather than silently widening the diff.

Generated by Claude Code

)
Carries the recorded maintainer ruling on #4895 (Option B: docs-truth fix,
family re-scoped as type-level).
- Delete the phantom `type: 'slot'` node from the Complete Example template.
Nothing registers `slot`, so a reader who copied it got OBJUI-001.
- Teach the declared `slotContent` path instead, and state plainly that it is
declared-but-not-yet-consumed: nothing reads `slots` / `slotContent` /
`template` at runtime.
- Frame `BlockSchema` as a definition and drop the component-schema framing for
`block-library` / `block-editor` / `block-instance`.
- Disambiguate from the slot system that IS wired end to end (slotted record
pages), which is an unrelated vocabulary.
- Drop the now-stale `slot` entry from DOC_TYPE_EXEMPTIONS in
scripts/check-doc-component-types.mjs, and re-point the three terse family
reasons at the ruling's framing.
Option A (build renderers) is rejected on zero pull; Option C (retire the
family) is deferred. Neither is implemented here.
Empty frontmatter: docs and a gate ledger only, no released package `src/`
is touched. Declared explicitly rather than left undeclared.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@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

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node - #5937

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth
Aug 24, 2026
Merged

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node#5937
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth

Conversation

@os-zhuang

@os-zhuangos-zhuang commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Part of #4895

Carries the maintainer ruling recorded on that card (comment 5339693569, 2026-08-19, 「全部接受」): Option B — docs-truth fix, the block family re-scoped as type-level. Option A (build renderers) was rejected on zero pull; Option C (retire the family) is deferred. Neither is implemented here, and neither is argued for.

Reference keyword is deliberately Part of, not a closing keyword: the ruling leaves Option C open, to be revisited if a later liveness pass shows the published types inert externally. The measurement below strengthens that future case rather than settling it, so the card should stay open for the PM seat to judge.

What was wrong

content/docs/blocks/block-schema.mdx:254 taught this inside BlockSchema.template:

{
type: 'slot',
name: 'content'
}

template is typed SchemaNode | SchemaNode[], so that snippet sat on the render path — and nothing registers slot. A reader who copied it got the renderer's OBJUI-001 "Unknown component type" panel. That node is deleted; the page now teaches slotContent, the key BlockSchema and BlockInstanceSchema actually declare.

The measurement that changed how this is written

The ruling says "teach slotContent". Before writing that, I measured whether slotContent is consumed by anything — a renderer, a hook, an adapter. It is not.

Every occurrence in the repository, excluding node_modules and dist:

sitewhat it is
packages/types/src/blocks.ts:201BlockSchema.slotContent declaration
packages/types/src/blocks.ts:377BlockInstanceSchema.slotContent declaration
packages/types/src/zod/blocks.zod.ts:77Zod mirror
packages/types/src/zod/blocks.zod.ts:134Zod mirror
content/docs/blocks/block-schema.mdx:297this page
scripts/check-doc-component-types.mjs:283the exemption reason this PR deletes

Zero renderers, zero hooks, zero adapters. The same holds for slots and template on this family — outside packages/types (the interfaces, the Zod mirror, and that package's own parse test) nothing reads them.

So the page does not present slotContent as the working path. It documents it as the declared path and states plainly that it is not yet consumed. Rewriting one phantom into a second phantom is the failure this card exists to close, so the page carries a status callout up front rather than implying a runtime that does not exist.

Two supporting measurements are written into the page because they are what a confused reader needs:

  • The four SchemaExample demos the page embeds are ordinary registered component trees (card, flex, stack, text, icon, button, badge). None carries type: 'block', slots or slotContent — nothing on the page was ever exercising the block vocabulary.
  • The slot system that is wired end to end is a different family: slotted record pages (kind: "slotted", page.slots), consumed by usePageAssignment, PageBlockCanvas and PageBlockInspector, documented at content/docs/guide/slotted-pages.md. The page now links there so the two slots spellings are not conflated.

Full vocabulary audit, not just slot

Every type literal the page teaches, checked against the gate's derived registered-key universe (659 keys). Line numbers are pre-change.

valuelinesregistered?vocabularyaction
block44, 140noBlockSchema discriminantkeep, exempted — a definition
block-editor356noBlockEditorSchema discriminantkeep, exempted
block-instance283noBlockInstanceSchema discriminantkeep, exempted
block-library320, 374noBlockLibrarySchema discriminantkeep, exempted
boolean62, 192yesBlockVariable.type data typeno change
button258yesSDUI nodeno change
card223yesSDUI nodeno change
div75, 227, 239yesSDUI nodeno change
icon231yesSDUI nodeno change
list300yesSDUI nodeno change
slot254nonothing — phantom, on the render pathdeleted
string56, 162, 170, 177, 185, 199noBlockVariable.type data typekeep, exempted
text243, 249yesSDUI nodeno change

slot is the only unregistered value that named a renderable node. The other five unregistered values are each a discriminant of a declared interface or a variable data type, and each keeps a live exemption. So there was no second slot-shaped defect to sweep up.

One incidental observation, not a defect on this page and not changed: boolean at :62 and :192 is a BlockVariable.type data type that happens to collide with a registered component key, so it passes the gate's flat rule without needing an exemption. The page is correct; the gate's judgment there is a coincidence rather than a check.

Gate calibration — proving the silence is a measurement

Removing the exemption is only meaningful if check-doc-component-types.mjs actually reads this file. A gate that is silent because it never looked is not a passing gate, so this was calibrated rather than assumed. The mutation was confirmed on disk by grep count, never by the editing tool's exit code, and the script carried a trap ... EXIT INT TERM restore.

Mutation leg — rewrite the registered type: 'card' node at :223 to a value nothing registers:

"type: 'card'," 1 -> 0 (deleted text)
'objui-4895-calibration-probe' 0 -> 1 (injected text)

Gate exit code, captured before any pipe: 1

content/docs/blocks/block-schema.mdx:223 [unregistered-doc-type] type 'objui-4895-calibration-probe' (plaintext)
type: 'objui-4895-calibration-probe',

Restore leg — probe absent, anchor back, git status clean for the file. Gate exit code: 0, ✅ Every documented component type is registered.

The gate reads this page.

The two halves are a matched pair

Both failure directions were exercised, so neither half is passing by accident:

  • Doc corrected but the exemption put backstale-exemption: content/docs/blocks/block-schema.mdx -> slot no code block in that file spells this type any more.
  • Exemption dropped but the phantom node put back → exit 1, content/docs/blocks/block-schema.mdx:305 [unregistered-doc-type] type 'slot' (plaintext)

Only both halves together are green.

Gate ledger

The slot entry in DOC_TYPE_EXEMPTIONS pointed at this card and is dropped, as the ruling requires. With the phantom node gone it would itself report as stale-exemption, so it is deleted rather than re-pointed; a comment records why, so the next reader does not re-add it. The three terse family reasons (block-instance / block-library / block-editor) are re-pointed at the ruling's framing — each now records that the schema is absent from AnySchema and has no renderer, which is the fact the gate's own standard asks an exemption reason to name.

Counters move coherently: exempted 139 → 138 (one entry gone), registered 743 → 744 (the new type: 'text' site), code blocks 1055 → 1056.

Verification — union run at ca35295a9 (final commit)

Exit codes captured before any pipe; verdicts quoted from each gate's own output.

gateexitverdict line
check-doc-component-types0Every documented component type is registered.
check-doc-links0Links are valid across 13 scan roots.
check-control-bytes0check-control-bytes: OK (scanned 4929 tracked text file(s); skipped 85 binary).
check-changeset-presence0No source of a released package changed in this range, so no changeset is owed.
check-changeset-fixed0All workspace packages are in the changeset fixed group.
check-changeset-no-major0No changeset declares a major bump.

scripts/__tests__/check-doc-component-types.test.ts34 passed (34), run at ca35295a9 from the repo root. Its three live-table assertions were also evaluated directly against this diff: stale-exemption findings []; exempted 138 (asserted > 50); declaration count 82 → 81 (asserted > 20).

eslint scripts/check-doc-component-types.mjs --no-inline-config --format json — 1 file linted, 0 errors, 0 warnings.

Declared narrowing: check-doc-snippet-types

Not run to completion — it needs a full monorepo build. The narrowing is a measurement, taken with that gate's own scanner, not an assumption:

  1. Its fence-language set is TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']) (its own source, line 224).
  2. scanFences() on this page returns blocks=0, markers=0 in both states — at HEAD~1 and on the working tree. Every fence on the page is plaintext (10 → 11), and the one I added is plaintext too.
  3. The page is not named in UNGATED_DOCS, so no ledger entry of mine can go stale.

The gate collects zero blocks from this page either way, so this diff cannot change its verdict. CI runs it in full regardless.

Changeset

.changeset/block-schema-docs-truth-4895.md, empty frontmatter — docs and a gate ledger only, no released package src/ is touched, declared explicitly rather than left undeclared. objectui has no skip-changeset label; the empty-frontmatter changeset is this repo's declaration mechanism.

Deliberately not done

  • No renderers built for block-library / block-editor / block-instance (Option A, rejected on zero pull).
  • Nothing retired from packages/types (Option C, deferred). No published type is touched.
  • Best Practices and Use Cases left as-is. Both still read somewhat aspirationally, but the status callout now governs the whole page, and gutting them is beyond the ruling's scope. Flagging rather than silently widening the diff.

Generated by Claude Code

)
Carries the recorded maintainer ruling on #4895 (Option B: docs-truth fix,
family re-scoped as type-level).
- Delete the phantom `type: 'slot'` node from the Complete Example template.
Nothing registers `slot`, so a reader who copied it got OBJUI-001.
- Teach the declared `slotContent` path instead, and state plainly that it is
declared-but-not-yet-consumed: nothing reads `slots` / `slotContent` /
`template` at runtime.
- Frame `BlockSchema` as a definition and drop the component-schema framing for
`block-library` / `block-editor` / `block-instance`.
- Disambiguate from the slot system that IS wired end to end (slotted record
pages), which is an unrelated vocabulary.
- Drop the now-stale `slot` entry from DOC_TYPE_EXEMPTIONS in
scripts/check-doc-component-types.mjs, and re-point the three terse family
reasons at the ruling's framing.
Option A (build renderers) is rejected on zero pull; Option C (retire the
family) is deferred. Neither is implemented here.
Empty frontmatter: docs and a gate ledger only, no released package `src/`
is touched. Declared explicitly rather than left undeclared.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@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

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node - #5937

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth
Aug 24, 2026
Merged

docs(blocks): correct block-schema.mdx to the declared vocabulary, drop the phantom slot node#5937
os-zhuang merged 2 commits into
mainfrom
claude/issue-4895-block-schema-docs-truth

Conversation

@os-zhuang

@os-zhuangos-zhuang commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Part of #4895

Carries the maintainer ruling recorded on that card (comment 5339693569, 2026-08-19, 「全部接受」): Option B — docs-truth fix, the block family re-scoped as type-level. Option A (build renderers) was rejected on zero pull; Option C (retire the family) is deferred. Neither is implemented here, and neither is argued for.

Reference keyword is deliberately Part of, not a closing keyword: the ruling leaves Option C open, to be revisited if a later liveness pass shows the published types inert externally. The measurement below strengthens that future case rather than settling it, so the card should stay open for the PM seat to judge.

What was wrong

content/docs/blocks/block-schema.mdx:254 taught this inside BlockSchema.template:

{
type: 'slot',
name: 'content'
}

template is typed SchemaNode | SchemaNode[], so that snippet sat on the render path — and nothing registers slot. A reader who copied it got the renderer's OBJUI-001 "Unknown component type" panel. That node is deleted; the page now teaches slotContent, the key BlockSchema and BlockInstanceSchema actually declare.

The measurement that changed how this is written

The ruling says "teach slotContent". Before writing that, I measured whether slotContent is consumed by anything — a renderer, a hook, an adapter. It is not.

Every occurrence in the repository, excluding node_modules and dist:

sitewhat it is
packages/types/src/blocks.ts:201BlockSchema.slotContent declaration
packages/types/src/blocks.ts:377BlockInstanceSchema.slotContent declaration
packages/types/src/zod/blocks.zod.ts:77Zod mirror
packages/types/src/zod/blocks.zod.ts:134Zod mirror
content/docs/blocks/block-schema.mdx:297this page
scripts/check-doc-component-types.mjs:283the exemption reason this PR deletes

Zero renderers, zero hooks, zero adapters. The same holds for slots and template on this family — outside packages/types (the interfaces, the Zod mirror, and that package's own parse test) nothing reads them.

So the page does not present slotContent as the working path. It documents it as the declared path and states plainly that it is not yet consumed. Rewriting one phantom into a second phantom is the failure this card exists to close, so the page carries a status callout up front rather than implying a runtime that does not exist.

Two supporting measurements are written into the page because they are what a confused reader needs:

  • The four SchemaExample demos the page embeds are ordinary registered component trees (card, flex, stack, text, icon, button, badge). None carries type: 'block', slots or slotContent — nothing on the page was ever exercising the block vocabulary.
  • The slot system that is wired end to end is a different family: slotted record pages (kind: "slotted", page.slots), consumed by usePageAssignment, PageBlockCanvas and PageBlockInspector, documented at content/docs/guide/slotted-pages.md. The page now links there so the two slots spellings are not conflated.

Full vocabulary audit, not just slot

Every type literal the page teaches, checked against the gate's derived registered-key universe (659 keys). Line numbers are pre-change.

valuelinesregistered?vocabularyaction
block44, 140noBlockSchema discriminantkeep, exempted — a definition
block-editor356noBlockEditorSchema discriminantkeep, exempted
block-instance283noBlockInstanceSchema discriminantkeep, exempted
block-library320, 374noBlockLibrarySchema discriminantkeep, exempted
boolean62, 192yesBlockVariable.type data typeno change
button258yesSDUI nodeno change
card223yesSDUI nodeno change
div75, 227, 239yesSDUI nodeno change
icon231yesSDUI nodeno change
list300yesSDUI nodeno change
slot254nonothing — phantom, on the render pathdeleted
string56, 162, 170, 177, 185, 199noBlockVariable.type data typekeep, exempted
text243, 249yesSDUI nodeno change

slot is the only unregistered value that named a renderable node. The other five unregistered values are each a discriminant of a declared interface or a variable data type, and each keeps a live exemption. So there was no second slot-shaped defect to sweep up.

One incidental observation, not a defect on this page and not changed: boolean at :62 and :192 is a BlockVariable.type data type that happens to collide with a registered component key, so it passes the gate's flat rule without needing an exemption. The page is correct; the gate's judgment there is a coincidence rather than a check.

Gate calibration — proving the silence is a measurement

Removing the exemption is only meaningful if check-doc-component-types.mjs actually reads this file. A gate that is silent because it never looked is not a passing gate, so this was calibrated rather than assumed. The mutation was confirmed on disk by grep count, never by the editing tool's exit code, and the script carried a trap ... EXIT INT TERM restore.

Mutation leg — rewrite the registered type: 'card' node at :223 to a value nothing registers:

"type: 'card'," 1 -> 0 (deleted text)
'objui-4895-calibration-probe' 0 -> 1 (injected text)

Gate exit code, captured before any pipe: 1

content/docs/blocks/block-schema.mdx:223 [unregistered-doc-type] type 'objui-4895-calibration-probe' (plaintext)
type: 'objui-4895-calibration-probe',

Restore leg — probe absent, anchor back, git status clean for the file. Gate exit code: 0, ✅ Every documented component type is registered.

The gate reads this page.

The two halves are a matched pair

Both failure directions were exercised, so neither half is passing by accident:

  • Doc corrected but the exemption put backstale-exemption: content/docs/blocks/block-schema.mdx -> slot no code block in that file spells this type any more.
  • Exemption dropped but the phantom node put back → exit 1, content/docs/blocks/block-schema.mdx:305 [unregistered-doc-type] type 'slot' (plaintext)

Only both halves together are green.

Gate ledger

The slot entry in DOC_TYPE_EXEMPTIONS pointed at this card and is dropped, as the ruling requires. With the phantom node gone it would itself report as stale-exemption, so it is deleted rather than re-pointed; a comment records why, so the next reader does not re-add it. The three terse family reasons (block-instance / block-library / block-editor) are re-pointed at the ruling's framing — each now records that the schema is absent from AnySchema and has no renderer, which is the fact the gate's own standard asks an exemption reason to name.

Counters move coherently: exempted 139 → 138 (one entry gone), registered 743 → 744 (the new type: 'text' site), code blocks 1055 → 1056.

Verification — union run at ca35295a9 (final commit)

Exit codes captured before any pipe; verdicts quoted from each gate's own output.

gateexitverdict line
check-doc-component-types0Every documented component type is registered.
check-doc-links0Links are valid across 13 scan roots.
check-control-bytes0check-control-bytes: OK (scanned 4929 tracked text file(s); skipped 85 binary).
check-changeset-presence0No source of a released package changed in this range, so no changeset is owed.
check-changeset-fixed0All workspace packages are in the changeset fixed group.
check-changeset-no-major0No changeset declares a major bump.

scripts/__tests__/check-doc-component-types.test.ts34 passed (34), run at ca35295a9 from the repo root. Its three live-table assertions were also evaluated directly against this diff: stale-exemption findings []; exempted 138 (asserted > 50); declaration count 82 → 81 (asserted > 20).

eslint scripts/check-doc-component-types.mjs --no-inline-config --format json — 1 file linted, 0 errors, 0 warnings.

Declared narrowing: check-doc-snippet-types

Not run to completion — it needs a full monorepo build. The narrowing is a measurement, taken with that gate's own scanner, not an assumption:

  1. Its fence-language set is TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']) (its own source, line 224).
  2. scanFences() on this page returns blocks=0, markers=0 in both states — at HEAD~1 and on the working tree. Every fence on the page is plaintext (10 → 11), and the one I added is plaintext too.
  3. The page is not named in UNGATED_DOCS, so no ledger entry of mine can go stale.

The gate collects zero blocks from this page either way, so this diff cannot change its verdict. CI runs it in full regardless.

Changeset

.changeset/block-schema-docs-truth-4895.md, empty frontmatter — docs and a gate ledger only, no released package src/ is touched, declared explicitly rather than left undeclared. objectui has no skip-changeset label; the empty-frontmatter changeset is this repo's declaration mechanism.

Deliberately not done

  • No renderers built for block-library / block-editor / block-instance (Option A, rejected on zero pull).
  • Nothing retired from packages/types (Option C, deferred). No published type is touched.
  • Best Practices and Use Cases left as-is. Both still read somewhat aspirationally, but the status callout now governs the whole page, and gutting them is beyond the ruling's scope. Flagging rather than silently widening the diff.

Generated by Claude Code

)
Carries the recorded maintainer ruling on #4895 (Option B: docs-truth fix,
family re-scoped as type-level).
- Delete the phantom `type: 'slot'` node from the Complete Example template.
Nothing registers `slot`, so a reader who copied it got OBJUI-001.
- Teach the declared `slotContent` path instead, and state plainly that it is
declared-but-not-yet-consumed: nothing reads `slots` / `slotContent` /
`template` at runtime.
- Frame `BlockSchema` as a definition and drop the component-schema framing for
`block-library` / `block-editor` / `block-instance`.
- Disambiguate from the slot system that IS wired end to end (slotted record
pages), which is an unrelated vocabulary.
- Drop the now-stale `slot` entry from DOC_TYPE_EXEMPTIONS in
scripts/check-doc-component-types.mjs, and re-point the three terse family
reasons at the ruling's framing.
Option A (build renderers) is rejected on zero pull; Option C (retire the
family) is deferred. Neither is implemented here.
Empty frontmatter: docs and a gate ledger only, no released package `src/`
is touched. Declared explicitly rather than left undeclared.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude