Skip to content

feat(gate): bring the root docs/*.md pages into the doc-snippet walk - #8158

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-7856-root-docs-snippet-walk
Sep 6, 2026
Merged

feat(gate): bring the root docs/*.md pages into the doc-snippet walk#8158
baozhoutao merged 1 commit into
mainfrom
claude/issue-7856-root-docs-snippet-walk

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

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/main at 571b4870d (carries batch 26 / PR #8147 and the --emit-census mode from PR #8138). Final HEAD: f011733bf.

What this does

scripts/check-doc-snippet-types.mjs's listDocuments() now carries a docs/*.md leg, top level only, in the same slot pattern the root README.md took 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 by statSync, so docs/adr/** (governed) and docs/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 when docs/ is missing, exactly as it already does for a dangling ROOT_PAGES name — a leg that silently collects nothing is finding(docs/gate): the root README's flagship dashboard example teaches stat-card, which nothing registers — and the root README is outside every doc gate's scan surface #7115's defect one level up.
  • The header's enumeration of the scan surface, the three-gate ownership table (new row docs/*.md (top level only): fences ✗ · snippets ✓ · types ✗), the unscanned-population paragraph and its git ls-files recipe are all updated in the same stroke.

Document population: 227 → 229. Covered blocks 735 → 741, compiled 577 → 583, declared fragments unchanged at 158, UNGATED_DOCS unchanged 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).

file ts/tsx blocks diagnostics by code blind spots
docs/ARCHITECTURE.md 5 (tsx at fence lines 161, 186, 211, 240, 253) 11, all semantic TS2552 x2, TS2304 x1, TS2323 x2, TS2393 x2, TS7031 x2, TS7006 x2 0
docs/CONSOLE-STREAMLINING-SUMMARY.md 0 0 0

Blind 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 covered docs/adr/** and docs/audits/** too.

Both the card's and the claim comment's readings needed one correction: docs/ARCHITECTURE.md holds 5 tsx fences, 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_DOCS entry 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.

block diagnostics decision why
fence 161 (Example 1) TS2552 myAPI, TS2304 MySidebar repair — declare const / declare function stand-ins annotated from the shipped DataSourceProviderProps['dataSource'] and AppShellProps['sidebar'] The batch-26 idiom: the reader's two inputs become real bindings typed by the surface these packages ship, so the block is self-contained the way the gate compiles it (one block, one module). The comment states the bound honestly — DataSourceProvider declares that prop any, so the annotation records where the value goes, it does not check the value's shape.
fence 186 (Example 2, Next.js) TS2323 x2, TS2393 x2, TS7031 x2 repair — split into the two files it always was, then annotate the two props The four duplicate-default diagnostics were not a defect in either example: one fence held two modules (app/layout.tsx and app/[object]/page.tsx), and the gate compiles a block in isolation, so the two export defaults collided. One fence per file is what the page meant; the prop types come from AppShellProps['children'] and the route's own { object: string }. This is the +1 block (5 → 6).
fence 211 (Example 3) TS2552 myAPI repair — same stand-in as fence 161 Same shape, same toolkit.
fence 240 (interface DataSource) 0 untouched Already compiled. See the proposal below for what the gate cannot see about it.
fence 253 (example adapter) TS7006 x2 repair — spell the two parameter types The implicit-any was the absence of an annotation, not a wrong one; the types written are the ones the page's own DataSource interface 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.

  1. The DataSource interface the page documents is not any package's exported contract, and ObjectView would reject it. Measured through the gate's own compileSnippets (probe, not committed): annotate a stand-in with the page's verbatim 6-method interface and pass it to ObjectView and you get TS2741: Property 'getObjectSchema' is missing in type 'DataSource' but required in type 'DataSource(OPT)'. @object-ui/types exports a much larger DataSource; @object-ui/app-shell has a near-identical private one but does not export it. The only reason the page's examples type-check is that useDataSource() is declared any and DataSourceProviderProps.dataSource is any — 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.
  2. The Next.js example has no 'use client'. app/layout.tsx renders ThemeProvider / AppShell and app/[object]/page.tsx calls the useDataSource hook; 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 scanning banner to EOF):

sha256
before, 571b4870d 5dfcd1b876c044ef33b6b5fed8a57ec719622ddbb20ce0ab539e5c985a233017
after, f011733bf 2749d53ae3a8df033a53b8d7a354fa7e22ee2d1a17f6ad0c3d61122e904e084b

Reason: 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 in main(). 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 — its walks exactly the documents the snippet gate walks assertion went red, as expected, because check:doc-fences does not gain this leg (its surface is not card 1's to move, and check:doc-types is finding(gate): check:doc-types does not walk packages/NAME/README.md — a package README's type literals are judged by nothing, and objectui#7856's enumeration says otherwise #7896's). It now records the divergence: the equality subtracts exactly rootDocsPages() — the snippet gate's own export, not a hand-written list of today's two filenames, so a page added to docs/ travels into both sides by itself and a page added under docs/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 above docs/adr/ and docs/audits/ in both walks, with a non-vacuity check that docs/adr still exists.
  • scripts/__tests__/check-doc-expression-carriage.test.tsuntouched and green. It pins the expression-carriage census against check-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 teaches stat-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 holding docs/adr/ and docs/audits/ pages, files-and-page-extensions only, and the missing-directory refusal.
  • check:doc-fences, check:doc-types, check:readme-exports all exit 0 as controls that the siblings did not move.

⚠️ Owed follow-up, outside this card's file surface. scripts/check-doc-fence-languages.mjs's header now says two things that stopped being true: "The scan surface is check-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: 42 into the Example 1 fence (objectName is string on ObjectViewSchema).

on-disk proof   anchor occurrences 2 -> 1, injected text 1   (blob 9e97fd3ce -> 6f9df8e96)
GATE_EXIT=1
  [semantic]  docs/ARCHITECTURE.md:190:48  TS2322: Type 'number' is not assignable to type 'string'.
Semantic phase: 583 of 583 block(s) judged, 1 failed.
restore         blob back to 9e97fd3ce24458f0d8a4e1a8705f8f7bdf8a9315 (= HEAD blob), `git diff HEAD -- docs/ARCHITECTURE.md` empty

B. The silent-skip control. Removed the leg's call site from listDocuments():

on-disk proof   call-site occurrences 1 -> 0, marker 1   (HEAD blob 56aca37a18634e3c9ec138996ec6b9d76c010216)
documents WITH leg    : 229
documents WITHOUT leg : 227
drop                  : 2 = rootDocsPages(root).length
restore         blob back to 56aca37a1 (= HEAD blob), `git diff HEAD -- scripts/check-doc-snippet-types.mjs` empty

⚠️ The instrument the brief named for control B does not measure documents. --emit-census is #7864's emitted-code census: it walks packages/NAME/src through listEmittedSources() and never calls listDocuments(). Measured on both sides of control B — Walked 1414 source file(s) with the leg and 1414 without it. So the document-population reading is the gate's own Scanned N document(s) counter, above; the --emit-census numbers are the falsification, not the measurement.

Gates, pinned to f011733bf

Exit codes captured by redirect-then-capture, never through a pipe.

command exit
pnpm check:doc-snippets 0 — Scanned 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 failed
pnpm exec vitest run on the three sibling pins 0 — 3 files, 160 tests (153 before)
pnpm exec vitest run scripts/__tests__/ 0 — 115 files, 3415 tests
pnpm check:doc-fences 0
pnpm check:doc-types 0
pnpm check:readme-exports 0 (first run exited 1 on @object-ui/plugin-ai's unbuilt dist — a precondition of the scoped build, not a verdict; 0 after building that package, and this diff holds no packages/** file)
pnpm type-check:scripts 0
pnpm lint:root 0 (32 pre-existing warnings; full run, 11s — no narrowing)
pnpm check:control-bytes 0, plus a grep -naP control-byte self-scan of all five changed paths: no match
pnpm check:entry-guard 0
node scripts/check-changeset-presence.mjs 0 — nothing owed (a gate script, its tests, a workflow comment and a repo-root doc publish from no package)
node scripts/check-governed-queue-guard.mjs --test on all five changed paths 0 — NOT GOVERNED — 5 path(s) checked against 5 governed surface(s); none matched

Live E2E (informational) is red on every branch today for an upstream reason (#7990 / objectstack#16186) — not this change.

Files

🤖 Generated with Claude Code

https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr


Generated by Claude Code

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

Copy link
Copy Markdown
Contributor Author

Standing down on Live E2E (informational) for this PR — domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, 2026-09-06T21:34Z.

That check is informational (not in the main ruleset's required contexts) and is red on the base branch for an upstream reason anchored on objectui#7990 / objectstack#16186; this PR touches a gate script, its tests, a repo-root doc and a workflow comment, none of which the Live E2E suite exercises. No re-run is requested. Every required check is judged on its own conclusion below; this PR flips to ready only when all of them read completed / success on f011733bf.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Armed — domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46.

  • CI on f011733bf converged all green at 21:44:59Z (28 checks; Live E2E (informational) excluded per the standing-down note above).
  • Ready for review at 21:45:24Z; post-flip Governed Surface Queue Guard completed/success at 21:45:45Z.
  • Auto-merge SQUASH enabled; added_to_merge_queue on the REST timeline at 21:45:56Z (read 21:46:04Z).

Landing stroke follows the merge: content probe on re-fetched origin/main (rootDocsPages in scripts/check-doc-snippet-types.mjs, with a control), LANDED note here, and the release stroke on objectui#7856 (card 1 landed, card 2 to triage).


Generated by Claude Code

Merged via the queue into main with commit 9bfd618 Sep 6, 2026
30 of 31 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-7856-root-docs-snippet-walk branch September 6, 2026 22:01

Copy link
Copy Markdown
Contributor Author

LANDED — domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46.

Merged at 2026-09-06T22:01:15Z as 9bfd61848 (squash; the removed_from_merge_queue event preceded the merged flag by a beat, as usual). Content probe on re-fetched origin/main at 22:01Z: git grep rootDocsPages origin/main -- scripts/check-doc-snippet-types.mjs hits the export at :533 and the call site at :999; control ROOT_PAGES non-empty; declare const myAPI present in docs/ARCHITECTURE.md at :174 and :234. Strictness region on origin/main hashes to 2749d53ae3a8df033a53b8d7a354fa7e22ee2d1a17f6ad0c3d61122e904e084b — the objectui#5174 burn-down's new baseline from this commit on.

Release stroke on objectui#7856 follows (card 1 landed; card 2 to triage).


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd configuration documentation Improvements or additions to documentation tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants