docs(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7) - #7325

Merged
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7
Sep 2, 2026
Merged

docs(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7)#7325
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Part of #5174

Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by fence density and delivers all three whole: 25 blocks, 20 compile against the built dist, 5 carry a FRAGMENT_MARKER with a written, measured reason. 26 diagnostics cleared. Nothing about the gate's strictness moved.

Batch 6 (#7314) landed while this was in flight; origin/main was merged (never rebased). The two ledger hunks conflicted textually because they are adjacent lines in one sorted object, and the conflict was resolved as the union of both removal sets — everything either side took out stays out. create-plugin.mdx, the only entry inside the conflict region that neither batch claimed, is the only one that survives it.

Pages taken, and how each block reached zero

All counts produced with the gate's own scanFences, not a hand grep.

pageblockscompiledeclareddiagnostics clearedwhat was actually wrong
content/docs/plugins/plugin-timeline.mdx9901 (1 syntax, 0 semantic)No fabricated key — every key the page documents has read sites. The page's real problem was that it verified nothing: eight blocks were unannotated literals, and the two "Date Formatting" / "Time Scales" fences held bare { dateFormat: 'short' } objects that TypeScript parses as block statements containing labelled expressions{ dateFormatt: 'short' } was equally green. Five schema literals now carry the TimelineSchema annotation the page's own prose already claims ("Every key above is declared on the exported TimelineSchema, so … a wrong value is a type error"), and the two value-list fences became indexed-access declarations. items: [...] in "Custom Colors" was the one parse failure.
content/docs/plugins/plugin-kanban.mdx88014 (6 syntax, 8 semantic)The eight semantic diagnostics were TS7006 on onCardMove's four parameters, twice — the callback was untyped because the literal was unannotated, while KanbanSchema declares it as taking four typed parameters. Five literals now carry KanbanSchema / KanbanCard / KanbanColumn. Three parse failures: a bare object-kanban literal, cards: [...], and an "Event Handling" block with columns: [...], a top-level await in a void-returning callback, and three ambient names — rewritten as a real hook. One type gap found and filed (#7322), not fixed here.
content/docs/utilities/runner.mdx83511 (5 syntax, 6 semantic)No fabricated API. Five blocks genuinely cannot compile here and are declared with measured reasons: four name @object-ui/plugin-yourplugin / -myplugin / ./components/MyComponent — the package or file the reader is being walked through creating — and one is a single property out of resolve.alias. Two real repairs: the Error Boundaries example imported SchemaErrorBoundary but rendered SchemaRenderer with neither the component nor schema in scope, so a reader copying it got two TS2304s; and defineConfig was called with no import. Two fences holding JSX were labelled typescript and are now tsx — a mislabel, corrected rather than declared. One fence held two files (a TS side-effect import and a JSON page document); the JSON half is a mislabel and now has its own json fence.

Ledger 37 → 34 (3 .mdx + 31 README). Covered documents 187 → 190 — strictly grows. Covered blocks 481 → 506; compiled 345 → 365; declared fragments 136 → 141. The per-page splits (9+8+3 = 20 compile, 0+0+5 = 5 declared) reconcile exactly with those deltas.

Invariants, each measured rather than asserted

  • The ledger hunk is removals only — 3 entry keys deleted, 0 added.
  • Gate strictness byte-identical, from the fence-scanning banner to EOF, merged base ad3d4029a vs this branch:
    sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3 — the same value on both sides, and the same value batches 5 and 6 recorded.
  • Covered count strictly grows — 187 → 190, ungated 37 → 34.
  • Every page taken is off the ledger and every block resolves the two honest ways — compile, or a FRAGMENT_MARKER whose reason names the measured diagnostics. No third route, no UNGATED_DOCS addition, no fence re-labelled to duck the gate. The two fence-language corrections go the other way: both blocks stay in the collected ts/tsx population, and the JSON document that leaves it was never TypeScript.

Before / after, both at a fully built tree, exit codes captured by redirect-then-$? and never through a pipe:

BEFORE (merged base ad3d4029a content for the four files, swapped in under an
EXIT/INT/TERM trap; restore proved by an empty `git diff HEAD`)
Scanned 224 document(s): 187 covered (86 hold a ts/tsx block), 37 ungated
Covered blocks: 481 — 345 to compile, 136 declared fragment(s)
Semantic phase: 345 of 345 block(s) judged, 0 failed exit 0
AFTER (9d6e6f5a7)
Scanned 224 document(s): 190 covered (89 hold a ts/tsx block), 34 ungated
Covered blocks: 506 — 365 to compile, 141 declared fragment(s)
Syntax phase: every block parsed, so every one of them reached the semantic phase.
Semantic phase: 365 of 365 block(s) judged, 0 failed exit 0

The branch point d717e8bc3 was measured the same way before the merge (184 covered / 40 ungated / 440 blocks, exit 0), which is batch 6's own BEFORE — the two agree.

The lid check, per page — including the negative results

Resolving an unresolved name can uncover diagnostics the first error was hiding. Run on all three, each planted and restored from HEAD under a trap:

  • plugin-kanban.mdx — POSITIVE, and it is the finding. The row-cap example is an object-kanban node. Annotated with ObjectKanbanSchema: TS2741: Property 'groupField' is missing … but required. Annotated with KanbanSchema, which is what ObjectKanbanComponentProps.schema actually declares: TS2322: Type '"object-kanban"' is not assignable to type '"kanban"'. Neither declared type accepts the shape the renderer reads. Note what the first diagnostic does not say — groupBy and limit raised nothing at all, because BaseSchema's index signature absorbed them. Filed as finding(types,plugin-kanban): ObjectKanbanSchema requires groupField (zero read sites) and declares neither groupBy nor limit — no working object-kanban node is assignable to any declared type #7322; the block ships as a plain const with no annotation.
  • plugin-timeline.mdx — NEGATIVE. The single parse failure (items: [...]) had kept that block out of the semantic phase entirely. With it resolved and five literals newly annotated, the re-run exposed nothing underneath. Reported as a negative result.
  • runner.mdx — NEGATIVE, twice. The declared fragments hide their contents from the gate, so both were probed with the fictional specifier shimmed to a real one: "Plugin Not Loading" with @object-ui/plugin-yourplugin swapped for @object-ui/plugin-markdown, and the ComponentRegistry.register block with ./components/MyComponent replaced by a local declaration. Both came back clean — the declared blocks are correct documentation the gate cannot reach, not defects in hiding.

The read-site grep — what the compiler structurally cannot see

BaseSchema carries [key: string]: any, and KanbanCard carries one of its own, so a wrong key on either is invisible to this gate. Every schema literal touched here was therefore also grepped for read sites:

  • Timelinevariant, items, dateFormat, scale, rowLabel, minDate, maxDate, className all read in plugin-timeline/src/renderer.tsx; the item keys time, title, description, variant, icon, content, className, startDate, endDate and the gantt row's label likewise. Nothing fabricated.
  • Kanban — card id / title / description / badges, badge label / variant, column id / title / cards / limit / className all read in KanbanImpl.tsx and KanbanEnhanced.tsx. Nothing fabricated.
  • object-kanban — this is where it paid. schema.groupBy is read at nine sites in ObjectKanban.tsx and schema.limit at two, while groupField — the key ObjectKanbanSchema marks required — has zero read sites in packages/plugin-kanban. The package's own test suite writes { type: 'object-kanban', objectName, groupBy, limit }, the documented shape. The page is right and the declaration is the stale half.

What the green does not mean

Stated because a green run must not be read as more than it is. Each limit below was measured on these pages by planting a probe, proving it reached the disk by counting the injected and removed text, running the gate, and restoring from HEAD under a trap with the restore proved by an empty git diff HEAD and a blob hash matching the HEAD blob.

Three probes stayed GREEN — these are real blind spots:

  1. A misspelled top-level key on an annotated schema literal is not caught. Probe: dateFormatt: 'long' added to the annotated TimelineSchema literal. Gate stayed green — BaseSchema's index signature absorbs it.
  2. A misspelled key inside items is not caught either. Probe: titlee on a timeline item. Gate stayed green — TimelineSchema.items is declared any[], deliberately, because the two element shapes are discriminated by variant and read dynamically. The item vocabulary this page documents in full is verified by read-site grep and by nothing else.
  3. A misspelled key on a KanbanCard is not caught. Probe: descriptionn on the annotated card literal. Gate stayed green — KanbanCard carries its own [key: string]: any, for the runtime-synthesized card keys.

Two probes went RED — so the annotations are live, not no-ops:

  1. variant: 'diagonal' on the annotated TimelineSchema literal produced TS2322: Type '"diagonal"' is not assignable to type '"horizontal" | "vertical" | "gantt" | undefined'.
  2. variant: 'critical' on a KanbanCard badge produced TS2322: Type '"critical"' is not assignable to type '"default" | "outline" | "destructive" | "secondary" | undefined' — the badge object is sealed even though its parent card is not.

So the boundary on these pages is exactly this: declared members with closed unions are genuinely checked; anything reaching an index signature, and everything inside items, is not. That is why the read-site grep above is not optional decoration — it is the only thing covering the second half.

  1. The gate answers only "does this snippet compile against the published types". Schema-key validity under safeParse, whether a type literal names a registered component, and whether a shell example runs are three other questions with three other answers.

Type gap found and filed, not fixed here

packages/* source is out of this PR's surface, so it was filed unassigned after a targeted dedupe search (which returned the same-family cards #7311, #7313, #5903 and #6973, so it was live rather than silently empty; none is this defect):

Header prose corrected, and why it is a rewrite rather than a new number

Batch 6 recorded, without filing, that the ledger docblock's sentence "what remains here is 12 .mdx pages and 32 package READMEs" had drifted. Batch 7's dispatch authorised fixing it now that the ledger surface is being edited for its own sake.

It is corrected by removing the count, not by writing a fresh one. Both halves were already wrong (the README count has been 31 since #5259; the .mdx count moves with every batch), and any number written there would have been stale again within the hour — batch 6 landed mid-run and moved it. The sentence now points at the entries below it, which are re-derived every run and shrink-only, so the names in the list are the count. Nothing mechanical fails on a stale number in a comment, which is precisely why it should not carry one.

This edit is header prose, above the fence-scanning banner, so the strictness proof above is unaffected and still holds byte-for-byte.

Gate verdict lines

Everything below at 9d6e6f5a7, the final commit, with the closure built first under the shared verify lock (VERDICT command-exit 0) and dist/index.d.ts presence confirmed on disk in this worktree for all 25 filters before any gate result was trusted — turbo replayed cached logs naming another worktree's path (objectui-issue-5174-b6) on the first build, so this was checked rather than assumed.

check:doc-snippets exit 0 Semantic phase: 365 of 365 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
check:doc-fences exit 0 every TypeScript block in 224 document(s) is fenced ts/tsx/typescript,
except 80 declared file(s) carrying 90 block(s) of #5867's population
check:doc-types exit 0 Every documented component type is registered.
docs:check-links exit 0 Links are valid across 17 scan roots.
check:control-bytes exit 0 scanned 6015 tracked text file(s); skipped 85 binary
check:readme-exports exit 0 386 self-imports judged (386 real, 0 wrong-path, 0 fabricated)
check-changeset-presence exit 0 No source or published contract of a released package changed in
this range, so no changeset is owed.
vitest (repo root) exit 0 Test Files 7 passed (7) · Tests 177 passed (177)

The seven vitest files are every file git grep -l check-doc-snippet-types -- '*.test.ts' '*.test.tsx' names — the two obvious ones plus five pin suites in examples/schema-catalog, plugin-gantt, react and types that an edit to this script could have hijacked. Run from the repo root with --maxWorkers=2, never through pnpm --filter.

check:doc-fences is unmoved by this PR: plugin-timeline.mdx and plugin-kanban.mdx each carry one entry in #5867's shrink-only ledger for their plaintext-fenced "TypeScript Support" blocks, and those blocks are untouched here — that axis belongs to #5867.

check:readme-exports first read NOT MEASURED, not red: it exited 1 with 45 findings, all 45 the string its type entry ./dist/index.d.ts is not on disk -- run pnpm build first for plugin-ai and plugin-gantt, and none naming a file in this diff. Building those two closures made it a real measurement, and it is genuinely green.

One process note worth recording: the probe harness's first disk-proof step was itself defective — it used grep -cF on multi-line strings, which counts matching lines rather than occurrences, and reported a false "no-op mutation". It failed closed: the harness aborted and the trap restored before any gate ran, so no reading was taken from it. The proof step was corrected to count the full multi-line strings, and every probe above was then run and recorded once.

Declared narrowing

Repo-wide pnpm lint was narrowed to the four changed files, and the narrowing is a measurement rather than a skip:

  1. Population read from ESLint's own config, not from a guess: npx eslint --no-inline-config --format json on the four files reports the three .mdx pages as File ignored because no matching configuration was supplied — they are not in ESLint's population at all.
  2. File count read from the JSON output: 4 files judged, 3 ignored by config, 1 actually linted (scripts/check-doc-snippet-types.mjs), 0 errors, 0 warnings.
  3. Config invariance for untouched files: eslint.config.js sets no project, projectService or tsconfigRootDir, so type-aware linting is not enabled and nothing in this diff can move the verdict on a file it does not touch.

A control-byte self-scan (grep -naP over the C0 range plus DEL) across the four changed files returns no hits, beside the repo-wide gate above.


🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

…pet ledger
Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by
fence density -- plugin-timeline (9 blocks), plugin-kanban (8), runner (8) --
and delivers all three whole: 25 blocks, 20 compile against the built dist,
5 carry a FRAGMENT_MARKER with a measured reason.
The ledger hunk is removals only and nothing about the gate's strictness moved.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
… its length
The sentence carried a literal "12 .mdx pages and 32 package READMEs". Both
halves had drifted -- the README count has been 31 since objectui#5259, and the
.mdx count moves with every batch of objectui#5174's burn-down -- and nothing
fails on a stale number written there. The entries below are re-derived every
run and shrink-only, so the names in the list are the count.
Header prose only: the gate's strictness region, from the fence-scanning banner
to EOF, is byte-identical (sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
…-ledger-batch7
# Conflicts:
#	scripts/check-doc-snippet-types.mjs
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

@yinlianghui@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(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7) - #7325

Merged
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7
Sep 2, 2026
Merged

docs(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7)#7325
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Part of #5174

Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by fence density and delivers all three whole: 25 blocks, 20 compile against the built dist, 5 carry a FRAGMENT_MARKER with a written, measured reason. 26 diagnostics cleared. Nothing about the gate's strictness moved.

Batch 6 (#7314) landed while this was in flight; origin/main was merged (never rebased). The two ledger hunks conflicted textually because they are adjacent lines in one sorted object, and the conflict was resolved as the union of both removal sets — everything either side took out stays out. create-plugin.mdx, the only entry inside the conflict region that neither batch claimed, is the only one that survives it.

Pages taken, and how each block reached zero

All counts produced with the gate's own scanFences, not a hand grep.

pageblockscompiledeclareddiagnostics clearedwhat was actually wrong
content/docs/plugins/plugin-timeline.mdx9901 (1 syntax, 0 semantic)No fabricated key — every key the page documents has read sites. The page's real problem was that it verified nothing: eight blocks were unannotated literals, and the two "Date Formatting" / "Time Scales" fences held bare { dateFormat: 'short' } objects that TypeScript parses as block statements containing labelled expressions{ dateFormatt: 'short' } was equally green. Five schema literals now carry the TimelineSchema annotation the page's own prose already claims ("Every key above is declared on the exported TimelineSchema, so … a wrong value is a type error"), and the two value-list fences became indexed-access declarations. items: [...] in "Custom Colors" was the one parse failure.
content/docs/plugins/plugin-kanban.mdx88014 (6 syntax, 8 semantic)The eight semantic diagnostics were TS7006 on onCardMove's four parameters, twice — the callback was untyped because the literal was unannotated, while KanbanSchema declares it as taking four typed parameters. Five literals now carry KanbanSchema / KanbanCard / KanbanColumn. Three parse failures: a bare object-kanban literal, cards: [...], and an "Event Handling" block with columns: [...], a top-level await in a void-returning callback, and three ambient names — rewritten as a real hook. One type gap found and filed (#7322), not fixed here.
content/docs/utilities/runner.mdx83511 (5 syntax, 6 semantic)No fabricated API. Five blocks genuinely cannot compile here and are declared with measured reasons: four name @object-ui/plugin-yourplugin / -myplugin / ./components/MyComponent — the package or file the reader is being walked through creating — and one is a single property out of resolve.alias. Two real repairs: the Error Boundaries example imported SchemaErrorBoundary but rendered SchemaRenderer with neither the component nor schema in scope, so a reader copying it got two TS2304s; and defineConfig was called with no import. Two fences holding JSX were labelled typescript and are now tsx — a mislabel, corrected rather than declared. One fence held two files (a TS side-effect import and a JSON page document); the JSON half is a mislabel and now has its own json fence.

Ledger 37 → 34 (3 .mdx + 31 README). Covered documents 187 → 190 — strictly grows. Covered blocks 481 → 506; compiled 345 → 365; declared fragments 136 → 141. The per-page splits (9+8+3 = 20 compile, 0+0+5 = 5 declared) reconcile exactly with those deltas.

Invariants, each measured rather than asserted

  • The ledger hunk is removals only — 3 entry keys deleted, 0 added.
  • Gate strictness byte-identical, from the fence-scanning banner to EOF, merged base ad3d4029a vs this branch:
    sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3 — the same value on both sides, and the same value batches 5 and 6 recorded.
  • Covered count strictly grows — 187 → 190, ungated 37 → 34.
  • Every page taken is off the ledger and every block resolves the two honest ways — compile, or a FRAGMENT_MARKER whose reason names the measured diagnostics. No third route, no UNGATED_DOCS addition, no fence re-labelled to duck the gate. The two fence-language corrections go the other way: both blocks stay in the collected ts/tsx population, and the JSON document that leaves it was never TypeScript.

Before / after, both at a fully built tree, exit codes captured by redirect-then-$? and never through a pipe:

BEFORE (merged base ad3d4029a content for the four files, swapped in under an
EXIT/INT/TERM trap; restore proved by an empty `git diff HEAD`)
Scanned 224 document(s): 187 covered (86 hold a ts/tsx block), 37 ungated
Covered blocks: 481 — 345 to compile, 136 declared fragment(s)
Semantic phase: 345 of 345 block(s) judged, 0 failed exit 0
AFTER (9d6e6f5a7)
Scanned 224 document(s): 190 covered (89 hold a ts/tsx block), 34 ungated
Covered blocks: 506 — 365 to compile, 141 declared fragment(s)
Syntax phase: every block parsed, so every one of them reached the semantic phase.
Semantic phase: 365 of 365 block(s) judged, 0 failed exit 0

The branch point d717e8bc3 was measured the same way before the merge (184 covered / 40 ungated / 440 blocks, exit 0), which is batch 6's own BEFORE — the two agree.

The lid check, per page — including the negative results

Resolving an unresolved name can uncover diagnostics the first error was hiding. Run on all three, each planted and restored from HEAD under a trap:

  • plugin-kanban.mdx — POSITIVE, and it is the finding. The row-cap example is an object-kanban node. Annotated with ObjectKanbanSchema: TS2741: Property 'groupField' is missing … but required. Annotated with KanbanSchema, which is what ObjectKanbanComponentProps.schema actually declares: TS2322: Type '"object-kanban"' is not assignable to type '"kanban"'. Neither declared type accepts the shape the renderer reads. Note what the first diagnostic does not say — groupBy and limit raised nothing at all, because BaseSchema's index signature absorbed them. Filed as finding(types,plugin-kanban): ObjectKanbanSchema requires groupField (zero read sites) and declares neither groupBy nor limit — no working object-kanban node is assignable to any declared type #7322; the block ships as a plain const with no annotation.
  • plugin-timeline.mdx — NEGATIVE. The single parse failure (items: [...]) had kept that block out of the semantic phase entirely. With it resolved and five literals newly annotated, the re-run exposed nothing underneath. Reported as a negative result.
  • runner.mdx — NEGATIVE, twice. The declared fragments hide their contents from the gate, so both were probed with the fictional specifier shimmed to a real one: "Plugin Not Loading" with @object-ui/plugin-yourplugin swapped for @object-ui/plugin-markdown, and the ComponentRegistry.register block with ./components/MyComponent replaced by a local declaration. Both came back clean — the declared blocks are correct documentation the gate cannot reach, not defects in hiding.

The read-site grep — what the compiler structurally cannot see

BaseSchema carries [key: string]: any, and KanbanCard carries one of its own, so a wrong key on either is invisible to this gate. Every schema literal touched here was therefore also grepped for read sites:

  • Timelinevariant, items, dateFormat, scale, rowLabel, minDate, maxDate, className all read in plugin-timeline/src/renderer.tsx; the item keys time, title, description, variant, icon, content, className, startDate, endDate and the gantt row's label likewise. Nothing fabricated.
  • Kanban — card id / title / description / badges, badge label / variant, column id / title / cards / limit / className all read in KanbanImpl.tsx and KanbanEnhanced.tsx. Nothing fabricated.
  • object-kanban — this is where it paid. schema.groupBy is read at nine sites in ObjectKanban.tsx and schema.limit at two, while groupField — the key ObjectKanbanSchema marks required — has zero read sites in packages/plugin-kanban. The package's own test suite writes { type: 'object-kanban', objectName, groupBy, limit }, the documented shape. The page is right and the declaration is the stale half.

What the green does not mean

Stated because a green run must not be read as more than it is. Each limit below was measured on these pages by planting a probe, proving it reached the disk by counting the injected and removed text, running the gate, and restoring from HEAD under a trap with the restore proved by an empty git diff HEAD and a blob hash matching the HEAD blob.

Three probes stayed GREEN — these are real blind spots:

  1. A misspelled top-level key on an annotated schema literal is not caught. Probe: dateFormatt: 'long' added to the annotated TimelineSchema literal. Gate stayed green — BaseSchema's index signature absorbs it.
  2. A misspelled key inside items is not caught either. Probe: titlee on a timeline item. Gate stayed green — TimelineSchema.items is declared any[], deliberately, because the two element shapes are discriminated by variant and read dynamically. The item vocabulary this page documents in full is verified by read-site grep and by nothing else.
  3. A misspelled key on a KanbanCard is not caught. Probe: descriptionn on the annotated card literal. Gate stayed green — KanbanCard carries its own [key: string]: any, for the runtime-synthesized card keys.

Two probes went RED — so the annotations are live, not no-ops:

  1. variant: 'diagonal' on the annotated TimelineSchema literal produced TS2322: Type '"diagonal"' is not assignable to type '"horizontal" | "vertical" | "gantt" | undefined'.
  2. variant: 'critical' on a KanbanCard badge produced TS2322: Type '"critical"' is not assignable to type '"default" | "outline" | "destructive" | "secondary" | undefined' — the badge object is sealed even though its parent card is not.

So the boundary on these pages is exactly this: declared members with closed unions are genuinely checked; anything reaching an index signature, and everything inside items, is not. That is why the read-site grep above is not optional decoration — it is the only thing covering the second half.

  1. The gate answers only "does this snippet compile against the published types". Schema-key validity under safeParse, whether a type literal names a registered component, and whether a shell example runs are three other questions with three other answers.

Type gap found and filed, not fixed here

packages/* source is out of this PR's surface, so it was filed unassigned after a targeted dedupe search (which returned the same-family cards #7311, #7313, #5903 and #6973, so it was live rather than silently empty; none is this defect):

Header prose corrected, and why it is a rewrite rather than a new number

Batch 6 recorded, without filing, that the ledger docblock's sentence "what remains here is 12 .mdx pages and 32 package READMEs" had drifted. Batch 7's dispatch authorised fixing it now that the ledger surface is being edited for its own sake.

It is corrected by removing the count, not by writing a fresh one. Both halves were already wrong (the README count has been 31 since #5259; the .mdx count moves with every batch), and any number written there would have been stale again within the hour — batch 6 landed mid-run and moved it. The sentence now points at the entries below it, which are re-derived every run and shrink-only, so the names in the list are the count. Nothing mechanical fails on a stale number in a comment, which is precisely why it should not carry one.

This edit is header prose, above the fence-scanning banner, so the strictness proof above is unaffected and still holds byte-for-byte.

Gate verdict lines

Everything below at 9d6e6f5a7, the final commit, with the closure built first under the shared verify lock (VERDICT command-exit 0) and dist/index.d.ts presence confirmed on disk in this worktree for all 25 filters before any gate result was trusted — turbo replayed cached logs naming another worktree's path (objectui-issue-5174-b6) on the first build, so this was checked rather than assumed.

check:doc-snippets exit 0 Semantic phase: 365 of 365 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
check:doc-fences exit 0 every TypeScript block in 224 document(s) is fenced ts/tsx/typescript,
except 80 declared file(s) carrying 90 block(s) of #5867's population
check:doc-types exit 0 Every documented component type is registered.
docs:check-links exit 0 Links are valid across 17 scan roots.
check:control-bytes exit 0 scanned 6015 tracked text file(s); skipped 85 binary
check:readme-exports exit 0 386 self-imports judged (386 real, 0 wrong-path, 0 fabricated)
check-changeset-presence exit 0 No source or published contract of a released package changed in
this range, so no changeset is owed.
vitest (repo root) exit 0 Test Files 7 passed (7) · Tests 177 passed (177)

The seven vitest files are every file git grep -l check-doc-snippet-types -- '*.test.ts' '*.test.tsx' names — the two obvious ones plus five pin suites in examples/schema-catalog, plugin-gantt, react and types that an edit to this script could have hijacked. Run from the repo root with --maxWorkers=2, never through pnpm --filter.

check:doc-fences is unmoved by this PR: plugin-timeline.mdx and plugin-kanban.mdx each carry one entry in #5867's shrink-only ledger for their plaintext-fenced "TypeScript Support" blocks, and those blocks are untouched here — that axis belongs to #5867.

check:readme-exports first read NOT MEASURED, not red: it exited 1 with 45 findings, all 45 the string its type entry ./dist/index.d.ts is not on disk -- run pnpm build first for plugin-ai and plugin-gantt, and none naming a file in this diff. Building those two closures made it a real measurement, and it is genuinely green.

One process note worth recording: the probe harness's first disk-proof step was itself defective — it used grep -cF on multi-line strings, which counts matching lines rather than occurrences, and reported a false "no-op mutation". It failed closed: the harness aborted and the trap restored before any gate ran, so no reading was taken from it. The proof step was corrected to count the full multi-line strings, and every probe above was then run and recorded once.

Declared narrowing

Repo-wide pnpm lint was narrowed to the four changed files, and the narrowing is a measurement rather than a skip:

  1. Population read from ESLint's own config, not from a guess: npx eslint --no-inline-config --format json on the four files reports the three .mdx pages as File ignored because no matching configuration was supplied — they are not in ESLint's population at all.
  2. File count read from the JSON output: 4 files judged, 3 ignored by config, 1 actually linted (scripts/check-doc-snippet-types.mjs), 0 errors, 0 warnings.
  3. Config invariance for untouched files: eslint.config.js sets no project, projectService or tsconfigRootDir, so type-aware linting is not enabled and nothing in this diff can move the verdict on a file it does not touch.

A control-byte self-scan (grep -naP over the C0 range plus DEL) across the four changed files returns no hits, beside the repo-wide gate above.


🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

…pet ledger
Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by
fence density -- plugin-timeline (9 blocks), plugin-kanban (8), runner (8) --
and delivers all three whole: 25 blocks, 20 compile against the built dist,
5 carry a FRAGMENT_MARKER with a measured reason.
The ledger hunk is removals only and nothing about the gate's strictness moved.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
… its length
The sentence carried a literal "12 .mdx pages and 32 package READMEs". Both
halves had drifted -- the README count has been 31 since objectui#5259, and the
.mdx count moves with every batch of objectui#5174's burn-down -- and nothing
fails on a stale number written there. The entries below are re-derived every
run and shrink-only, so the names in the list are the count.
Header prose only: the gate's strictness region, from the fence-scanning banner
to EOF, is byte-identical (sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
…-ledger-batch7
# Conflicts:
#	scripts/check-doc-snippet-types.mjs
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

@yinlianghui@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(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7) - #7325

Merged
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7
Sep 2, 2026
Merged

docs(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7)#7325
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Part of #5174

Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by fence density and delivers all three whole: 25 blocks, 20 compile against the built dist, 5 carry a FRAGMENT_MARKER with a written, measured reason. 26 diagnostics cleared. Nothing about the gate's strictness moved.

Batch 6 (#7314) landed while this was in flight; origin/main was merged (never rebased). The two ledger hunks conflicted textually because they are adjacent lines in one sorted object, and the conflict was resolved as the union of both removal sets — everything either side took out stays out. create-plugin.mdx, the only entry inside the conflict region that neither batch claimed, is the only one that survives it.

Pages taken, and how each block reached zero

All counts produced with the gate's own scanFences, not a hand grep.

pageblockscompiledeclareddiagnostics clearedwhat was actually wrong
content/docs/plugins/plugin-timeline.mdx9901 (1 syntax, 0 semantic)No fabricated key — every key the page documents has read sites. The page's real problem was that it verified nothing: eight blocks were unannotated literals, and the two "Date Formatting" / "Time Scales" fences held bare { dateFormat: 'short' } objects that TypeScript parses as block statements containing labelled expressions{ dateFormatt: 'short' } was equally green. Five schema literals now carry the TimelineSchema annotation the page's own prose already claims ("Every key above is declared on the exported TimelineSchema, so … a wrong value is a type error"), and the two value-list fences became indexed-access declarations. items: [...] in "Custom Colors" was the one parse failure.
content/docs/plugins/plugin-kanban.mdx88014 (6 syntax, 8 semantic)The eight semantic diagnostics were TS7006 on onCardMove's four parameters, twice — the callback was untyped because the literal was unannotated, while KanbanSchema declares it as taking four typed parameters. Five literals now carry KanbanSchema / KanbanCard / KanbanColumn. Three parse failures: a bare object-kanban literal, cards: [...], and an "Event Handling" block with columns: [...], a top-level await in a void-returning callback, and three ambient names — rewritten as a real hook. One type gap found and filed (#7322), not fixed here.
content/docs/utilities/runner.mdx83511 (5 syntax, 6 semantic)No fabricated API. Five blocks genuinely cannot compile here and are declared with measured reasons: four name @object-ui/plugin-yourplugin / -myplugin / ./components/MyComponent — the package or file the reader is being walked through creating — and one is a single property out of resolve.alias. Two real repairs: the Error Boundaries example imported SchemaErrorBoundary but rendered SchemaRenderer with neither the component nor schema in scope, so a reader copying it got two TS2304s; and defineConfig was called with no import. Two fences holding JSX were labelled typescript and are now tsx — a mislabel, corrected rather than declared. One fence held two files (a TS side-effect import and a JSON page document); the JSON half is a mislabel and now has its own json fence.

Ledger 37 → 34 (3 .mdx + 31 README). Covered documents 187 → 190 — strictly grows. Covered blocks 481 → 506; compiled 345 → 365; declared fragments 136 → 141. The per-page splits (9+8+3 = 20 compile, 0+0+5 = 5 declared) reconcile exactly with those deltas.

Invariants, each measured rather than asserted

  • The ledger hunk is removals only — 3 entry keys deleted, 0 added.
  • Gate strictness byte-identical, from the fence-scanning banner to EOF, merged base ad3d4029a vs this branch:
    sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3 — the same value on both sides, and the same value batches 5 and 6 recorded.
  • Covered count strictly grows — 187 → 190, ungated 37 → 34.
  • Every page taken is off the ledger and every block resolves the two honest ways — compile, or a FRAGMENT_MARKER whose reason names the measured diagnostics. No third route, no UNGATED_DOCS addition, no fence re-labelled to duck the gate. The two fence-language corrections go the other way: both blocks stay in the collected ts/tsx population, and the JSON document that leaves it was never TypeScript.

Before / after, both at a fully built tree, exit codes captured by redirect-then-$? and never through a pipe:

BEFORE (merged base ad3d4029a content for the four files, swapped in under an
EXIT/INT/TERM trap; restore proved by an empty `git diff HEAD`)
Scanned 224 document(s): 187 covered (86 hold a ts/tsx block), 37 ungated
Covered blocks: 481 — 345 to compile, 136 declared fragment(s)
Semantic phase: 345 of 345 block(s) judged, 0 failed exit 0
AFTER (9d6e6f5a7)
Scanned 224 document(s): 190 covered (89 hold a ts/tsx block), 34 ungated
Covered blocks: 506 — 365 to compile, 141 declared fragment(s)
Syntax phase: every block parsed, so every one of them reached the semantic phase.
Semantic phase: 365 of 365 block(s) judged, 0 failed exit 0

The branch point d717e8bc3 was measured the same way before the merge (184 covered / 40 ungated / 440 blocks, exit 0), which is batch 6's own BEFORE — the two agree.

The lid check, per page — including the negative results

Resolving an unresolved name can uncover diagnostics the first error was hiding. Run on all three, each planted and restored from HEAD under a trap:

  • plugin-kanban.mdx — POSITIVE, and it is the finding. The row-cap example is an object-kanban node. Annotated with ObjectKanbanSchema: TS2741: Property 'groupField' is missing … but required. Annotated with KanbanSchema, which is what ObjectKanbanComponentProps.schema actually declares: TS2322: Type '"object-kanban"' is not assignable to type '"kanban"'. Neither declared type accepts the shape the renderer reads. Note what the first diagnostic does not say — groupBy and limit raised nothing at all, because BaseSchema's index signature absorbed them. Filed as finding(types,plugin-kanban): ObjectKanbanSchema requires groupField (zero read sites) and declares neither groupBy nor limit — no working object-kanban node is assignable to any declared type #7322; the block ships as a plain const with no annotation.
  • plugin-timeline.mdx — NEGATIVE. The single parse failure (items: [...]) had kept that block out of the semantic phase entirely. With it resolved and five literals newly annotated, the re-run exposed nothing underneath. Reported as a negative result.
  • runner.mdx — NEGATIVE, twice. The declared fragments hide their contents from the gate, so both were probed with the fictional specifier shimmed to a real one: "Plugin Not Loading" with @object-ui/plugin-yourplugin swapped for @object-ui/plugin-markdown, and the ComponentRegistry.register block with ./components/MyComponent replaced by a local declaration. Both came back clean — the declared blocks are correct documentation the gate cannot reach, not defects in hiding.

The read-site grep — what the compiler structurally cannot see

BaseSchema carries [key: string]: any, and KanbanCard carries one of its own, so a wrong key on either is invisible to this gate. Every schema literal touched here was therefore also grepped for read sites:

  • Timelinevariant, items, dateFormat, scale, rowLabel, minDate, maxDate, className all read in plugin-timeline/src/renderer.tsx; the item keys time, title, description, variant, icon, content, className, startDate, endDate and the gantt row's label likewise. Nothing fabricated.
  • Kanban — card id / title / description / badges, badge label / variant, column id / title / cards / limit / className all read in KanbanImpl.tsx and KanbanEnhanced.tsx. Nothing fabricated.
  • object-kanban — this is where it paid. schema.groupBy is read at nine sites in ObjectKanban.tsx and schema.limit at two, while groupField — the key ObjectKanbanSchema marks required — has zero read sites in packages/plugin-kanban. The package's own test suite writes { type: 'object-kanban', objectName, groupBy, limit }, the documented shape. The page is right and the declaration is the stale half.

What the green does not mean

Stated because a green run must not be read as more than it is. Each limit below was measured on these pages by planting a probe, proving it reached the disk by counting the injected and removed text, running the gate, and restoring from HEAD under a trap with the restore proved by an empty git diff HEAD and a blob hash matching the HEAD blob.

Three probes stayed GREEN — these are real blind spots:

  1. A misspelled top-level key on an annotated schema literal is not caught. Probe: dateFormatt: 'long' added to the annotated TimelineSchema literal. Gate stayed green — BaseSchema's index signature absorbs it.
  2. A misspelled key inside items is not caught either. Probe: titlee on a timeline item. Gate stayed green — TimelineSchema.items is declared any[], deliberately, because the two element shapes are discriminated by variant and read dynamically. The item vocabulary this page documents in full is verified by read-site grep and by nothing else.
  3. A misspelled key on a KanbanCard is not caught. Probe: descriptionn on the annotated card literal. Gate stayed green — KanbanCard carries its own [key: string]: any, for the runtime-synthesized card keys.

Two probes went RED — so the annotations are live, not no-ops:

  1. variant: 'diagonal' on the annotated TimelineSchema literal produced TS2322: Type '"diagonal"' is not assignable to type '"horizontal" | "vertical" | "gantt" | undefined'.
  2. variant: 'critical' on a KanbanCard badge produced TS2322: Type '"critical"' is not assignable to type '"default" | "outline" | "destructive" | "secondary" | undefined' — the badge object is sealed even though its parent card is not.

So the boundary on these pages is exactly this: declared members with closed unions are genuinely checked; anything reaching an index signature, and everything inside items, is not. That is why the read-site grep above is not optional decoration — it is the only thing covering the second half.

  1. The gate answers only "does this snippet compile against the published types". Schema-key validity under safeParse, whether a type literal names a registered component, and whether a shell example runs are three other questions with three other answers.

Type gap found and filed, not fixed here

packages/* source is out of this PR's surface, so it was filed unassigned after a targeted dedupe search (which returned the same-family cards #7311, #7313, #5903 and #6973, so it was live rather than silently empty; none is this defect):

Header prose corrected, and why it is a rewrite rather than a new number

Batch 6 recorded, without filing, that the ledger docblock's sentence "what remains here is 12 .mdx pages and 32 package READMEs" had drifted. Batch 7's dispatch authorised fixing it now that the ledger surface is being edited for its own sake.

It is corrected by removing the count, not by writing a fresh one. Both halves were already wrong (the README count has been 31 since #5259; the .mdx count moves with every batch), and any number written there would have been stale again within the hour — batch 6 landed mid-run and moved it. The sentence now points at the entries below it, which are re-derived every run and shrink-only, so the names in the list are the count. Nothing mechanical fails on a stale number in a comment, which is precisely why it should not carry one.

This edit is header prose, above the fence-scanning banner, so the strictness proof above is unaffected and still holds byte-for-byte.

Gate verdict lines

Everything below at 9d6e6f5a7, the final commit, with the closure built first under the shared verify lock (VERDICT command-exit 0) and dist/index.d.ts presence confirmed on disk in this worktree for all 25 filters before any gate result was trusted — turbo replayed cached logs naming another worktree's path (objectui-issue-5174-b6) on the first build, so this was checked rather than assumed.

check:doc-snippets exit 0 Semantic phase: 365 of 365 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
check:doc-fences exit 0 every TypeScript block in 224 document(s) is fenced ts/tsx/typescript,
except 80 declared file(s) carrying 90 block(s) of #5867's population
check:doc-types exit 0 Every documented component type is registered.
docs:check-links exit 0 Links are valid across 17 scan roots.
check:control-bytes exit 0 scanned 6015 tracked text file(s); skipped 85 binary
check:readme-exports exit 0 386 self-imports judged (386 real, 0 wrong-path, 0 fabricated)
check-changeset-presence exit 0 No source or published contract of a released package changed in
this range, so no changeset is owed.
vitest (repo root) exit 0 Test Files 7 passed (7) · Tests 177 passed (177)

The seven vitest files are every file git grep -l check-doc-snippet-types -- '*.test.ts' '*.test.tsx' names — the two obvious ones plus five pin suites in examples/schema-catalog, plugin-gantt, react and types that an edit to this script could have hijacked. Run from the repo root with --maxWorkers=2, never through pnpm --filter.

check:doc-fences is unmoved by this PR: plugin-timeline.mdx and plugin-kanban.mdx each carry one entry in #5867's shrink-only ledger for their plaintext-fenced "TypeScript Support" blocks, and those blocks are untouched here — that axis belongs to #5867.

check:readme-exports first read NOT MEASURED, not red: it exited 1 with 45 findings, all 45 the string its type entry ./dist/index.d.ts is not on disk -- run pnpm build first for plugin-ai and plugin-gantt, and none naming a file in this diff. Building those two closures made it a real measurement, and it is genuinely green.

One process note worth recording: the probe harness's first disk-proof step was itself defective — it used grep -cF on multi-line strings, which counts matching lines rather than occurrences, and reported a false "no-op mutation". It failed closed: the harness aborted and the trap restored before any gate ran, so no reading was taken from it. The proof step was corrected to count the full multi-line strings, and every probe above was then run and recorded once.

Declared narrowing

Repo-wide pnpm lint was narrowed to the four changed files, and the narrowing is a measurement rather than a skip:

  1. Population read from ESLint's own config, not from a guess: npx eslint --no-inline-config --format json on the four files reports the three .mdx pages as File ignored because no matching configuration was supplied — they are not in ESLint's population at all.
  2. File count read from the JSON output: 4 files judged, 3 ignored by config, 1 actually linted (scripts/check-doc-snippet-types.mjs), 0 errors, 0 warnings.
  3. Config invariance for untouched files: eslint.config.js sets no project, projectService or tsconfigRootDir, so type-aware linting is not enabled and nothing in this diff can move the verdict on a file it does not touch.

A control-byte self-scan (grep -naP over the C0 range plus DEL) across the four changed files returns no hits, beside the repo-wide gate above.


🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

…pet ledger
Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by
fence density -- plugin-timeline (9 blocks), plugin-kanban (8), runner (8) --
and delivers all three whole: 25 blocks, 20 compile against the built dist,
5 carry a FRAGMENT_MARKER with a measured reason.
The ledger hunk is removals only and nothing about the gate's strictness moved.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
… its length
The sentence carried a literal "12 .mdx pages and 32 package READMEs". Both
halves had drifted -- the README count has been 31 since objectui#5259, and the
.mdx count moves with every batch of objectui#5174's burn-down -- and nothing
fails on a stale number written there. The entries below are re-derived every
run and shrink-only, so the names in the list are the count.
Header prose only: the gate's strictness region, from the fence-scanning banner
to EOF, is byte-identical (sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
…-ledger-batch7
# Conflicts:
#	scripts/check-doc-snippet-types.mjs
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

@yinlianghui@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(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7) - #7325

Merged
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7
Sep 2, 2026
Merged

docs(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7)#7325
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Part of #5174

Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by fence density and delivers all three whole: 25 blocks, 20 compile against the built dist, 5 carry a FRAGMENT_MARKER with a written, measured reason. 26 diagnostics cleared. Nothing about the gate's strictness moved.

Batch 6 (#7314) landed while this was in flight; origin/main was merged (never rebased). The two ledger hunks conflicted textually because they are adjacent lines in one sorted object, and the conflict was resolved as the union of both removal sets — everything either side took out stays out. create-plugin.mdx, the only entry inside the conflict region that neither batch claimed, is the only one that survives it.

Pages taken, and how each block reached zero

All counts produced with the gate's own scanFences, not a hand grep.

pageblockscompiledeclareddiagnostics clearedwhat was actually wrong
content/docs/plugins/plugin-timeline.mdx9901 (1 syntax, 0 semantic)No fabricated key — every key the page documents has read sites. The page's real problem was that it verified nothing: eight blocks were unannotated literals, and the two "Date Formatting" / "Time Scales" fences held bare { dateFormat: 'short' } objects that TypeScript parses as block statements containing labelled expressions{ dateFormatt: 'short' } was equally green. Five schema literals now carry the TimelineSchema annotation the page's own prose already claims ("Every key above is declared on the exported TimelineSchema, so … a wrong value is a type error"), and the two value-list fences became indexed-access declarations. items: [...] in "Custom Colors" was the one parse failure.
content/docs/plugins/plugin-kanban.mdx88014 (6 syntax, 8 semantic)The eight semantic diagnostics were TS7006 on onCardMove's four parameters, twice — the callback was untyped because the literal was unannotated, while KanbanSchema declares it as taking four typed parameters. Five literals now carry KanbanSchema / KanbanCard / KanbanColumn. Three parse failures: a bare object-kanban literal, cards: [...], and an "Event Handling" block with columns: [...], a top-level await in a void-returning callback, and three ambient names — rewritten as a real hook. One type gap found and filed (#7322), not fixed here.
content/docs/utilities/runner.mdx83511 (5 syntax, 6 semantic)No fabricated API. Five blocks genuinely cannot compile here and are declared with measured reasons: four name @object-ui/plugin-yourplugin / -myplugin / ./components/MyComponent — the package or file the reader is being walked through creating — and one is a single property out of resolve.alias. Two real repairs: the Error Boundaries example imported SchemaErrorBoundary but rendered SchemaRenderer with neither the component nor schema in scope, so a reader copying it got two TS2304s; and defineConfig was called with no import. Two fences holding JSX were labelled typescript and are now tsx — a mislabel, corrected rather than declared. One fence held two files (a TS side-effect import and a JSON page document); the JSON half is a mislabel and now has its own json fence.

Ledger 37 → 34 (3 .mdx + 31 README). Covered documents 187 → 190 — strictly grows. Covered blocks 481 → 506; compiled 345 → 365; declared fragments 136 → 141. The per-page splits (9+8+3 = 20 compile, 0+0+5 = 5 declared) reconcile exactly with those deltas.

Invariants, each measured rather than asserted

  • The ledger hunk is removals only — 3 entry keys deleted, 0 added.
  • Gate strictness byte-identical, from the fence-scanning banner to EOF, merged base ad3d4029a vs this branch:
    sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3 — the same value on both sides, and the same value batches 5 and 6 recorded.
  • Covered count strictly grows — 187 → 190, ungated 37 → 34.
  • Every page taken is off the ledger and every block resolves the two honest ways — compile, or a FRAGMENT_MARKER whose reason names the measured diagnostics. No third route, no UNGATED_DOCS addition, no fence re-labelled to duck the gate. The two fence-language corrections go the other way: both blocks stay in the collected ts/tsx population, and the JSON document that leaves it was never TypeScript.

Before / after, both at a fully built tree, exit codes captured by redirect-then-$? and never through a pipe:

BEFORE (merged base ad3d4029a content for the four files, swapped in under an
EXIT/INT/TERM trap; restore proved by an empty `git diff HEAD`)
Scanned 224 document(s): 187 covered (86 hold a ts/tsx block), 37 ungated
Covered blocks: 481 — 345 to compile, 136 declared fragment(s)
Semantic phase: 345 of 345 block(s) judged, 0 failed exit 0
AFTER (9d6e6f5a7)
Scanned 224 document(s): 190 covered (89 hold a ts/tsx block), 34 ungated
Covered blocks: 506 — 365 to compile, 141 declared fragment(s)
Syntax phase: every block parsed, so every one of them reached the semantic phase.
Semantic phase: 365 of 365 block(s) judged, 0 failed exit 0

The branch point d717e8bc3 was measured the same way before the merge (184 covered / 40 ungated / 440 blocks, exit 0), which is batch 6's own BEFORE — the two agree.

The lid check, per page — including the negative results

Resolving an unresolved name can uncover diagnostics the first error was hiding. Run on all three, each planted and restored from HEAD under a trap:

  • plugin-kanban.mdx — POSITIVE, and it is the finding. The row-cap example is an object-kanban node. Annotated with ObjectKanbanSchema: TS2741: Property 'groupField' is missing … but required. Annotated with KanbanSchema, which is what ObjectKanbanComponentProps.schema actually declares: TS2322: Type '"object-kanban"' is not assignable to type '"kanban"'. Neither declared type accepts the shape the renderer reads. Note what the first diagnostic does not say — groupBy and limit raised nothing at all, because BaseSchema's index signature absorbed them. Filed as finding(types,plugin-kanban): ObjectKanbanSchema requires groupField (zero read sites) and declares neither groupBy nor limit — no working object-kanban node is assignable to any declared type #7322; the block ships as a plain const with no annotation.
  • plugin-timeline.mdx — NEGATIVE. The single parse failure (items: [...]) had kept that block out of the semantic phase entirely. With it resolved and five literals newly annotated, the re-run exposed nothing underneath. Reported as a negative result.
  • runner.mdx — NEGATIVE, twice. The declared fragments hide their contents from the gate, so both were probed with the fictional specifier shimmed to a real one: "Plugin Not Loading" with @object-ui/plugin-yourplugin swapped for @object-ui/plugin-markdown, and the ComponentRegistry.register block with ./components/MyComponent replaced by a local declaration. Both came back clean — the declared blocks are correct documentation the gate cannot reach, not defects in hiding.

The read-site grep — what the compiler structurally cannot see

BaseSchema carries [key: string]: any, and KanbanCard carries one of its own, so a wrong key on either is invisible to this gate. Every schema literal touched here was therefore also grepped for read sites:

  • Timelinevariant, items, dateFormat, scale, rowLabel, minDate, maxDate, className all read in plugin-timeline/src/renderer.tsx; the item keys time, title, description, variant, icon, content, className, startDate, endDate and the gantt row's label likewise. Nothing fabricated.
  • Kanban — card id / title / description / badges, badge label / variant, column id / title / cards / limit / className all read in KanbanImpl.tsx and KanbanEnhanced.tsx. Nothing fabricated.
  • object-kanban — this is where it paid. schema.groupBy is read at nine sites in ObjectKanban.tsx and schema.limit at two, while groupField — the key ObjectKanbanSchema marks required — has zero read sites in packages/plugin-kanban. The package's own test suite writes { type: 'object-kanban', objectName, groupBy, limit }, the documented shape. The page is right and the declaration is the stale half.

What the green does not mean

Stated because a green run must not be read as more than it is. Each limit below was measured on these pages by planting a probe, proving it reached the disk by counting the injected and removed text, running the gate, and restoring from HEAD under a trap with the restore proved by an empty git diff HEAD and a blob hash matching the HEAD blob.

Three probes stayed GREEN — these are real blind spots:

  1. A misspelled top-level key on an annotated schema literal is not caught. Probe: dateFormatt: 'long' added to the annotated TimelineSchema literal. Gate stayed green — BaseSchema's index signature absorbs it.
  2. A misspelled key inside items is not caught either. Probe: titlee on a timeline item. Gate stayed green — TimelineSchema.items is declared any[], deliberately, because the two element shapes are discriminated by variant and read dynamically. The item vocabulary this page documents in full is verified by read-site grep and by nothing else.
  3. A misspelled key on a KanbanCard is not caught. Probe: descriptionn on the annotated card literal. Gate stayed green — KanbanCard carries its own [key: string]: any, for the runtime-synthesized card keys.

Two probes went RED — so the annotations are live, not no-ops:

  1. variant: 'diagonal' on the annotated TimelineSchema literal produced TS2322: Type '"diagonal"' is not assignable to type '"horizontal" | "vertical" | "gantt" | undefined'.
  2. variant: 'critical' on a KanbanCard badge produced TS2322: Type '"critical"' is not assignable to type '"default" | "outline" | "destructive" | "secondary" | undefined' — the badge object is sealed even though its parent card is not.

So the boundary on these pages is exactly this: declared members with closed unions are genuinely checked; anything reaching an index signature, and everything inside items, is not. That is why the read-site grep above is not optional decoration — it is the only thing covering the second half.

  1. The gate answers only "does this snippet compile against the published types". Schema-key validity under safeParse, whether a type literal names a registered component, and whether a shell example runs are three other questions with three other answers.

Type gap found and filed, not fixed here

packages/* source is out of this PR's surface, so it was filed unassigned after a targeted dedupe search (which returned the same-family cards #7311, #7313, #5903 and #6973, so it was live rather than silently empty; none is this defect):

Header prose corrected, and why it is a rewrite rather than a new number

Batch 6 recorded, without filing, that the ledger docblock's sentence "what remains here is 12 .mdx pages and 32 package READMEs" had drifted. Batch 7's dispatch authorised fixing it now that the ledger surface is being edited for its own sake.

It is corrected by removing the count, not by writing a fresh one. Both halves were already wrong (the README count has been 31 since #5259; the .mdx count moves with every batch), and any number written there would have been stale again within the hour — batch 6 landed mid-run and moved it. The sentence now points at the entries below it, which are re-derived every run and shrink-only, so the names in the list are the count. Nothing mechanical fails on a stale number in a comment, which is precisely why it should not carry one.

This edit is header prose, above the fence-scanning banner, so the strictness proof above is unaffected and still holds byte-for-byte.

Gate verdict lines

Everything below at 9d6e6f5a7, the final commit, with the closure built first under the shared verify lock (VERDICT command-exit 0) and dist/index.d.ts presence confirmed on disk in this worktree for all 25 filters before any gate result was trusted — turbo replayed cached logs naming another worktree's path (objectui-issue-5174-b6) on the first build, so this was checked rather than assumed.

check:doc-snippets exit 0 Semantic phase: 365 of 365 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
check:doc-fences exit 0 every TypeScript block in 224 document(s) is fenced ts/tsx/typescript,
except 80 declared file(s) carrying 90 block(s) of #5867's population
check:doc-types exit 0 Every documented component type is registered.
docs:check-links exit 0 Links are valid across 17 scan roots.
check:control-bytes exit 0 scanned 6015 tracked text file(s); skipped 85 binary
check:readme-exports exit 0 386 self-imports judged (386 real, 0 wrong-path, 0 fabricated)
check-changeset-presence exit 0 No source or published contract of a released package changed in
this range, so no changeset is owed.
vitest (repo root) exit 0 Test Files 7 passed (7) · Tests 177 passed (177)

The seven vitest files are every file git grep -l check-doc-snippet-types -- '*.test.ts' '*.test.tsx' names — the two obvious ones plus five pin suites in examples/schema-catalog, plugin-gantt, react and types that an edit to this script could have hijacked. Run from the repo root with --maxWorkers=2, never through pnpm --filter.

check:doc-fences is unmoved by this PR: plugin-timeline.mdx and plugin-kanban.mdx each carry one entry in #5867's shrink-only ledger for their plaintext-fenced "TypeScript Support" blocks, and those blocks are untouched here — that axis belongs to #5867.

check:readme-exports first read NOT MEASURED, not red: it exited 1 with 45 findings, all 45 the string its type entry ./dist/index.d.ts is not on disk -- run pnpm build first for plugin-ai and plugin-gantt, and none naming a file in this diff. Building those two closures made it a real measurement, and it is genuinely green.

One process note worth recording: the probe harness's first disk-proof step was itself defective — it used grep -cF on multi-line strings, which counts matching lines rather than occurrences, and reported a false "no-op mutation". It failed closed: the harness aborted and the trap restored before any gate ran, so no reading was taken from it. The proof step was corrected to count the full multi-line strings, and every probe above was then run and recorded once.

Declared narrowing

Repo-wide pnpm lint was narrowed to the four changed files, and the narrowing is a measurement rather than a skip:

  1. Population read from ESLint's own config, not from a guess: npx eslint --no-inline-config --format json on the four files reports the three .mdx pages as File ignored because no matching configuration was supplied — they are not in ESLint's population at all.
  2. File count read from the JSON output: 4 files judged, 3 ignored by config, 1 actually linted (scripts/check-doc-snippet-types.mjs), 0 errors, 0 warnings.
  3. Config invariance for untouched files: eslint.config.js sets no project, projectService or tsconfigRootDir, so type-aware linting is not enabled and nothing in this diff can move the verdict on a file it does not touch.

A control-byte self-scan (grep -naP over the C0 range plus DEL) across the four changed files returns no hits, beside the repo-wide gate above.


🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

…pet ledger
Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by
fence density -- plugin-timeline (9 blocks), plugin-kanban (8), runner (8) --
and delivers all three whole: 25 blocks, 20 compile against the built dist,
5 carry a FRAGMENT_MARKER with a measured reason.
The ledger hunk is removals only and nothing about the gate's strictness moved.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
… its length
The sentence carried a literal "12 .mdx pages and 32 package READMEs". Both
halves had drifted -- the README count has been 31 since objectui#5259, and the
.mdx count moves with every batch of objectui#5174's burn-down -- and nothing
fails on a stale number written there. The entries below are re-derived every
run and shrink-only, so the names in the list are the count.
Header prose only: the gate's strictness region, from the fence-scanning banner
to EOF, is byte-identical (sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
…-ledger-batch7
# Conflicts:
#	scripts/check-doc-snippet-types.mjs
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

@yinlianghui@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(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7) - #7325

Merged
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7
Sep 2, 2026
Merged

docs(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7)#7325
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Part of #5174

Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by fence density and delivers all three whole: 25 blocks, 20 compile against the built dist, 5 carry a FRAGMENT_MARKER with a written, measured reason. 26 diagnostics cleared. Nothing about the gate's strictness moved.

Batch 6 (#7314) landed while this was in flight; origin/main was merged (never rebased). The two ledger hunks conflicted textually because they are adjacent lines in one sorted object, and the conflict was resolved as the union of both removal sets — everything either side took out stays out. create-plugin.mdx, the only entry inside the conflict region that neither batch claimed, is the only one that survives it.

Pages taken, and how each block reached zero

All counts produced with the gate's own scanFences, not a hand grep.

pageblockscompiledeclareddiagnostics clearedwhat was actually wrong
content/docs/plugins/plugin-timeline.mdx9901 (1 syntax, 0 semantic)No fabricated key — every key the page documents has read sites. The page's real problem was that it verified nothing: eight blocks were unannotated literals, and the two "Date Formatting" / "Time Scales" fences held bare { dateFormat: 'short' } objects that TypeScript parses as block statements containing labelled expressions{ dateFormatt: 'short' } was equally green. Five schema literals now carry the TimelineSchema annotation the page's own prose already claims ("Every key above is declared on the exported TimelineSchema, so … a wrong value is a type error"), and the two value-list fences became indexed-access declarations. items: [...] in "Custom Colors" was the one parse failure.
content/docs/plugins/plugin-kanban.mdx88014 (6 syntax, 8 semantic)The eight semantic diagnostics were TS7006 on onCardMove's four parameters, twice — the callback was untyped because the literal was unannotated, while KanbanSchema declares it as taking four typed parameters. Five literals now carry KanbanSchema / KanbanCard / KanbanColumn. Three parse failures: a bare object-kanban literal, cards: [...], and an "Event Handling" block with columns: [...], a top-level await in a void-returning callback, and three ambient names — rewritten as a real hook. One type gap found and filed (#7322), not fixed here.
content/docs/utilities/runner.mdx83511 (5 syntax, 6 semantic)No fabricated API. Five blocks genuinely cannot compile here and are declared with measured reasons: four name @object-ui/plugin-yourplugin / -myplugin / ./components/MyComponent — the package or file the reader is being walked through creating — and one is a single property out of resolve.alias. Two real repairs: the Error Boundaries example imported SchemaErrorBoundary but rendered SchemaRenderer with neither the component nor schema in scope, so a reader copying it got two TS2304s; and defineConfig was called with no import. Two fences holding JSX were labelled typescript and are now tsx — a mislabel, corrected rather than declared. One fence held two files (a TS side-effect import and a JSON page document); the JSON half is a mislabel and now has its own json fence.

Ledger 37 → 34 (3 .mdx + 31 README). Covered documents 187 → 190 — strictly grows. Covered blocks 481 → 506; compiled 345 → 365; declared fragments 136 → 141. The per-page splits (9+8+3 = 20 compile, 0+0+5 = 5 declared) reconcile exactly with those deltas.

Invariants, each measured rather than asserted

  • The ledger hunk is removals only — 3 entry keys deleted, 0 added.
  • Gate strictness byte-identical, from the fence-scanning banner to EOF, merged base ad3d4029a vs this branch:
    sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3 — the same value on both sides, and the same value batches 5 and 6 recorded.
  • Covered count strictly grows — 187 → 190, ungated 37 → 34.
  • Every page taken is off the ledger and every block resolves the two honest ways — compile, or a FRAGMENT_MARKER whose reason names the measured diagnostics. No third route, no UNGATED_DOCS addition, no fence re-labelled to duck the gate. The two fence-language corrections go the other way: both blocks stay in the collected ts/tsx population, and the JSON document that leaves it was never TypeScript.

Before / after, both at a fully built tree, exit codes captured by redirect-then-$? and never through a pipe:

BEFORE (merged base ad3d4029a content for the four files, swapped in under an
EXIT/INT/TERM trap; restore proved by an empty `git diff HEAD`)
Scanned 224 document(s): 187 covered (86 hold a ts/tsx block), 37 ungated
Covered blocks: 481 — 345 to compile, 136 declared fragment(s)
Semantic phase: 345 of 345 block(s) judged, 0 failed exit 0
AFTER (9d6e6f5a7)
Scanned 224 document(s): 190 covered (89 hold a ts/tsx block), 34 ungated
Covered blocks: 506 — 365 to compile, 141 declared fragment(s)
Syntax phase: every block parsed, so every one of them reached the semantic phase.
Semantic phase: 365 of 365 block(s) judged, 0 failed exit 0

The branch point d717e8bc3 was measured the same way before the merge (184 covered / 40 ungated / 440 blocks, exit 0), which is batch 6's own BEFORE — the two agree.

The lid check, per page — including the negative results

Resolving an unresolved name can uncover diagnostics the first error was hiding. Run on all three, each planted and restored from HEAD under a trap:

  • plugin-kanban.mdx — POSITIVE, and it is the finding. The row-cap example is an object-kanban node. Annotated with ObjectKanbanSchema: TS2741: Property 'groupField' is missing … but required. Annotated with KanbanSchema, which is what ObjectKanbanComponentProps.schema actually declares: TS2322: Type '"object-kanban"' is not assignable to type '"kanban"'. Neither declared type accepts the shape the renderer reads. Note what the first diagnostic does not say — groupBy and limit raised nothing at all, because BaseSchema's index signature absorbed them. Filed as finding(types,plugin-kanban): ObjectKanbanSchema requires groupField (zero read sites) and declares neither groupBy nor limit — no working object-kanban node is assignable to any declared type #7322; the block ships as a plain const with no annotation.
  • plugin-timeline.mdx — NEGATIVE. The single parse failure (items: [...]) had kept that block out of the semantic phase entirely. With it resolved and five literals newly annotated, the re-run exposed nothing underneath. Reported as a negative result.
  • runner.mdx — NEGATIVE, twice. The declared fragments hide their contents from the gate, so both were probed with the fictional specifier shimmed to a real one: "Plugin Not Loading" with @object-ui/plugin-yourplugin swapped for @object-ui/plugin-markdown, and the ComponentRegistry.register block with ./components/MyComponent replaced by a local declaration. Both came back clean — the declared blocks are correct documentation the gate cannot reach, not defects in hiding.

The read-site grep — what the compiler structurally cannot see

BaseSchema carries [key: string]: any, and KanbanCard carries one of its own, so a wrong key on either is invisible to this gate. Every schema literal touched here was therefore also grepped for read sites:

  • Timelinevariant, items, dateFormat, scale, rowLabel, minDate, maxDate, className all read in plugin-timeline/src/renderer.tsx; the item keys time, title, description, variant, icon, content, className, startDate, endDate and the gantt row's label likewise. Nothing fabricated.
  • Kanban — card id / title / description / badges, badge label / variant, column id / title / cards / limit / className all read in KanbanImpl.tsx and KanbanEnhanced.tsx. Nothing fabricated.
  • object-kanban — this is where it paid. schema.groupBy is read at nine sites in ObjectKanban.tsx and schema.limit at two, while groupField — the key ObjectKanbanSchema marks required — has zero read sites in packages/plugin-kanban. The package's own test suite writes { type: 'object-kanban', objectName, groupBy, limit }, the documented shape. The page is right and the declaration is the stale half.

What the green does not mean

Stated because a green run must not be read as more than it is. Each limit below was measured on these pages by planting a probe, proving it reached the disk by counting the injected and removed text, running the gate, and restoring from HEAD under a trap with the restore proved by an empty git diff HEAD and a blob hash matching the HEAD blob.

Three probes stayed GREEN — these are real blind spots:

  1. A misspelled top-level key on an annotated schema literal is not caught. Probe: dateFormatt: 'long' added to the annotated TimelineSchema literal. Gate stayed green — BaseSchema's index signature absorbs it.
  2. A misspelled key inside items is not caught either. Probe: titlee on a timeline item. Gate stayed green — TimelineSchema.items is declared any[], deliberately, because the two element shapes are discriminated by variant and read dynamically. The item vocabulary this page documents in full is verified by read-site grep and by nothing else.
  3. A misspelled key on a KanbanCard is not caught. Probe: descriptionn on the annotated card literal. Gate stayed green — KanbanCard carries its own [key: string]: any, for the runtime-synthesized card keys.

Two probes went RED — so the annotations are live, not no-ops:

  1. variant: 'diagonal' on the annotated TimelineSchema literal produced TS2322: Type '"diagonal"' is not assignable to type '"horizontal" | "vertical" | "gantt" | undefined'.
  2. variant: 'critical' on a KanbanCard badge produced TS2322: Type '"critical"' is not assignable to type '"default" | "outline" | "destructive" | "secondary" | undefined' — the badge object is sealed even though its parent card is not.

So the boundary on these pages is exactly this: declared members with closed unions are genuinely checked; anything reaching an index signature, and everything inside items, is not. That is why the read-site grep above is not optional decoration — it is the only thing covering the second half.

  1. The gate answers only "does this snippet compile against the published types". Schema-key validity under safeParse, whether a type literal names a registered component, and whether a shell example runs are three other questions with three other answers.

Type gap found and filed, not fixed here

packages/* source is out of this PR's surface, so it was filed unassigned after a targeted dedupe search (which returned the same-family cards #7311, #7313, #5903 and #6973, so it was live rather than silently empty; none is this defect):

Header prose corrected, and why it is a rewrite rather than a new number

Batch 6 recorded, without filing, that the ledger docblock's sentence "what remains here is 12 .mdx pages and 32 package READMEs" had drifted. Batch 7's dispatch authorised fixing it now that the ledger surface is being edited for its own sake.

It is corrected by removing the count, not by writing a fresh one. Both halves were already wrong (the README count has been 31 since #5259; the .mdx count moves with every batch), and any number written there would have been stale again within the hour — batch 6 landed mid-run and moved it. The sentence now points at the entries below it, which are re-derived every run and shrink-only, so the names in the list are the count. Nothing mechanical fails on a stale number in a comment, which is precisely why it should not carry one.

This edit is header prose, above the fence-scanning banner, so the strictness proof above is unaffected and still holds byte-for-byte.

Gate verdict lines

Everything below at 9d6e6f5a7, the final commit, with the closure built first under the shared verify lock (VERDICT command-exit 0) and dist/index.d.ts presence confirmed on disk in this worktree for all 25 filters before any gate result was trusted — turbo replayed cached logs naming another worktree's path (objectui-issue-5174-b6) on the first build, so this was checked rather than assumed.

check:doc-snippets exit 0 Semantic phase: 365 of 365 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
check:doc-fences exit 0 every TypeScript block in 224 document(s) is fenced ts/tsx/typescript,
except 80 declared file(s) carrying 90 block(s) of #5867's population
check:doc-types exit 0 Every documented component type is registered.
docs:check-links exit 0 Links are valid across 17 scan roots.
check:control-bytes exit 0 scanned 6015 tracked text file(s); skipped 85 binary
check:readme-exports exit 0 386 self-imports judged (386 real, 0 wrong-path, 0 fabricated)
check-changeset-presence exit 0 No source or published contract of a released package changed in
this range, so no changeset is owed.
vitest (repo root) exit 0 Test Files 7 passed (7) · Tests 177 passed (177)

The seven vitest files are every file git grep -l check-doc-snippet-types -- '*.test.ts' '*.test.tsx' names — the two obvious ones plus five pin suites in examples/schema-catalog, plugin-gantt, react and types that an edit to this script could have hijacked. Run from the repo root with --maxWorkers=2, never through pnpm --filter.

check:doc-fences is unmoved by this PR: plugin-timeline.mdx and plugin-kanban.mdx each carry one entry in #5867's shrink-only ledger for their plaintext-fenced "TypeScript Support" blocks, and those blocks are untouched here — that axis belongs to #5867.

check:readme-exports first read NOT MEASURED, not red: it exited 1 with 45 findings, all 45 the string its type entry ./dist/index.d.ts is not on disk -- run pnpm build first for plugin-ai and plugin-gantt, and none naming a file in this diff. Building those two closures made it a real measurement, and it is genuinely green.

One process note worth recording: the probe harness's first disk-proof step was itself defective — it used grep -cF on multi-line strings, which counts matching lines rather than occurrences, and reported a false "no-op mutation". It failed closed: the harness aborted and the trap restored before any gate ran, so no reading was taken from it. The proof step was corrected to count the full multi-line strings, and every probe above was then run and recorded once.

Declared narrowing

Repo-wide pnpm lint was narrowed to the four changed files, and the narrowing is a measurement rather than a skip:

  1. Population read from ESLint's own config, not from a guess: npx eslint --no-inline-config --format json on the four files reports the three .mdx pages as File ignored because no matching configuration was supplied — they are not in ESLint's population at all.
  2. File count read from the JSON output: 4 files judged, 3 ignored by config, 1 actually linted (scripts/check-doc-snippet-types.mjs), 0 errors, 0 warnings.
  3. Config invariance for untouched files: eslint.config.js sets no project, projectService or tsconfigRootDir, so type-aware linting is not enabled and nothing in this diff can move the verdict on a file it does not touch.

A control-byte self-scan (grep -naP over the C0 range plus DEL) across the four changed files returns no hits, beside the repo-wide gate above.


🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

…pet ledger
Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by
fence density -- plugin-timeline (9 blocks), plugin-kanban (8), runner (8) --
and delivers all three whole: 25 blocks, 20 compile against the built dist,
5 carry a FRAGMENT_MARKER with a measured reason.
The ledger hunk is removals only and nothing about the gate's strictness moved.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
… its length
The sentence carried a literal "12 .mdx pages and 32 package READMEs". Both
halves had drifted -- the README count has been 31 since objectui#5259, and the
.mdx count moves with every batch of objectui#5174's burn-down -- and nothing
fails on a stale number written there. The entries below are re-derived every
run and shrink-only, so the names in the list are the count.
Header prose only: the gate's strictness region, from the fence-scanning banner
to EOF, is byte-identical (sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
…-ledger-batch7
# Conflicts:
#	scripts/check-doc-snippet-types.mjs
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

@yinlianghui@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(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7) - #7325

Merged
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7
Sep 2, 2026
Merged

docs(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7)#7325
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Part of #5174

Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by fence density and delivers all three whole: 25 blocks, 20 compile against the built dist, 5 carry a FRAGMENT_MARKER with a written, measured reason. 26 diagnostics cleared. Nothing about the gate's strictness moved.

Batch 6 (#7314) landed while this was in flight; origin/main was merged (never rebased). The two ledger hunks conflicted textually because they are adjacent lines in one sorted object, and the conflict was resolved as the union of both removal sets — everything either side took out stays out. create-plugin.mdx, the only entry inside the conflict region that neither batch claimed, is the only one that survives it.

Pages taken, and how each block reached zero

All counts produced with the gate's own scanFences, not a hand grep.

pageblockscompiledeclareddiagnostics clearedwhat was actually wrong
content/docs/plugins/plugin-timeline.mdx9901 (1 syntax, 0 semantic)No fabricated key — every key the page documents has read sites. The page's real problem was that it verified nothing: eight blocks were unannotated literals, and the two "Date Formatting" / "Time Scales" fences held bare { dateFormat: 'short' } objects that TypeScript parses as block statements containing labelled expressions{ dateFormatt: 'short' } was equally green. Five schema literals now carry the TimelineSchema annotation the page's own prose already claims ("Every key above is declared on the exported TimelineSchema, so … a wrong value is a type error"), and the two value-list fences became indexed-access declarations. items: [...] in "Custom Colors" was the one parse failure.
content/docs/plugins/plugin-kanban.mdx88014 (6 syntax, 8 semantic)The eight semantic diagnostics were TS7006 on onCardMove's four parameters, twice — the callback was untyped because the literal was unannotated, while KanbanSchema declares it as taking four typed parameters. Five literals now carry KanbanSchema / KanbanCard / KanbanColumn. Three parse failures: a bare object-kanban literal, cards: [...], and an "Event Handling" block with columns: [...], a top-level await in a void-returning callback, and three ambient names — rewritten as a real hook. One type gap found and filed (#7322), not fixed here.
content/docs/utilities/runner.mdx83511 (5 syntax, 6 semantic)No fabricated API. Five blocks genuinely cannot compile here and are declared with measured reasons: four name @object-ui/plugin-yourplugin / -myplugin / ./components/MyComponent — the package or file the reader is being walked through creating — and one is a single property out of resolve.alias. Two real repairs: the Error Boundaries example imported SchemaErrorBoundary but rendered SchemaRenderer with neither the component nor schema in scope, so a reader copying it got two TS2304s; and defineConfig was called with no import. Two fences holding JSX were labelled typescript and are now tsx — a mislabel, corrected rather than declared. One fence held two files (a TS side-effect import and a JSON page document); the JSON half is a mislabel and now has its own json fence.

Ledger 37 → 34 (3 .mdx + 31 README). Covered documents 187 → 190 — strictly grows. Covered blocks 481 → 506; compiled 345 → 365; declared fragments 136 → 141. The per-page splits (9+8+3 = 20 compile, 0+0+5 = 5 declared) reconcile exactly with those deltas.

Invariants, each measured rather than asserted

  • The ledger hunk is removals only — 3 entry keys deleted, 0 added.
  • Gate strictness byte-identical, from the fence-scanning banner to EOF, merged base ad3d4029a vs this branch:
    sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3 — the same value on both sides, and the same value batches 5 and 6 recorded.
  • Covered count strictly grows — 187 → 190, ungated 37 → 34.
  • Every page taken is off the ledger and every block resolves the two honest ways — compile, or a FRAGMENT_MARKER whose reason names the measured diagnostics. No third route, no UNGATED_DOCS addition, no fence re-labelled to duck the gate. The two fence-language corrections go the other way: both blocks stay in the collected ts/tsx population, and the JSON document that leaves it was never TypeScript.

Before / after, both at a fully built tree, exit codes captured by redirect-then-$? and never through a pipe:

BEFORE (merged base ad3d4029a content for the four files, swapped in under an
EXIT/INT/TERM trap; restore proved by an empty `git diff HEAD`)
Scanned 224 document(s): 187 covered (86 hold a ts/tsx block), 37 ungated
Covered blocks: 481 — 345 to compile, 136 declared fragment(s)
Semantic phase: 345 of 345 block(s) judged, 0 failed exit 0
AFTER (9d6e6f5a7)
Scanned 224 document(s): 190 covered (89 hold a ts/tsx block), 34 ungated
Covered blocks: 506 — 365 to compile, 141 declared fragment(s)
Syntax phase: every block parsed, so every one of them reached the semantic phase.
Semantic phase: 365 of 365 block(s) judged, 0 failed exit 0

The branch point d717e8bc3 was measured the same way before the merge (184 covered / 40 ungated / 440 blocks, exit 0), which is batch 6's own BEFORE — the two agree.

The lid check, per page — including the negative results

Resolving an unresolved name can uncover diagnostics the first error was hiding. Run on all three, each planted and restored from HEAD under a trap:

  • plugin-kanban.mdx — POSITIVE, and it is the finding. The row-cap example is an object-kanban node. Annotated with ObjectKanbanSchema: TS2741: Property 'groupField' is missing … but required. Annotated with KanbanSchema, which is what ObjectKanbanComponentProps.schema actually declares: TS2322: Type '"object-kanban"' is not assignable to type '"kanban"'. Neither declared type accepts the shape the renderer reads. Note what the first diagnostic does not say — groupBy and limit raised nothing at all, because BaseSchema's index signature absorbed them. Filed as finding(types,plugin-kanban): ObjectKanbanSchema requires groupField (zero read sites) and declares neither groupBy nor limit — no working object-kanban node is assignable to any declared type #7322; the block ships as a plain const with no annotation.
  • plugin-timeline.mdx — NEGATIVE. The single parse failure (items: [...]) had kept that block out of the semantic phase entirely. With it resolved and five literals newly annotated, the re-run exposed nothing underneath. Reported as a negative result.
  • runner.mdx — NEGATIVE, twice. The declared fragments hide their contents from the gate, so both were probed with the fictional specifier shimmed to a real one: "Plugin Not Loading" with @object-ui/plugin-yourplugin swapped for @object-ui/plugin-markdown, and the ComponentRegistry.register block with ./components/MyComponent replaced by a local declaration. Both came back clean — the declared blocks are correct documentation the gate cannot reach, not defects in hiding.

The read-site grep — what the compiler structurally cannot see

BaseSchema carries [key: string]: any, and KanbanCard carries one of its own, so a wrong key on either is invisible to this gate. Every schema literal touched here was therefore also grepped for read sites:

  • Timelinevariant, items, dateFormat, scale, rowLabel, minDate, maxDate, className all read in plugin-timeline/src/renderer.tsx; the item keys time, title, description, variant, icon, content, className, startDate, endDate and the gantt row's label likewise. Nothing fabricated.
  • Kanban — card id / title / description / badges, badge label / variant, column id / title / cards / limit / className all read in KanbanImpl.tsx and KanbanEnhanced.tsx. Nothing fabricated.
  • object-kanban — this is where it paid. schema.groupBy is read at nine sites in ObjectKanban.tsx and schema.limit at two, while groupField — the key ObjectKanbanSchema marks required — has zero read sites in packages/plugin-kanban. The package's own test suite writes { type: 'object-kanban', objectName, groupBy, limit }, the documented shape. The page is right and the declaration is the stale half.

What the green does not mean

Stated because a green run must not be read as more than it is. Each limit below was measured on these pages by planting a probe, proving it reached the disk by counting the injected and removed text, running the gate, and restoring from HEAD under a trap with the restore proved by an empty git diff HEAD and a blob hash matching the HEAD blob.

Three probes stayed GREEN — these are real blind spots:

  1. A misspelled top-level key on an annotated schema literal is not caught. Probe: dateFormatt: 'long' added to the annotated TimelineSchema literal. Gate stayed green — BaseSchema's index signature absorbs it.
  2. A misspelled key inside items is not caught either. Probe: titlee on a timeline item. Gate stayed green — TimelineSchema.items is declared any[], deliberately, because the two element shapes are discriminated by variant and read dynamically. The item vocabulary this page documents in full is verified by read-site grep and by nothing else.
  3. A misspelled key on a KanbanCard is not caught. Probe: descriptionn on the annotated card literal. Gate stayed green — KanbanCard carries its own [key: string]: any, for the runtime-synthesized card keys.

Two probes went RED — so the annotations are live, not no-ops:

  1. variant: 'diagonal' on the annotated TimelineSchema literal produced TS2322: Type '"diagonal"' is not assignable to type '"horizontal" | "vertical" | "gantt" | undefined'.
  2. variant: 'critical' on a KanbanCard badge produced TS2322: Type '"critical"' is not assignable to type '"default" | "outline" | "destructive" | "secondary" | undefined' — the badge object is sealed even though its parent card is not.

So the boundary on these pages is exactly this: declared members with closed unions are genuinely checked; anything reaching an index signature, and everything inside items, is not. That is why the read-site grep above is not optional decoration — it is the only thing covering the second half.

  1. The gate answers only "does this snippet compile against the published types". Schema-key validity under safeParse, whether a type literal names a registered component, and whether a shell example runs are three other questions with three other answers.

Type gap found and filed, not fixed here

packages/* source is out of this PR's surface, so it was filed unassigned after a targeted dedupe search (which returned the same-family cards #7311, #7313, #5903 and #6973, so it was live rather than silently empty; none is this defect):

Header prose corrected, and why it is a rewrite rather than a new number

Batch 6 recorded, without filing, that the ledger docblock's sentence "what remains here is 12 .mdx pages and 32 package READMEs" had drifted. Batch 7's dispatch authorised fixing it now that the ledger surface is being edited for its own sake.

It is corrected by removing the count, not by writing a fresh one. Both halves were already wrong (the README count has been 31 since #5259; the .mdx count moves with every batch), and any number written there would have been stale again within the hour — batch 6 landed mid-run and moved it. The sentence now points at the entries below it, which are re-derived every run and shrink-only, so the names in the list are the count. Nothing mechanical fails on a stale number in a comment, which is precisely why it should not carry one.

This edit is header prose, above the fence-scanning banner, so the strictness proof above is unaffected and still holds byte-for-byte.

Gate verdict lines

Everything below at 9d6e6f5a7, the final commit, with the closure built first under the shared verify lock (VERDICT command-exit 0) and dist/index.d.ts presence confirmed on disk in this worktree for all 25 filters before any gate result was trusted — turbo replayed cached logs naming another worktree's path (objectui-issue-5174-b6) on the first build, so this was checked rather than assumed.

check:doc-snippets exit 0 Semantic phase: 365 of 365 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
check:doc-fences exit 0 every TypeScript block in 224 document(s) is fenced ts/tsx/typescript,
except 80 declared file(s) carrying 90 block(s) of #5867's population
check:doc-types exit 0 Every documented component type is registered.
docs:check-links exit 0 Links are valid across 17 scan roots.
check:control-bytes exit 0 scanned 6015 tracked text file(s); skipped 85 binary
check:readme-exports exit 0 386 self-imports judged (386 real, 0 wrong-path, 0 fabricated)
check-changeset-presence exit 0 No source or published contract of a released package changed in
this range, so no changeset is owed.
vitest (repo root) exit 0 Test Files 7 passed (7) · Tests 177 passed (177)

The seven vitest files are every file git grep -l check-doc-snippet-types -- '*.test.ts' '*.test.tsx' names — the two obvious ones plus five pin suites in examples/schema-catalog, plugin-gantt, react and types that an edit to this script could have hijacked. Run from the repo root with --maxWorkers=2, never through pnpm --filter.

check:doc-fences is unmoved by this PR: plugin-timeline.mdx and plugin-kanban.mdx each carry one entry in #5867's shrink-only ledger for their plaintext-fenced "TypeScript Support" blocks, and those blocks are untouched here — that axis belongs to #5867.

check:readme-exports first read NOT MEASURED, not red: it exited 1 with 45 findings, all 45 the string its type entry ./dist/index.d.ts is not on disk -- run pnpm build first for plugin-ai and plugin-gantt, and none naming a file in this diff. Building those two closures made it a real measurement, and it is genuinely green.

One process note worth recording: the probe harness's first disk-proof step was itself defective — it used grep -cF on multi-line strings, which counts matching lines rather than occurrences, and reported a false "no-op mutation". It failed closed: the harness aborted and the trap restored before any gate ran, so no reading was taken from it. The proof step was corrected to count the full multi-line strings, and every probe above was then run and recorded once.

Declared narrowing

Repo-wide pnpm lint was narrowed to the four changed files, and the narrowing is a measurement rather than a skip:

  1. Population read from ESLint's own config, not from a guess: npx eslint --no-inline-config --format json on the four files reports the three .mdx pages as File ignored because no matching configuration was supplied — they are not in ESLint's population at all.
  2. File count read from the JSON output: 4 files judged, 3 ignored by config, 1 actually linted (scripts/check-doc-snippet-types.mjs), 0 errors, 0 warnings.
  3. Config invariance for untouched files: eslint.config.js sets no project, projectService or tsconfigRootDir, so type-aware linting is not enabled and nothing in this diff can move the verdict on a file it does not touch.

A control-byte self-scan (grep -naP over the C0 range plus DEL) across the four changed files returns no hits, beside the repo-wide gate above.


🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

…pet ledger
Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by
fence density -- plugin-timeline (9 blocks), plugin-kanban (8), runner (8) --
and delivers all three whole: 25 blocks, 20 compile against the built dist,
5 carry a FRAGMENT_MARKER with a measured reason.
The ledger hunk is removals only and nothing about the gate's strictness moved.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
… its length
The sentence carried a literal "12 .mdx pages and 32 package READMEs". Both
halves had drifted -- the README count has been 31 since objectui#5259, and the
.mdx count moves with every batch of objectui#5174's burn-down -- and nothing
fails on a stale number written there. The entries below are re-derived every
run and shrink-only, so the names in the list are the count.
Header prose only: the gate's strictness region, from the fence-scanning banner
to EOF, is byte-identical (sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
…-ledger-batch7
# Conflicts:
#	scripts/check-doc-snippet-types.mjs
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

@yinlianghui@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(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7) - #7325

Merged
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7
Sep 2, 2026
Merged

docs(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7)#7325
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Part of #5174

Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by fence density and delivers all three whole: 25 blocks, 20 compile against the built dist, 5 carry a FRAGMENT_MARKER with a written, measured reason. 26 diagnostics cleared. Nothing about the gate's strictness moved.

Batch 6 (#7314) landed while this was in flight; origin/main was merged (never rebased). The two ledger hunks conflicted textually because they are adjacent lines in one sorted object, and the conflict was resolved as the union of both removal sets — everything either side took out stays out. create-plugin.mdx, the only entry inside the conflict region that neither batch claimed, is the only one that survives it.

Pages taken, and how each block reached zero

All counts produced with the gate's own scanFences, not a hand grep.

pageblockscompiledeclareddiagnostics clearedwhat was actually wrong
content/docs/plugins/plugin-timeline.mdx9901 (1 syntax, 0 semantic)No fabricated key — every key the page documents has read sites. The page's real problem was that it verified nothing: eight blocks were unannotated literals, and the two "Date Formatting" / "Time Scales" fences held bare { dateFormat: 'short' } objects that TypeScript parses as block statements containing labelled expressions{ dateFormatt: 'short' } was equally green. Five schema literals now carry the TimelineSchema annotation the page's own prose already claims ("Every key above is declared on the exported TimelineSchema, so … a wrong value is a type error"), and the two value-list fences became indexed-access declarations. items: [...] in "Custom Colors" was the one parse failure.
content/docs/plugins/plugin-kanban.mdx88014 (6 syntax, 8 semantic)The eight semantic diagnostics were TS7006 on onCardMove's four parameters, twice — the callback was untyped because the literal was unannotated, while KanbanSchema declares it as taking four typed parameters. Five literals now carry KanbanSchema / KanbanCard / KanbanColumn. Three parse failures: a bare object-kanban literal, cards: [...], and an "Event Handling" block with columns: [...], a top-level await in a void-returning callback, and three ambient names — rewritten as a real hook. One type gap found and filed (#7322), not fixed here.
content/docs/utilities/runner.mdx83511 (5 syntax, 6 semantic)No fabricated API. Five blocks genuinely cannot compile here and are declared with measured reasons: four name @object-ui/plugin-yourplugin / -myplugin / ./components/MyComponent — the package or file the reader is being walked through creating — and one is a single property out of resolve.alias. Two real repairs: the Error Boundaries example imported SchemaErrorBoundary but rendered SchemaRenderer with neither the component nor schema in scope, so a reader copying it got two TS2304s; and defineConfig was called with no import. Two fences holding JSX were labelled typescript and are now tsx — a mislabel, corrected rather than declared. One fence held two files (a TS side-effect import and a JSON page document); the JSON half is a mislabel and now has its own json fence.

Ledger 37 → 34 (3 .mdx + 31 README). Covered documents 187 → 190 — strictly grows. Covered blocks 481 → 506; compiled 345 → 365; declared fragments 136 → 141. The per-page splits (9+8+3 = 20 compile, 0+0+5 = 5 declared) reconcile exactly with those deltas.

Invariants, each measured rather than asserted

  • The ledger hunk is removals only — 3 entry keys deleted, 0 added.
  • Gate strictness byte-identical, from the fence-scanning banner to EOF, merged base ad3d4029a vs this branch:
    sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3 — the same value on both sides, and the same value batches 5 and 6 recorded.
  • Covered count strictly grows — 187 → 190, ungated 37 → 34.
  • Every page taken is off the ledger and every block resolves the two honest ways — compile, or a FRAGMENT_MARKER whose reason names the measured diagnostics. No third route, no UNGATED_DOCS addition, no fence re-labelled to duck the gate. The two fence-language corrections go the other way: both blocks stay in the collected ts/tsx population, and the JSON document that leaves it was never TypeScript.

Before / after, both at a fully built tree, exit codes captured by redirect-then-$? and never through a pipe:

BEFORE (merged base ad3d4029a content for the four files, swapped in under an
EXIT/INT/TERM trap; restore proved by an empty `git diff HEAD`)
Scanned 224 document(s): 187 covered (86 hold a ts/tsx block), 37 ungated
Covered blocks: 481 — 345 to compile, 136 declared fragment(s)
Semantic phase: 345 of 345 block(s) judged, 0 failed exit 0
AFTER (9d6e6f5a7)
Scanned 224 document(s): 190 covered (89 hold a ts/tsx block), 34 ungated
Covered blocks: 506 — 365 to compile, 141 declared fragment(s)
Syntax phase: every block parsed, so every one of them reached the semantic phase.
Semantic phase: 365 of 365 block(s) judged, 0 failed exit 0

The branch point d717e8bc3 was measured the same way before the merge (184 covered / 40 ungated / 440 blocks, exit 0), which is batch 6's own BEFORE — the two agree.

The lid check, per page — including the negative results

Resolving an unresolved name can uncover diagnostics the first error was hiding. Run on all three, each planted and restored from HEAD under a trap:

  • plugin-kanban.mdx — POSITIVE, and it is the finding. The row-cap example is an object-kanban node. Annotated with ObjectKanbanSchema: TS2741: Property 'groupField' is missing … but required. Annotated with KanbanSchema, which is what ObjectKanbanComponentProps.schema actually declares: TS2322: Type '"object-kanban"' is not assignable to type '"kanban"'. Neither declared type accepts the shape the renderer reads. Note what the first diagnostic does not say — groupBy and limit raised nothing at all, because BaseSchema's index signature absorbed them. Filed as finding(types,plugin-kanban): ObjectKanbanSchema requires groupField (zero read sites) and declares neither groupBy nor limit — no working object-kanban node is assignable to any declared type #7322; the block ships as a plain const with no annotation.
  • plugin-timeline.mdx — NEGATIVE. The single parse failure (items: [...]) had kept that block out of the semantic phase entirely. With it resolved and five literals newly annotated, the re-run exposed nothing underneath. Reported as a negative result.
  • runner.mdx — NEGATIVE, twice. The declared fragments hide their contents from the gate, so both were probed with the fictional specifier shimmed to a real one: "Plugin Not Loading" with @object-ui/plugin-yourplugin swapped for @object-ui/plugin-markdown, and the ComponentRegistry.register block with ./components/MyComponent replaced by a local declaration. Both came back clean — the declared blocks are correct documentation the gate cannot reach, not defects in hiding.

The read-site grep — what the compiler structurally cannot see

BaseSchema carries [key: string]: any, and KanbanCard carries one of its own, so a wrong key on either is invisible to this gate. Every schema literal touched here was therefore also grepped for read sites:

  • Timelinevariant, items, dateFormat, scale, rowLabel, minDate, maxDate, className all read in plugin-timeline/src/renderer.tsx; the item keys time, title, description, variant, icon, content, className, startDate, endDate and the gantt row's label likewise. Nothing fabricated.
  • Kanban — card id / title / description / badges, badge label / variant, column id / title / cards / limit / className all read in KanbanImpl.tsx and KanbanEnhanced.tsx. Nothing fabricated.
  • object-kanban — this is where it paid. schema.groupBy is read at nine sites in ObjectKanban.tsx and schema.limit at two, while groupField — the key ObjectKanbanSchema marks required — has zero read sites in packages/plugin-kanban. The package's own test suite writes { type: 'object-kanban', objectName, groupBy, limit }, the documented shape. The page is right and the declaration is the stale half.

What the green does not mean

Stated because a green run must not be read as more than it is. Each limit below was measured on these pages by planting a probe, proving it reached the disk by counting the injected and removed text, running the gate, and restoring from HEAD under a trap with the restore proved by an empty git diff HEAD and a blob hash matching the HEAD blob.

Three probes stayed GREEN — these are real blind spots:

  1. A misspelled top-level key on an annotated schema literal is not caught. Probe: dateFormatt: 'long' added to the annotated TimelineSchema literal. Gate stayed green — BaseSchema's index signature absorbs it.
  2. A misspelled key inside items is not caught either. Probe: titlee on a timeline item. Gate stayed green — TimelineSchema.items is declared any[], deliberately, because the two element shapes are discriminated by variant and read dynamically. The item vocabulary this page documents in full is verified by read-site grep and by nothing else.
  3. A misspelled key on a KanbanCard is not caught. Probe: descriptionn on the annotated card literal. Gate stayed green — KanbanCard carries its own [key: string]: any, for the runtime-synthesized card keys.

Two probes went RED — so the annotations are live, not no-ops:

  1. variant: 'diagonal' on the annotated TimelineSchema literal produced TS2322: Type '"diagonal"' is not assignable to type '"horizontal" | "vertical" | "gantt" | undefined'.
  2. variant: 'critical' on a KanbanCard badge produced TS2322: Type '"critical"' is not assignable to type '"default" | "outline" | "destructive" | "secondary" | undefined' — the badge object is sealed even though its parent card is not.

So the boundary on these pages is exactly this: declared members with closed unions are genuinely checked; anything reaching an index signature, and everything inside items, is not. That is why the read-site grep above is not optional decoration — it is the only thing covering the second half.

  1. The gate answers only "does this snippet compile against the published types". Schema-key validity under safeParse, whether a type literal names a registered component, and whether a shell example runs are three other questions with three other answers.

Type gap found and filed, not fixed here

packages/* source is out of this PR's surface, so it was filed unassigned after a targeted dedupe search (which returned the same-family cards #7311, #7313, #5903 and #6973, so it was live rather than silently empty; none is this defect):

Header prose corrected, and why it is a rewrite rather than a new number

Batch 6 recorded, without filing, that the ledger docblock's sentence "what remains here is 12 .mdx pages and 32 package READMEs" had drifted. Batch 7's dispatch authorised fixing it now that the ledger surface is being edited for its own sake.

It is corrected by removing the count, not by writing a fresh one. Both halves were already wrong (the README count has been 31 since #5259; the .mdx count moves with every batch), and any number written there would have been stale again within the hour — batch 6 landed mid-run and moved it. The sentence now points at the entries below it, which are re-derived every run and shrink-only, so the names in the list are the count. Nothing mechanical fails on a stale number in a comment, which is precisely why it should not carry one.

This edit is header prose, above the fence-scanning banner, so the strictness proof above is unaffected and still holds byte-for-byte.

Gate verdict lines

Everything below at 9d6e6f5a7, the final commit, with the closure built first under the shared verify lock (VERDICT command-exit 0) and dist/index.d.ts presence confirmed on disk in this worktree for all 25 filters before any gate result was trusted — turbo replayed cached logs naming another worktree's path (objectui-issue-5174-b6) on the first build, so this was checked rather than assumed.

check:doc-snippets exit 0 Semantic phase: 365 of 365 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
check:doc-fences exit 0 every TypeScript block in 224 document(s) is fenced ts/tsx/typescript,
except 80 declared file(s) carrying 90 block(s) of #5867's population
check:doc-types exit 0 Every documented component type is registered.
docs:check-links exit 0 Links are valid across 17 scan roots.
check:control-bytes exit 0 scanned 6015 tracked text file(s); skipped 85 binary
check:readme-exports exit 0 386 self-imports judged (386 real, 0 wrong-path, 0 fabricated)
check-changeset-presence exit 0 No source or published contract of a released package changed in
this range, so no changeset is owed.
vitest (repo root) exit 0 Test Files 7 passed (7) · Tests 177 passed (177)

The seven vitest files are every file git grep -l check-doc-snippet-types -- '*.test.ts' '*.test.tsx' names — the two obvious ones plus five pin suites in examples/schema-catalog, plugin-gantt, react and types that an edit to this script could have hijacked. Run from the repo root with --maxWorkers=2, never through pnpm --filter.

check:doc-fences is unmoved by this PR: plugin-timeline.mdx and plugin-kanban.mdx each carry one entry in #5867's shrink-only ledger for their plaintext-fenced "TypeScript Support" blocks, and those blocks are untouched here — that axis belongs to #5867.

check:readme-exports first read NOT MEASURED, not red: it exited 1 with 45 findings, all 45 the string its type entry ./dist/index.d.ts is not on disk -- run pnpm build first for plugin-ai and plugin-gantt, and none naming a file in this diff. Building those two closures made it a real measurement, and it is genuinely green.

One process note worth recording: the probe harness's first disk-proof step was itself defective — it used grep -cF on multi-line strings, which counts matching lines rather than occurrences, and reported a false "no-op mutation". It failed closed: the harness aborted and the trap restored before any gate ran, so no reading was taken from it. The proof step was corrected to count the full multi-line strings, and every probe above was then run and recorded once.

Declared narrowing

Repo-wide pnpm lint was narrowed to the four changed files, and the narrowing is a measurement rather than a skip:

  1. Population read from ESLint's own config, not from a guess: npx eslint --no-inline-config --format json on the four files reports the three .mdx pages as File ignored because no matching configuration was supplied — they are not in ESLint's population at all.
  2. File count read from the JSON output: 4 files judged, 3 ignored by config, 1 actually linted (scripts/check-doc-snippet-types.mjs), 0 errors, 0 warnings.
  3. Config invariance for untouched files: eslint.config.js sets no project, projectService or tsconfigRootDir, so type-aware linting is not enabled and nothing in this diff can move the verdict on a file it does not touch.

A control-byte self-scan (grep -naP over the C0 range plus DEL) across the four changed files returns no hits, beside the repo-wide gate above.


🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

…pet ledger
Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by
fence density -- plugin-timeline (9 blocks), plugin-kanban (8), runner (8) --
and delivers all three whole: 25 blocks, 20 compile against the built dist,
5 carry a FRAGMENT_MARKER with a measured reason.
The ledger hunk is removals only and nothing about the gate's strictness moved.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
… its length
The sentence carried a literal "12 .mdx pages and 32 package READMEs". Both
halves had drifted -- the README count has been 31 since objectui#5259, and the
.mdx count moves with every batch of objectui#5174's burn-down -- and nothing
fails on a stale number written there. The entries below are re-derived every
run and shrink-only, so the names in the list are the count.
Header prose only: the gate's strictness region, from the fence-scanning banner
to EOF, is byte-identical (sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
…-ledger-batch7
# Conflicts:
#	scripts/check-doc-snippet-types.mjs
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

@yinlianghui@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(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7) - #7325

Merged
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7
Sep 2, 2026
Merged

docs(plugins,utilities,tooling): clear three more .mdx pages off the doc-snippet ledger (#5174 batch 7)#7325
yinlianghui merged 3 commits into
mainfrom
claude/issue-5174-mdx-ledger-batch7

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Part of #5174

Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by fence density and delivers all three whole: 25 blocks, 20 compile against the built dist, 5 carry a FRAGMENT_MARKER with a written, measured reason. 26 diagnostics cleared. Nothing about the gate's strictness moved.

Batch 6 (#7314) landed while this was in flight; origin/main was merged (never rebased). The two ledger hunks conflicted textually because they are adjacent lines in one sorted object, and the conflict was resolved as the union of both removal sets — everything either side took out stays out. create-plugin.mdx, the only entry inside the conflict region that neither batch claimed, is the only one that survives it.

Pages taken, and how each block reached zero

All counts produced with the gate's own scanFences, not a hand grep.

pageblockscompiledeclareddiagnostics clearedwhat was actually wrong
content/docs/plugins/plugin-timeline.mdx9901 (1 syntax, 0 semantic)No fabricated key — every key the page documents has read sites. The page's real problem was that it verified nothing: eight blocks were unannotated literals, and the two "Date Formatting" / "Time Scales" fences held bare { dateFormat: 'short' } objects that TypeScript parses as block statements containing labelled expressions{ dateFormatt: 'short' } was equally green. Five schema literals now carry the TimelineSchema annotation the page's own prose already claims ("Every key above is declared on the exported TimelineSchema, so … a wrong value is a type error"), and the two value-list fences became indexed-access declarations. items: [...] in "Custom Colors" was the one parse failure.
content/docs/plugins/plugin-kanban.mdx88014 (6 syntax, 8 semantic)The eight semantic diagnostics were TS7006 on onCardMove's four parameters, twice — the callback was untyped because the literal was unannotated, while KanbanSchema declares it as taking four typed parameters. Five literals now carry KanbanSchema / KanbanCard / KanbanColumn. Three parse failures: a bare object-kanban literal, cards: [...], and an "Event Handling" block with columns: [...], a top-level await in a void-returning callback, and three ambient names — rewritten as a real hook. One type gap found and filed (#7322), not fixed here.
content/docs/utilities/runner.mdx83511 (5 syntax, 6 semantic)No fabricated API. Five blocks genuinely cannot compile here and are declared with measured reasons: four name @object-ui/plugin-yourplugin / -myplugin / ./components/MyComponent — the package or file the reader is being walked through creating — and one is a single property out of resolve.alias. Two real repairs: the Error Boundaries example imported SchemaErrorBoundary but rendered SchemaRenderer with neither the component nor schema in scope, so a reader copying it got two TS2304s; and defineConfig was called with no import. Two fences holding JSX were labelled typescript and are now tsx — a mislabel, corrected rather than declared. One fence held two files (a TS side-effect import and a JSON page document); the JSON half is a mislabel and now has its own json fence.

Ledger 37 → 34 (3 .mdx + 31 README). Covered documents 187 → 190 — strictly grows. Covered blocks 481 → 506; compiled 345 → 365; declared fragments 136 → 141. The per-page splits (9+8+3 = 20 compile, 0+0+5 = 5 declared) reconcile exactly with those deltas.

Invariants, each measured rather than asserted

  • The ledger hunk is removals only — 3 entry keys deleted, 0 added.
  • Gate strictness byte-identical, from the fence-scanning banner to EOF, merged base ad3d4029a vs this branch:
    sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3 — the same value on both sides, and the same value batches 5 and 6 recorded.
  • Covered count strictly grows — 187 → 190, ungated 37 → 34.
  • Every page taken is off the ledger and every block resolves the two honest ways — compile, or a FRAGMENT_MARKER whose reason names the measured diagnostics. No third route, no UNGATED_DOCS addition, no fence re-labelled to duck the gate. The two fence-language corrections go the other way: both blocks stay in the collected ts/tsx population, and the JSON document that leaves it was never TypeScript.

Before / after, both at a fully built tree, exit codes captured by redirect-then-$? and never through a pipe:

BEFORE (merged base ad3d4029a content for the four files, swapped in under an
EXIT/INT/TERM trap; restore proved by an empty `git diff HEAD`)
Scanned 224 document(s): 187 covered (86 hold a ts/tsx block), 37 ungated
Covered blocks: 481 — 345 to compile, 136 declared fragment(s)
Semantic phase: 345 of 345 block(s) judged, 0 failed exit 0
AFTER (9d6e6f5a7)
Scanned 224 document(s): 190 covered (89 hold a ts/tsx block), 34 ungated
Covered blocks: 506 — 365 to compile, 141 declared fragment(s)
Syntax phase: every block parsed, so every one of them reached the semantic phase.
Semantic phase: 365 of 365 block(s) judged, 0 failed exit 0

The branch point d717e8bc3 was measured the same way before the merge (184 covered / 40 ungated / 440 blocks, exit 0), which is batch 6's own BEFORE — the two agree.

The lid check, per page — including the negative results

Resolving an unresolved name can uncover diagnostics the first error was hiding. Run on all three, each planted and restored from HEAD under a trap:

  • plugin-kanban.mdx — POSITIVE, and it is the finding. The row-cap example is an object-kanban node. Annotated with ObjectKanbanSchema: TS2741: Property 'groupField' is missing … but required. Annotated with KanbanSchema, which is what ObjectKanbanComponentProps.schema actually declares: TS2322: Type '"object-kanban"' is not assignable to type '"kanban"'. Neither declared type accepts the shape the renderer reads. Note what the first diagnostic does not say — groupBy and limit raised nothing at all, because BaseSchema's index signature absorbed them. Filed as finding(types,plugin-kanban): ObjectKanbanSchema requires groupField (zero read sites) and declares neither groupBy nor limit — no working object-kanban node is assignable to any declared type #7322; the block ships as a plain const with no annotation.
  • plugin-timeline.mdx — NEGATIVE. The single parse failure (items: [...]) had kept that block out of the semantic phase entirely. With it resolved and five literals newly annotated, the re-run exposed nothing underneath. Reported as a negative result.
  • runner.mdx — NEGATIVE, twice. The declared fragments hide their contents from the gate, so both were probed with the fictional specifier shimmed to a real one: "Plugin Not Loading" with @object-ui/plugin-yourplugin swapped for @object-ui/plugin-markdown, and the ComponentRegistry.register block with ./components/MyComponent replaced by a local declaration. Both came back clean — the declared blocks are correct documentation the gate cannot reach, not defects in hiding.

The read-site grep — what the compiler structurally cannot see

BaseSchema carries [key: string]: any, and KanbanCard carries one of its own, so a wrong key on either is invisible to this gate. Every schema literal touched here was therefore also grepped for read sites:

  • Timelinevariant, items, dateFormat, scale, rowLabel, minDate, maxDate, className all read in plugin-timeline/src/renderer.tsx; the item keys time, title, description, variant, icon, content, className, startDate, endDate and the gantt row's label likewise. Nothing fabricated.
  • Kanban — card id / title / description / badges, badge label / variant, column id / title / cards / limit / className all read in KanbanImpl.tsx and KanbanEnhanced.tsx. Nothing fabricated.
  • object-kanban — this is where it paid. schema.groupBy is read at nine sites in ObjectKanban.tsx and schema.limit at two, while groupField — the key ObjectKanbanSchema marks required — has zero read sites in packages/plugin-kanban. The package's own test suite writes { type: 'object-kanban', objectName, groupBy, limit }, the documented shape. The page is right and the declaration is the stale half.

What the green does not mean

Stated because a green run must not be read as more than it is. Each limit below was measured on these pages by planting a probe, proving it reached the disk by counting the injected and removed text, running the gate, and restoring from HEAD under a trap with the restore proved by an empty git diff HEAD and a blob hash matching the HEAD blob.

Three probes stayed GREEN — these are real blind spots:

  1. A misspelled top-level key on an annotated schema literal is not caught. Probe: dateFormatt: 'long' added to the annotated TimelineSchema literal. Gate stayed green — BaseSchema's index signature absorbs it.
  2. A misspelled key inside items is not caught either. Probe: titlee on a timeline item. Gate stayed green — TimelineSchema.items is declared any[], deliberately, because the two element shapes are discriminated by variant and read dynamically. The item vocabulary this page documents in full is verified by read-site grep and by nothing else.
  3. A misspelled key on a KanbanCard is not caught. Probe: descriptionn on the annotated card literal. Gate stayed green — KanbanCard carries its own [key: string]: any, for the runtime-synthesized card keys.

Two probes went RED — so the annotations are live, not no-ops:

  1. variant: 'diagonal' on the annotated TimelineSchema literal produced TS2322: Type '"diagonal"' is not assignable to type '"horizontal" | "vertical" | "gantt" | undefined'.
  2. variant: 'critical' on a KanbanCard badge produced TS2322: Type '"critical"' is not assignable to type '"default" | "outline" | "destructive" | "secondary" | undefined' — the badge object is sealed even though its parent card is not.

So the boundary on these pages is exactly this: declared members with closed unions are genuinely checked; anything reaching an index signature, and everything inside items, is not. That is why the read-site grep above is not optional decoration — it is the only thing covering the second half.

  1. The gate answers only "does this snippet compile against the published types". Schema-key validity under safeParse, whether a type literal names a registered component, and whether a shell example runs are three other questions with three other answers.

Type gap found and filed, not fixed here

packages/* source is out of this PR's surface, so it was filed unassigned after a targeted dedupe search (which returned the same-family cards #7311, #7313, #5903 and #6973, so it was live rather than silently empty; none is this defect):

Header prose corrected, and why it is a rewrite rather than a new number

Batch 6 recorded, without filing, that the ledger docblock's sentence "what remains here is 12 .mdx pages and 32 package READMEs" had drifted. Batch 7's dispatch authorised fixing it now that the ledger surface is being edited for its own sake.

It is corrected by removing the count, not by writing a fresh one. Both halves were already wrong (the README count has been 31 since #5259; the .mdx count moves with every batch), and any number written there would have been stale again within the hour — batch 6 landed mid-run and moved it. The sentence now points at the entries below it, which are re-derived every run and shrink-only, so the names in the list are the count. Nothing mechanical fails on a stale number in a comment, which is precisely why it should not carry one.

This edit is header prose, above the fence-scanning banner, so the strictness proof above is unaffected and still holds byte-for-byte.

Gate verdict lines

Everything below at 9d6e6f5a7, the final commit, with the closure built first under the shared verify lock (VERDICT command-exit 0) and dist/index.d.ts presence confirmed on disk in this worktree for all 25 filters before any gate result was trusted — turbo replayed cached logs naming another worktree's path (objectui-issue-5174-b6) on the first build, so this was checked rather than assumed.

check:doc-snippets exit 0 Semantic phase: 365 of 365 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
check:doc-fences exit 0 every TypeScript block in 224 document(s) is fenced ts/tsx/typescript,
except 80 declared file(s) carrying 90 block(s) of #5867's population
check:doc-types exit 0 Every documented component type is registered.
docs:check-links exit 0 Links are valid across 17 scan roots.
check:control-bytes exit 0 scanned 6015 tracked text file(s); skipped 85 binary
check:readme-exports exit 0 386 self-imports judged (386 real, 0 wrong-path, 0 fabricated)
check-changeset-presence exit 0 No source or published contract of a released package changed in
this range, so no changeset is owed.
vitest (repo root) exit 0 Test Files 7 passed (7) · Tests 177 passed (177)

The seven vitest files are every file git grep -l check-doc-snippet-types -- '*.test.ts' '*.test.tsx' names — the two obvious ones plus five pin suites in examples/schema-catalog, plugin-gantt, react and types that an edit to this script could have hijacked. Run from the repo root with --maxWorkers=2, never through pnpm --filter.

check:doc-fences is unmoved by this PR: plugin-timeline.mdx and plugin-kanban.mdx each carry one entry in #5867's shrink-only ledger for their plaintext-fenced "TypeScript Support" blocks, and those blocks are untouched here — that axis belongs to #5867.

check:readme-exports first read NOT MEASURED, not red: it exited 1 with 45 findings, all 45 the string its type entry ./dist/index.d.ts is not on disk -- run pnpm build first for plugin-ai and plugin-gantt, and none naming a file in this diff. Building those two closures made it a real measurement, and it is genuinely green.

One process note worth recording: the probe harness's first disk-proof step was itself defective — it used grep -cF on multi-line strings, which counts matching lines rather than occurrences, and reported a false "no-op mutation". It failed closed: the harness aborted and the trap restored before any gate ran, so no reading was taken from it. The proof step was corrected to count the full multi-line strings, and every probe above was then run and recorded once.

Declared narrowing

Repo-wide pnpm lint was narrowed to the four changed files, and the narrowing is a measurement rather than a skip:

  1. Population read from ESLint's own config, not from a guess: npx eslint --no-inline-config --format json on the four files reports the three .mdx pages as File ignored because no matching configuration was supplied — they are not in ESLint's population at all.
  2. File count read from the JSON output: 4 files judged, 3 ignored by config, 1 actually linted (scripts/check-doc-snippet-types.mjs), 0 errors, 0 warnings.
  3. Config invariance for untouched files: eslint.config.js sets no project, projectService or tsconfigRootDir, so type-aware linting is not enabled and nothing in this diff can move the verdict on a file it does not touch.

A control-byte self-scan (grep -naP over the C0 range plus DEL) across the four changed files returns no hits, beside the repo-wide gate above.


🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

…pet ledger
Batch 7 of the UNGATED_DOCS burn-down. Takes the next three .mdx entries by
fence density -- plugin-timeline (9 blocks), plugin-kanban (8), runner (8) --
and delivers all three whole: 25 blocks, 20 compile against the built dist,
5 carry a FRAGMENT_MARKER with a measured reason.
The ledger hunk is removals only and nothing about the gate's strictness moved.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
… its length
The sentence carried a literal "12 .mdx pages and 32 package READMEs". Both
halves had drifted -- the README count has been 31 since objectui#5259, and the
.mdx count moves with every batch of objectui#5174's burn-down -- and nothing
fails on a stale number written there. The entries below are re-derived every
run and shrink-only, so the names in the list are the count.
Header prose only: the gate's strictness region, from the fence-scanning banner
to EOF, is byte-identical (sha256 b87e347626a6cbab30b904eaf7d3eb72f6804122505bdf9ec8f2f2c8136e95a3).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
…-ledger-batch7
# Conflicts:
#	scripts/check-doc-snippet-types.mjs
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

@yinlianghui@claude