Skip to content

fix(scripts): doc-snippet gate collects .md under content/docs, and states its scan surface - #5341

Merged
os-support-ai merged 1 commit into
mainfrom
claude/issue-5174-doc-snippet-gate-collects-md
Aug 19, 2026
Merged

fix(scripts): doc-snippet gate collects .md under content/docs, and states its scan surface#5341
os-support-ai merged 1 commit into
mainfrom
claude/issue-5174-doc-snippet-gate-collects-md

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Part of #5174

scripts/check-doc-snippet-types.mjs states its coverage rule in its own docblock — a document is covered unless it is named in UNGATED_DOCS with a reason — and carries a fragment rule that exists so a snippet is never silently skipped. The collector then picked documents by extension, admitting 143 .mdx under content/docs and excluding 40 .md. None of the 40 was in the ledger, so they were neither covered nor declared ungated: invisible to the gate's own accounting, and unmentionable by the summary line it prints.

Both numbers were re-measured on today's main before planning: still 143 .mdx and 40 .md.

This lands the collector change, the docblock half, a complete and measured ledger for everything newly collected, and outright fixes for the eight guides that were cheap and genuinely broken. It is deliberately partial — Part of, not Fixes. What is left is named below.

Before / after

Both columns are real runs of the gate against the built dist, at the same tree state, with the origin/main script used verbatim for the "before" column.

beforeafterdelta
documents scanned182222+40
covered138158+20
covered docs holding a ts/tsx block1321+8
ungated ledger entries4464+20
blocks compiled6887+19
declared fragments510+5
syntax failures00
semantic failures00

The three constraints this change had to hold, each checked mechanically rather than asserted:

  1. The covered set strictly grows — 138 to 158.
  2. No previously covered document became ungated. The ledger key sets were diffed programmatically: REMOVED entries: [], and all 20 added keys are newly collected .md under content/docs. The 44 pre-existing entries are byte-identical — the only deletions in the script's diff are the five lines listed under "What changed" below, none of them inside the ledger. (Rebased on current main first, so PR5332's rewrite of the objectos-integration.mdx entry and PR5320's reason-string edit are both carried through untouched.)
  3. Nothing about the gate's strictness moved. No threshold, no diagnostic class, no FRAGMENT_MARKER semantics. The regex, MIN_REASON_LENGTH, TS_FENCE_LANGUAGES, the two-phase split and all three controls are unchanged.

Where the 40 newly collected documents went

outcomecount
hold no ts/tsx block at all — covered, nothing to judge12
fixed in this PR, now compiling8
ledgered with a measured reason20

Zero of the 40 passed as collected: every one of the 28 that holds a ts/tsx block failed on first collection. That is the finding, not an accident of this PR.

The eight fixed

Three are prose fragments, declared with the HTML-comment marker the script already spells out for .md (keyword doc-snippet: fragment, plus a written reason). Declaring is the gate's own designed mechanism, not a relaxation:

  • guide/quick-start.md — a vite.config.ts for the reader's project; @vitejs/plugin-react and @tailwindcss/vite are the reader's dependencies, not this repo's.
  • guide/react-pages.md — a bad/good contrast of two bare JSX opening tags; closing them would erase the difference the section is about.
  • guide/slotted-pages.md — a bare slots: metadata excerpt.

Five carried real defects — documented symbols that do not exist, or literals that do not type-check:

  • guide/schema-playground.mdregisterDefaultRenderers is not exported by @object-ui/components, and registerAllFields takes no arguments. Replaced with the repo's own one registration path: initializeComponents() plus the side-effect @object-ui/fields import, matching apps/site/app/components/ObjectUIProvider.tsx.
  • guide/fields.mdonChange is optional on CellRendererProps and was invoked unguarded; a KanbanCard prop was untyped; a field literal omitted the name that UserFieldMetadata requires.
  • guide/metadata-diagnostics.mdnew MetadataClient(...) was written with its only required argument elided, so the documented line does not compile; and an interface excerpt named MetadataDiagnostics without importing it.
  • guide/data-source.md — an interface excerpt named BatchTransactionOperation without importing it, and the injection example rendered a mySchema it never defined. One block (the OData query keys) genuinely continues the block above it and is declared as such.
  • guide/console-architecture.mduseActionRunner used with no import (it comes from @object-ui/react); the AppShell branding block is a bare opening tag and is declared.

The twenty ledgered

Every entry carries a measured diagnostic mix in the same shape the existing entries use — parse / undefined-name / unresolved-module counts, then the remaining codes as TSxxxxxN candidate real defects. No entry says "not triaged yet" and none is a bare filename.

Where the mix included a missing-export diagnostic, the entry names the symbol, because that is the reader-visible half (the objectui#5160 class). What that surfaced, aggregated:

missing symbolmoduleoccurrences
getComponentRegistry@object-ui/react6
registerDefaultRenderers@object-ui/components5
AppSchema / ThemeSchema / ReportSchema@object-ui/types and /zod9
BaseSchema@object-ui/core3
PageSchema / FormSchema@object-ui/core, @object-ui/types3
registerAllComponents@object-ui/components1
getExpressionEvaluator@object-ui/core1
ObjectStackProvider@object-ui/data-objectstack1
ObjectSchema / Field@object-ui/types2
InputRenderer@object-ui/components1
componentSchema@object-ui/types/zod1
DashboardSchema@object-ui/types1

These are getting-started guides. A reader copying from them today gets code that does not compile. None of it is caused by this PR — all of it was equally broken before and simply unnamed, which is the whole point of the card. Working those entries down is per-page documentation work, not a mechanical edit, and it is why #5174 stays open.

CI cost — this needs a decision under the objectui#4846 ruling

--build-filter output did change, and the closure grows more than the filter list suggests:

filtersturbo build tasks (filters + their dependencies)
before1121
after1533
full workspace, for scale46

The four packages added to the filter are @object-ui/app-shell, @object-ui/components, @object-ui/fields, @object-ui/plugin-detail. Almost all of the growth is one of them: @object-ui/app-shell is pulled in by exactly one document, guide/metadata-diagnostics.md, and it drags the plugin fan-out with it. Measured: dropping app-shell from the filter takes the closure from 33 back to 22 tasks — one more than before.

So the trade is explicit and reversible in one line: covering guide/metadata-diagnostics.md costs ~11 extra build tasks per run of this gate. It is fixed and passing here because it was genuinely broken and cheap to fix; if the reviewer would rather not pay that, moving it to the ledger with its measured reason is the one-line alternative and the other seven fixes are unaffected. Flagging rather than deciding, since #4846 makes this a per-PR call.

Verification

Run from the repo root, at 76a09baef, against a build of the gate's own filter closure:

  • node scripts/check-doc-snippet-types.mjs — green. Controls all pass: resolution lands on packages/types/dist/index.d.ts, the planted sentinel produces its TS2305, the positive control is clean, no package src/ leaked into the program.
    Scanned 222 document(s): 158 covered (21 of them hold a ts/tsx block), 64 ungated
    Covered blocks: 97 — 87 to compile, 10 declared fragment(s).
    Syntax phase: every block parsed, so every one of them reached the semantic phase.
    Semantic phase: 87 of 87 block(s) judged, 0 failed.
    
  • pnpm exec vitest run scripts/__tests__/check-doc-snippet-types.test.ts20 passed (20). Running the script is not running its test; both were run.
  • node scripts/check-doc-component-types.mjs, node scripts/check-doc-links.mjs, node scripts/check-control-bytes.mjs, pnpm run type-check:scripts, eslint on both touched script files — all green.
  • node scripts/check-changeset-presence.mjs"No source of a released package changed in this range, so no changeset is owed." No changeset added.
  • Prettier reports style issues on all nine touched files, and reports the same issues on their origin/main versions; no workflow runs prettier --check. Left alone rather than mixed in as reformatting churn.

The markers render as nothing

The three .md fragment markers are HTML comments, which the script documents as the .md spelling. Verified rather than assumed: fumadocs-mdx 15.2.3 selects the compiler format with filePath.endsWith(".mdx") ? "mdx" : "md", and every edited .md file was compiled through @mdx-js/mdx in format: 'md' — all compile, and the marker text does not appear in the output.

Reverse-verification of the collector change

Prediction, written before the run: restoring the .mdx-only filter drops the 20 newly ledgered .md documents out of the scan set, so analyze re-derives the ledger against a narrower set and emits a stale-ungated-entry finding for each; the test is green, and the ledger is exact fails with 20 such findings; the other 19 tests still pass, since 182 documents still clears the plausibility floor and 68 blocks still clears the "has snippets to judge" floor.

Observed: Tests 1 failed | 19 passed (20), the single failure being is green, and the ledger is exact, with exactly 20 stale-ungated-entry findings in the diff. Predicted and observed match, including the direction — this leg goes red by producing more findings, not fewer.

Build artifact between the edit and the thing under test: none, on either leg. The suite imports ../check-doc-snippet-types.mjs directly as source and the gate runs that file directly, so no dist sits between the mutation and the verdict and nothing needed rebuilding for either leg. (A build artifact does sit between the packages and the gate's verdict — that is the point of the gate — but it is not on the path from this edit to this test.) The restore leg was run and is green: Tests 20 passed (20), with git status clean against the commit, so the tree is byte-identical to what is pushed.

What changed

  • scripts/check-doc-snippet-types.mjs
    • MDX_ROOT becomes DOCS_ROOT, and a new DOC_EXTENSIONS names both page extensions with the reason they are both collected. The walk tests that list instead of one hard-coded suffix. Five lines removed in total, all of them these.
    • The docblock section that states the coverage rule now states the scan surface in the same breath, with the history of why.
    • 20 ledger entries added, sorted into place; a note above the ledger records that they are disclosed debt rather than new debt.
  • content/docs/guide/{quick-start,react-pages,slotted-pages,schema-playground,fields,metadata-diagnostics,data-source,console-architecture}.md — the eight fixes above.

No changes to packages/**.

Generated by Claude Code


Generated by Claude Code

…tates its scan surface
`check-doc-snippet-types.mjs` says a document is covered unless it is named in
`UNGATED_DOCS` with a reason, and carries a fragment rule so that no snippet is
ever silently skipped. The collector then picked documents by extension, so 143
`.mdx` under `content/docs` were admitted and 40 `.md` guides were excluded —
none of them in the ledger, so they were neither covered nor declared ungated.
The collector now reads both extensions, and the scan surface is stated in the
docblock beside the coverage rule rather than left to be read off `listDocuments`.
Eight of the newly collected guides are fixed outright; the remaining twenty get
a ledger entry carrying the same measured diagnostic mix the existing entries
use, several naming the missing export by hand.
Part of #5174
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE
@os-support-aiClaude

Copy link
Copy Markdown
CollaboratorAuthor

ACCEPT — PM review, round 17. Part of #5174, so the card stays open for the per-page half.

  • Path surface: 9 files — scripts/check-doc-snippet-types.mjs and 8 content/docs/guide/*.md. Zero governed-surface hits; content/docs/releases/ untouched. No changeset owed — check-changeset-presence.mjs was run and its verdict followed.
  • Gates: every gate job completed: success — Lint, Type Check, Test shards 1–4, Build & E2E, Build Docs, Doc Snippet Type Check, Doc Component Type Check, Changeset Declaration / Fixed Group, Control Byte Scan, Internal Docs Link Check, Skill Guide Path Check. No cancelled, no in_progress.

The four invariants, each measured rather than asserted

invariantmeasured
covered set strictly grows138 → 158
no previously-covered document becomes ungatedkey sets diffed programmatically — REMOVED entries: []
every new entry carries a measured diagnostic mix44 → 64, several naming the missing export by hand
strictness unmovedFRAGMENT_MARKER, MIN_REASON_LENGTH, TS_FENCE_LANGUAGES, the two-phase split, all three controls untouched

This is the one card this round where adding ledger entries is the correct outcome rather than a weakening — the ledger's rule is covered by default, declare exceptions with a reason, so a newly-collected page that cannot pass yet is owed an entry. The PR gets that distinction right in the ledger's own prose: the 20 new entries are disclosed debt, not new debt — "every one of them was equally unverified before, just unnamed."

The number that justifies the card

Of the 40 newly collected documents: 12 hold no ts/tsx block, 8 are fixed and now compiling, 20 are ledgered. Zero passed as collected — all 28 with a block failed on first sight. A third of the guide tree was unverified and the gate's own summary line could not say so.

The docblock is the better half of this PR

The dev's framing is sharper than the card's, and correct: the finding was never the missing extension — it was that a reader had to open listDocuments to learn that "covered by default" meant "covered if the filename ends in .mdx." The scan surface is now stated in the same breath as the coverage rule, with "anything added to the scan surface later belongs in that list, on the same day." That is what stops this recurring.

Verification

Reverse-verification predicted the collector restore would redden by producing MORE findings — 20 stale-ungated-entry findings, failing exactly is green, and the ledger is exact while the other 19 tests pass — and observed Tests 1 failed | 19 passed (20) with exactly 20 such findings. Direction predicted as well as count, which is the leg that is easy to get backwards. No build artifact on either leg: the suite imports the .mjs directly as source and the gate runs that file directly.

The three .md fragment markers were verified rather than assumed to survive: fumadocs-mdx 15.2.3 picks its compiler with filePath.endsWith('.mdx') ? 'mdx' : 'md', every edited .md was compiled through @mdx-js/mdx in md format, and the marker text does not reach the output.

The CI-cost flag stands, and is not blocking

--build-filter changed: filters 11 → 15, turbo tasks 21 → 33, attributed to exactly one document (guide/metadata-diagnostics.md pulling @object-ui/app-shell; without it, 22). That is a per-PR call under #4846 and it is the maintainer's — raised on #5174 rather than decided here, and not held against a green PR: the build is cached (FULL TURBO, 473 ms) so the marginal cost is cold-cache only, the page had two real defects now fixed and guarded, and reverting to the ledger is one line at any time.

Follow-up shaped and filed: #5343 carries the fabricated-export family (~14 symbols, one decision each), the per-page fragment work stays on #5174, and #5342 records the same extension-only collector in check-doc-component-types.mjs as a sub-issue of #5106.

Merging via the queue.


Generated by Claude Code

@os-support-ai
os-support-ai added this pull request to the merge queueAug 19, 2026
Merged via the queue into main with commit 383f8b4Aug 19, 2026
20 checks passed
@os-support-ai
os-support-ai deleted the claude/issue-5174-doc-snippet-gate-collects-md branch August 19, 2026 17:19
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-support-ai@claude