Skip to content

docs(guides,tooling): clear ten .md pages off the doc-snippet ledger (#5174 batch 1) - #5951

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-5174-md-ledger-triage-batch1
Aug 24, 2026
Merged

docs(guides,tooling): clear ten .md pages off the doc-snippet ledger (#5174 batch 1)#5951
os-warren merged 2 commits into
mainfrom
claude/issue-5174-md-ledger-triage-batch1

Conversation

@os-warren

@os-warrenos-warren commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Part of #5174

Batch 1 of the UNGATED_DOCS triage. Ten .md guide pages leave the ledger; nine stay, with their measured reasons untouched. No gate strictness moved, no entry was added, and no previously-covered document became ungated.

Two measurements that re-scope the card before anything else

1. The remaining .md guide population is 19, not 51. The dispatch and #5174's deferral note both read "51 .md guides" off the 63-entry ledger. That figure counts every entry whose filename ends in .md, which sweeps in 32 packages/*/README.md entries. Split by where they live:

ledger slice on origin/main @ baac3f4entries
content/docs/**/*.md — the guide tree, this card's surface19
packages/*/README.md.md, but not guides32
content/docs/**/*.mdx12
total63

So this batch of ten is over half the actual population, not a fifth of it. The 63 total was exactly right; only its .md split was not.

2. The "mechanically clearable now that #5343 landed" assumption is falsified — measured, not assumed. Compiling all 19 pages in one program against the built dist/ produces zeroTS2305 / TS2724 / TS2614 — not one fabricated-export diagnostic anywhere in the guide tree. #5343 (PR #5364) cleared that class completely, and the gate's own docblock already says so: "No entry on this list names a missing export any more." There is nothing left for an already-made symbol decision to be applied to. Every remaining diagnostic is per-page fragment / self-containment judgement, plus a handful of genuinely wrong documented APIs. Per the dispatch's own rule for that outcome the batch stays small; it is ten rather than eight only because the two extra pages measured cheap.

⛔ No fabricated symbol was re-decided per page. One decision genuinely outside #5343's table came up and is called out below.

Selection rule: cheapest-first by measured total diagnostic count

Every one of the 19 pages was compiled first, in a single program that reuses the gate's own analyze() / compileSnippets(), and ranked by total diagnostics. The ten lowest were taken. That rule is defensible on its own terms — it maximises entries removed per unit of review — and it is reproducible: rerun the harness and you get the same ten.

Calibration for that harness: it reproduces the ledger's recorded mixes exactly for 17 of the 19 pages, which is what makes the before column trustworthy. (The two that differ are noted at the bottom.)

Cleared — ten entries deleted, 90 diagnostics gone

pagewasblocks now compilingblocks now declared fragments
content/docs/api/schema-reference.md120
content/docs/plugins/index.md312
content/docs/guide/architecture-overview.md802
content/docs/guide/deployment.md812
content/docs/guide/schema-overview.md943
content/docs/guide/notifications.md1014
content/docs/guide/public-forms.md1034
content/docs/guide/troubleshooting.md1372
content/docs/guide/expressions.md1434
content/docs/guide/user-state-persistence.md1441
total902624

Each page reached zero the two honest ways, never a third: a block that should compile was made self-contained against the built dist/, and a block that genuinely cannot compile got a FRAGMENT_MARKER declaration carrying a written reason. ⛔ Nothing was cleared by loosening what "covered" means.

Left — nine entries, reasons untouched

pagemeasured total
content/docs/guide/plugins.md20
content/docs/guide/building-crud-app.md21
content/docs/rfcs/0001-clipboard-paste.md23
content/docs/guide/architecture.md25
content/docs/guide/plugin-development.md25
content/docs/guide/schema-rendering.md27
content/docs/guide/theming.md29
content/docs/guide/layout.md45
content/docs/guide/component-registry.md59

274 diagnostics across nine pages — two batches' worth, and layout + component-registry are 104 between them.

The four invariants, measured

Computed by diffing the ledger key sets programmatically — origin/main's copy of the script and this branch's copy, both run over the same working tree, so the covered-set delta is attributable to the ledger alone.

invariantmeasured
the covered set strictly grows159 → 169 documents
no previously-covered document becomes ungatedADDED ledger entries: [] · previously-covered docs now UNGATED: []
every entry left keeps a measured diagnostic mixall nine untouched — this diff only deletes entries; not one surviving reason string was re-worded
nothing about the gate's strictness movesproven byte-for-byte: everything from the // ── Fence scanning banner to EOF is identical to origin/mainscanFences, listDocuments, derivePackageTypePaths, analyze, compileSnippets, the reporting and main. FRAGMENT_MARKER + MIN_REASON_LENGTH diffed separately and also byte-identical. DOC_EXTENSIONS, TS_FENCE_LANGUAGES, COMPILER_OPTIONS and all three controls unchanged.

REMOVED entries: (the ten deleted, verbatim from the diff):

content/docs/api/schema-reference.md
content/docs/guide/architecture-overview.md
content/docs/guide/deployment.md
content/docs/guide/expressions.md
content/docs/guide/notifications.md
content/docs/guide/public-forms.md
content/docs/guide/schema-overview.md
content/docs/guide/troubleshooting.md
content/docs/guide/user-state-persistence.md
content/docs/plugins/index.md

The script diff is 11 insertions, 60 deletions: the ten entries, plus the ledger docblock's own count, which said "19 of these entries are .md pages" and would have been false the moment these landed.

CI cost under the #4846 ruling: the closure does not grow

--build-filter output did change — 15 → 19 filters, adding @object-ui/auth, @object-ui/i18n, @object-ui/plugin-form, @object-ui/plugin-kanban. The number that matters did not:

readingbeforeafter
--filter= arguments1519
turbo build tasks (filters + dependencies)3333

The two task sets are identical — diffed package-by-package from turbo run build … --dry=json, and the only difference between the two files was a trailing newline. All four newly-named packages were already inside the closure as transitive dependencies of @object-ui/app-shell, which #5341 pulled in via guide/metadata-diagnostics.md. So ten more covered pages cost this gate zero extra build tasks. Stated rather than absorbed, per #4846; there is nothing here for the maintainer to weigh.

Real defects fixed, not just declared

Four documented APIs did not compile against what the packages ship. These are the #5160 class, on the pages a reader copies from most:

  • guide/deployment.mdcreateLazyPlugin(() => import('@object-ui/plugin-grid')). @object-ui/plugin-grid has no default export (dist/index.d.ts opens export { ObjectGrid, VirtualGrid, ImportWizard };), so the argument is the module namespace, not a component: TS2322, and at runtime React.lazy would render the namespace object. Now async () => ({ default: (await import('@object-ui/plugin-grid')).ObjectGrid }). ⚠️ The .then((m) => ({ default: m.ObjectGrid })) spelling infers P as never on one then branch and stays red — the async form is load-bearing. The same wrong call is still authored in packages/react/src/LazyPluginLoader.tsx's own JSDoc, three times, and reaches the published .d.ts. Out of this card's file surface → filed as createLazyPlugin's own JSDoc teaches a call that does not compile — import('@object-ui/plugin-grid') has no default export #5949.
  • guide/troubleshooting.md — the page mounted I18nProvider with locale="en" and messages={messages}. I18nProviderProps declares neither prop; it takes config?: I18nConfig. Now config={{ defaultLanguage: 'en', resources }}.
  • guide/troubleshooting.mdconst { startMeasure, endMeasure } = usePerformance('MyWidget'). usePerformance takes a PerformanceConfigobject, not a label string, and returns { config, metrics, markRenderStart, debounce } — neither destructured name exists. Now markRenderStart(), which returns the stop function.
  • guide/user-state-persistence.mdcreateObjectStackUserStateAdapter({ …, kind: 'favorites' }). The option is key, a dotted namespaced string (ui.favorites), and kind is rejected outright (TS2353). Now key: 'ui.favorites' / key: 'ui.recent', plus the missing useEffect import and a typed dataSource parameter. ⚠️ That page's prose and yaml storage-model section still describe a user_app_state object keyed by kind, a design packages/data-objectstack/src/userState.ts:6-10 explicitly rejected in favour of sys_user_preference. No gate reads a yaml fence, so it is a different defect → filed as user-state-persistence.md's storage-model section documents a user_app_state object keyed by kind — the shipped adapter uses sys_user_preference keyed by key #5950.

One decision #5343 genuinely did not cover, stated explicitly

guide/public-forms.md's quick start imported restDataSource from @object-ui/data-rest — a package that does not exist: no such directory under packages/, and no manifest in the workspace references it. #5343's table enumerates fabricated symbols on real packages; a fabricated module is outside it, so this call is mine and I am naming it rather than folding it in silently. Disposition, in #5053's vocabulary: lives in a neighbour package — the real adapter is createObjectStackAdapter from @object-ui/data-objectstack, whose declared return type is a generic DataSource, exactly what EmbeddableFormProps.dataSource takes. ⛔ No export was added anywhere.

Verification

All at the final commit 31fe8b4, working tree clean, run from the repo root against a build of the gate's own filter closure (turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter) --concurrency=2, 32/32 successful, serialised under the container's shared heavy-verify lock). Every gate by name with its own printed verdict:

  • node scripts/check-doc-snippet-types.mjs → exit 0. Scanned 222 document(s): 169 covered (33 of them hold a ts/tsx block), 53 ungated · Covered blocks: 161 — 127 to compile, 34 declared fragment(s). · Syntax phase: every block parsed, so every one of them reached the semantic phase. · Semantic phase: 127 of 127 block(s) judged, 0 failed. · Every covered documentation snippet compiles against the built types. Controls green: resolution landed on packages/types/dist/index.d.ts, sentinel produced TS2305, positive control 0 diagnostics.
  • pnpm exec vitest run scripts/__tests__/check-doc-snippet-types.test.tsTest Files 1 passed (1) / Tests 20 passed (20). Running the script is not running its test; both were run, from the repo root.
  • node scripts/check-doc-component-types.mjs → exit 0. Scanned 183 doc file(s) (.mdx + .md), 1056 code block(s), 883 type literal(s) … 745 registered, 138 exempted. · ✅ Every documented component type is registered.
  • node scripts/check-doc-links.mjs → exit 0. Links are valid across 13 scan roots.
  • node scripts/check-control-bytes.mjs → exit 0. ✅ check-control-bytes: OK (scanned 4939 tracked text file(s); skipped 85 binary). Plus a manual grep -naP control-byte scan over all 11 changed files: no hits.
  • node scripts/check-changeset-presence.mjs → exit 0. 11 file(s) changed, 0 of them under the src/ of a package the release covers … ✅ No source of a released package changed in this range, so no changeset is owed. None added, per its verdict.
  • node scripts/check-changeset-no-major.mjs → exit 0. ✅ No changeset declares a major bump.
  • eslint . --ignore-pattern 'packages/*/**' --ignore-pattern 'examples/*/**' --ignore-pattern 'apps/*/**' --ignore-pattern 'docs/**' (the complete root lint task, not a narrowing — it runs in 9.5s) → exit 0. ✖ 26 problems (0 errors, 26 warnings), all pre-existing; the changed .mjs reports errors=0 warnings=0. Population read from eslint's own --format json: 165 files, of which 0 are .md — a changed .md linted directly answers File ignored because no matching configuration was supplied, so ten of the eleven changed files are outside eslint's population by its own config. The other 46 turbo run lint tasks are per-package and this diff touches nothing under packages/*/, apps/*/ or examples/*/.
  • pnpm check (the CLI self-check the lint workflow runs) → exit 0. Analyzing 615 files … ✓ All checks passed.

Exit codes were captured before any pipe, and each line above is the gate's own printed verdict rather than a bare $?.

The .md fragment marker was re-verified, not inherited

24 markers were added and the whole batch leans on them, so #5341's finding was re-measured rather than trusted. apps/site/node_modules/fumadocs-mdx@15.2.3 selects the compiler format by extension — filePath.endsWith(".mdx") ? "mdx" : "md" — and every one of the nine marker-bearing files was then compiled through @mdx-js/mdx 3.1.1 in format md: all nine compile, and the string doc-snippet appears 0 times in every compiled output against 24 occurrences in source. The marker reaches no reader.

Reverse verification — prediction written before the run, both legs

Build artifact between the mutation and the thing under test: NONE on either leg. The mutation is markdown the gate reads straight from disk; the dist/*.d.ts it compiles against is untouched, so no rebuild is required for either leg or for the restore. Both legs proved the mutation reached disk by grepping the specific text meant to change, not by trusting an editor's exit code, and the script carried a trap … EXIT INT TERM restore.

Leg 1 — revert api/schema-reference.md to origin/main while keeping its ledger entry removed. Predicted: exit 1, exactly one new diagnostic (TS2304: Cannot find name 'BaseSchema'), block counts unchanged, direction MORE findings. Observed: exit 1 · content/docs/api/schema-reference.md:26:19 TS2304: Cannot find name 'BaseSchema'. · Covered blocks: 161 — 127 to compile, 34 declared fragment(s) · Semantic phase: 127 of 127 block(s) judged, 1 failed. Match, including direction.

Leg 2 — revert all ten pages, ledger removals kept. Predicted: exit 1, declared fragments collapse 34 → 10, blocks-to-compile rise, parse failures reappear and the reduced-coverage NOTE prints, roughly the 90 diagnostics measured in the baseline. Observed: exit 1 · Covered blocks: 161 — 151 to compile, 10 declared fragment(s) · Syntax phase: 8 block(s) failed to parse and were NOT semantically checked · Semantic phase: 143 of 151 block(s) judged, 29 failed · the NOTE printed · 90 failure lines exactly, matching the baseline total to the unit. Match, including direction.

Restore leg — run, not skipped: tree clean against HEAD, 24 markers back on disk, gate exit 0 with 127 of 127 block(s) judged, 0 failed. The tree is byte-identical to what is pushed.

Two ledger reasons that have drifted, reported rather than edited

The measurement harness reproduces the recorded mix exactly for 17 of 19 pages. Two disagree, and neither is touched here — re-wording a surviving entry would push this diff outside the batch:

  • guide/schema-overview.md recorded 8 parse diagnostics; 7 measured. (Cleared by this PR anyway.)
  • guide/building-crud-app.md recorded 20 undefined-name … 4 unresolved-module; 14 and 3 measured. Its entry now over-states its debt by 6 and 1. Worth refreshing when that page is taken.

Out of scope, filed unassigned

Both searched for first; no open issue covered either.

⛔ Not folded in, all deliberately: #5867 (plaintext-fenced ts/tsx blocks), #5106 / #5342 (the sibling gate's fence and extension axes), #5465 (skills/ outside the scan surface). Same gate family, different defects.


Generated by Claude Code

os-warrenand others added 2 commits August 24, 2026 03:49
…pages
Part of #5174
Make every ts/tsx block in these pages either compile against the built
dist/ types or carry a fragment declaration with a written reason.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQ3NihCHE9LUtHoGxo6A9f
Part of #5174
Batch 1 of the UNGATED_DOCS triage: the cheapest ten of the nineteen .md
guide entries, chosen by measured total diagnostic count. Each page reaches
zero the two honest ways - a block that should compile is made
self-contained against the built dist/, a block that genuinely cannot
compile carries a FRAGMENT_MARKER declaration with a written reason - and
its ledger entry is then deleted.
Covered set 159 -> 169 documents; ledger 63 -> 53 entries; zero entries
added and no previously-covered document became ungated. Nothing about the
gate's strictness moved: everything from the 'Fence scanning' banner to EOF
is byte-identical to origin/main.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQ3NihCHE9LUtHoGxo6A9f
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.

1 participant

@os-warren