feat(gate): bring the root docs/*.md pages into the doc-snippet walk - #8158
Conversation
objectui#7856 card 1. The repository-root `docs/` tree was read by no documentation gate: not this one, not `check:doc-fences`, not `check:doc-types`, and `lint:root` ignores it by name. Three phantom-teaching sites had already been found in it by hand (objectui#7838, objectui#7854). `listDocuments()` now carries a `docs/*.md` leg, TOP LEVEL only, in the same slot pattern the root `README.md` took for objectui#7115 — a named, exported `ROOT_DOCS` constant plus an exported `rootDocsPages()` enumerator, filtered to files by `statSync`, so `docs/adr/**` (a governed surface) and `docs/audits/**` cannot arrive by accident. Those two are card 2, for a seat that can land a governed-surface pull request. `main()` refuses a verdict when the directory is missing, the way it already does for a dangling `ROOT_PAGES` name. The leg is REPAIRED, not ledgered: `UNGATED_DOCS` is untouched at 11 entries and declared fragments stay at 158. All 11 diagnostics measured under `docs/*.md` were in `docs/ARCHITECTURE.md`, all semantic, and each was resolved with a typed binding rather than a marker — `declare const` stand-ins annotated from the shipped `DataSourceProviderProps` / `AppShellProps`, a Next.js example split into the two files it always was (one fence per module, which is how the gate compiles them), and the example adapter's two parameters spelled with the types the page's own `DataSource` interface declares. `check-doc-fence-languages.test.ts` records the divergence rather than losing the comparison: it subtracts exactly `rootDocsPages()` — the gate's own export, not a hand-written list of today's filenames — and still fails on any other drift, plus a new pin that the leg stops above `docs/adr/` and `docs/audits/`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
|
Standing down on That check is informational (not in the Generated by Claude Code |
|
Armed —
Landing stroke follows the merge: content probe on re-fetched Generated by Claude Code |
|
LANDED — Merged at 2026-09-06T22:01:15Z as Release stroke on objectui#7856 follows (card 1 landed; card 2 to triage). Generated by Claude Code |
Refs #7856 (card 1: docs/*.md). Card 2 (
docs/adr/**+docs/audits/**, the governed half) stays open on that card for another seat — no closing keyword anywhere in this body on purpose.Base:
origin/mainat571b4870d(carries batch 26 / PR #8147 and the--emit-censusmode from PR #8138). Final HEAD:f011733bf.What this does
scripts/check-doc-snippet-types.mjs'slistDocuments()now carries adocs/*.mdleg, top level only, in the same slot pattern the rootREADME.mdtook for #7115:export const ROOT_DOCS = { dir: 'docs', recursive: false }— a named constant that states where the surface stops.export function rootDocsPages(root)— enumerates directory entries, filtered to files bystatSync, sodocs/adr/**(governed) anddocs/audits/**cannot arrive by a page being moved into a subdirectory. Exported so a sibling census asks this gate what its leg holds instead of re-spelling it.main()refuses a verdict whendocs/is missing, exactly as it already does for a danglingROOT_PAGESname — a leg that silently collects nothing is finding(docs/gate): the root README's flagship dashboard example teachesstat-card, which nothing registers — and the root README is outside every doc gate's scan surface #7115's defect one level up.docs/*.md (top level only): fences ✗ · snippets ✓ · types ✗), the unscanned-population paragraph and itsgit ls-filesrecipe are all updated in the same stroke.Document population: 227 → 229. Covered blocks 735 → 741, compiled 577 → 583, declared fragments unchanged at 158,
UNGATED_DOCSunchanged at 11 entries.Census — the gate's own analyzer, on this tree
Taken with the gate's exported
scanFences/derivePackageTypePaths/deriveDeclaredDependencyPaths/compileSnippets(no hand-written regex), against the built closure (pnpm exec turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter) --concurrency=2— 34/34 tasks successful).docs/ARCHITECTURE.mdtsxat fence lines 161, 186, 211, 240, 253)docs/CONSOLE-STREAMLINING-SUMMARY.mdBlind spots: none. Syntax phase: every block parsed, so all 5 reached the semantic phase; root-bound refusals 0; no fence the extractor could not read. The card's estimate for this half (11 diagnostics on
8507a2283) re-measures 11 here; the per-code split differs from the card's tree-wide split because that one covereddocs/adr/**anddocs/audits/**too.Both the card's and the claim comment's readings needed one correction:
docs/ARCHITECTURE.mdholds 5tsxfences, at 161, 186, 211, 240 and 253 — the claim's list of four omitted 253, which carries 2 of the 11 diagnostics. The 240 block (interface DataSource) is the one that already compiled clean.Ledger or repair, per block
Repair, in all four cases. No
UNGATED_DOCSentry was written and no fragment marker was added — declared fragments stay at 158. Typed bindings before markers (batch 26 / PR #8147), no@ts-expect-error(no readonly or known-defect case here), no loosened type, no marker hiding a teaching defect.myAPI, TS2304MySidebardeclare const/declare functionstand-ins annotated from the shippedDataSourceProviderProps['dataSource']andAppShellProps['sidebar']DataSourceProviderdeclares that propany, so the annotation records where the value goes, it does not check the value's shape.defaultdiagnostics were not a defect in either example: one fence held two modules (app/layout.tsxandapp/[object]/page.tsx), and the gate compiles a block in isolation, so the twoexport defaults collided. One fence per file is what the page meant; the prop types come fromAppShellProps['children']and the route's own{ object: string }. This is the +1 block (5 → 6).myAPIinterface DataSource)DataSourceinterface declares two paragraphs above, so nothing new is asserted.Real teaching defects, named rather than papered over — proposals for their own cards
None of these produced a diagnostic; the gate structurally cannot see them. They are not filed as issues — this card's output goes here for the PM to route.
DataSourceinterface the page documents is not any package's exported contract, andObjectViewwould reject it. Measured through the gate's owncompileSnippets(probe, not committed): annotate a stand-in with the page's verbatim 6-method interface and pass it toObjectViewand you getTS2741: Property 'getObjectSchema' is missing in type 'DataSource' but required in type 'DataSource(OPT)'.@object-ui/typesexports a much largerDataSource;@object-ui/app-shellhas a near-identical private one but does not export it. The only reason the page's examples type-check is thatuseDataSource()is declaredanyandDataSourceProviderProps.dataSourceisany— the value is laundered between the provider and the renderer, so the page's central claim ("third-party systems implement this interface") is unchecked end to end by construction.'use client'.app/layout.tsxrendersThemeProvider/AppShellandapp/[object]/page.tsxcalls theuseDataSourcehook; under the App Router both are server components by default, so a reader copying them literally gets a runtime error. The string appears nowhere on the page.Strictness region
The #5174 burn-down's byte-identical-strictness licence, re-baselined here because the leg lands inside the region (the
Fence scanningbanner to EOF):571b4870d5dfcd1b876c044ef33b6b5fed8a57ec719622ddbb20ce0ab539e5c985a233017f011733bf2749d53ae3a8df033a53b8d7a354fa7e22ee2d1a17f6ad0c3d61122e904e084bReason: the scan population grew; no strictness rule moved. Exactly two additions sit inside the region — the leg's call site in
listDocuments()(4 lines, one of them code) and the missing-directory guard inmain(). Fence languages read, fragment-marker semantics, the diagnostic filters and every failure classification are byte-identical.Pin resolution
The PM's reading held, and only one pin moved:
scripts/__tests__/check-doc-fence-languages.test.ts— itswalks exactly the documents the snippet gate walksassertion went red, as expected, becausecheck:doc-fencesdoes not gain this leg (its surface is not card 1's to move, andcheck:doc-typesis finding(gate):check:doc-typesdoes not walkpackages/NAME/README.md— a package README'stypeliterals are judged by nothing, and objectui#7856's enumeration says otherwise #7896's). It now records the divergence: the equality subtracts exactlyrootDocsPages()— the snippet gate's own export, not a hand-written list of today's two filenames, so a page added todocs/travels into both sides by itself and a page added underdocs/adr/travels into neither. Two assertions added beside it: the subtraction is non-empty and every subtracted document really is on one side only; and the leg stops abovedocs/adr/anddocs/audits/in both walks, with a non-vacuity check thatdocs/adrstill exists.scripts/__tests__/check-doc-expression-carriage.test.ts— untouched and green. It pins the expression-carriage census againstcheck-doc-component-types' constants, not this gate's, so this leg does not reach it.scripts/__tests__/check-doc-snippet-types.test.ts— gains the widening's own proof, the way finding(docs/gate): the root README's flagship dashboard example teachesstat-card, which nothing registers — and the root README is outside every doc gate's scan surface #7115's leg has one: membership by name, that the leg contributes blocks to the compiled tier (a leg whose pages all sat on the ledger would be visible and judged by nothing), non-recursion on a fixture holdingdocs/adr/anddocs/audits/pages, files-and-page-extensions only, and the missing-directory refusal.check:doc-fences,check:doc-types,check:readme-exportsall exit 0 as controls that the siblings did not move.scripts/check-doc-fence-languages.mjs's header now says two things that stopped being true: "The scan surface ischeck-doc-snippet-types's, exactly: …", and that the pin "fails if they ever return different document lists". The pin's own comment states the divergence and its reason, but that gate's prose should be corrected in a follow-up (this card's file surface named that gate's test, not the gate).Positive controls — both by state, under a trap, against the committed tree
A. The gate really judges the new leg. Injected
objectName: 42into the Example 1 fence (objectNameisstringonObjectViewSchema).B. The silent-skip control. Removed the leg's call site from
listDocuments():--emit-censusis #7864's emitted-code census: it walkspackages/NAME/srcthroughlistEmittedSources()and never callslistDocuments(). Measured on both sides of control B —Walked 1414 source file(s)with the leg and1414without it. So the document-population reading is the gate's ownScanned N document(s)counter, above; the--emit-censusnumbers are the falsification, not the measurement.Gates, pinned to
f011733bfExit codes captured by redirect-then-capture, never through a pipe.
pnpm check:doc-snippetsScanned 229 document(s): 218 covered (115 of them hold a ts/tsx block), 11 ungated·Covered blocks: 741 — 583 to compile, 158 declared fragment(s)·Semantic phase: 583 of 583 block(s) judged, 0 failedpnpm exec vitest runon the three sibling pinspnpm exec vitest run scripts/__tests__/pnpm check:doc-fencespnpm check:doc-typespnpm check:readme-exports@object-ui/plugin-ai's unbuiltdist— a precondition of the scoped build, not a verdict; 0 after building that package, and this diff holds nopackages/**file)pnpm type-check:scriptspnpm lint:rootpnpm check:control-bytesgrep -naPcontrol-byte self-scan of all five changed paths: no matchpnpm check:entry-guardnode scripts/check-changeset-presence.mjsnode scripts/check-governed-queue-guard.mjs --teston all five changed pathsNOT GOVERNED — 5 path(s) checked against 5 governed surface(s); none matchedLive E2E (informational)is red on every branch today for an upstream reason (#7990 / objectstack#16186) — not this change.Files
scripts/check-doc-snippet-types.mjs— the leg, the constant, the enumerator, themain()guard, the header enumerations.scripts/__tests__/check-doc-snippet-types.test.ts— the widening's proof.scripts/__tests__/check-doc-fence-languages.test.ts— the divergence, recorded.docs/ARCHITECTURE.md— block repairs only..github/workflows/doc-snippet-types.yml— one step comment that copied the gate's enumeration (it had already gone stale on finding(docs/gate): the root README's flagship dashboard example teachesstat-card, which nothing registers — and the root README is outside every doc gate's scan surface #7115's rootREADME.md).🤖 Generated with Claude Code
https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
Generated by Claude Code