feat(scripts): point the three doc gates at apps/*/docs (#6600) - #7449

Merged
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots
Sep 3, 2026
Merged

feat(scripts): point the three doc gates at apps/*/docs (#6600)#7449
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes#6600

Step 2 of maintainer ruling D on #6600 (2026-09-02, verbatim 「同意」, recorded by the director seat). Step 1 was the content change, landed as PR #7425 (39af82f).

The three documentation gates all rooted their walk at content/docs — plus, for two of them, packages/NAME/README.md and the root README.md. None descended into apps/, so apps/console/docs/** was read by no documentation gate at all. The only check whose surface contained those files was check:control-bytes, which enumerates git ls-files and therefore covers every tracked text file: the console's operator guides were checked for control bytes and for nothing else.

check:doc-fences and check:doc-snippets move together because check-doc-fence-languages.test.ts pins their document lists deep-equal — that coupling is mechanical, not a convention. check:doc-types joining was the one judgement the ruling delegated to this lane; the reasoning is below and is written into that gate's header.

What moved

gatebeforeafter
check:doc-fences — documents225228
check:doc-snippets — documents225228
check:doc-snippets — covered / ungated193 / 32196 / 32
check:doc-snippets — covered blocks563577
check:doc-snippets — compiled / declared fragments417 / 146421 / 156
check:doc-types — doc files186189
check:doc-types — code blocks10901115
check:doc-typestype literals922922

The acceptance criterion the ruling set, met verbatim: check:doc-snippets judges the apps/*/docs/** blocks and reports 0 failed (421 of 421 block(s) judged, 0 failed), with the fragment blocks counted as declared fragments (+10) rather than skipped files; no UNGATED_DOCS growth (32 → 32); the three gate headers state their scope and agree with their roots.

UNGATED_DOCS and the fences shrink-only baseline are byte-identical to main, asserted rather than eyeballed — same SHA-256 for the extracted block in both cases, and KNOWN_UNHIGHLIGHTED_TS_FENCES still reports the same 80 files / 90 blocks.

⚠️ Three of the four newly-compiled blocks are green about nothing — #7426

This is stated here rather than absorbed into the green, because this card exists precisely because a gate suite reports green over a file it never opened, and shipping a gate that reports green over blocks it cannot fail is the same defect wearing the fix's clothes.

Of the 4 blocks that become live-compiled in this tree, 3 are design-token lists under "Visual Design Specs" in apps/console/docs/UI_IMPROVEMENT_PROPOSAL.md (Color Palette, Typography, Spacing). They compile only because background: "bg-background" parses as a labelled statement whose body is a string-literal expression — syntactically legal, semantically empty. So three quarters of this tree's new semantic coverage is vacuous.

That is not a prediction. It is demonstrated in leg A2 of the ablation below: replacing background: "bg-background" with background: "ABLATION_6600_this_is_not_typescript_at_all" leaves check:doc-snippetsgreen (exit 0), while the same class of defect in the one genuine block turns it red.

#7426 remains open and is the right home for it — re-fencing is a document-shape change and that card records the fence target (plain text vs. a markdown table) as undecided, which makes it domain:ui's judgement call. ⛔ The three blocks were deliberately not marked doc-snippet: fragment: that marker asserts a block cannot compile, which is false here, and it would drop them from real coverage. Out of scope for this PR: #7426.

Non-vacuity — each moved gate proven to actually judge the new population

A root that moves while the walk still misses the files passes every green check and buys nothing. Each leg plants a deliberate defect in an apps/*/docs/** block, proves the mutation reached disk before the run (HEAD blob hash != worktree blob hash, plus a marker grep -c), then restores with git checkout HEAD -- ABSOLUTE_PATH and proves the restore (blob == HEAD blob and empty git diff HEAD). The script carries trap … EXIT INT TERM. The mutated subject is markdown the gate reads by path at runtime, not an artifact resolved through a package dist/, so no rebuild leg applies; the packages the snippet gate compiles against were unchanged and already built.

legplanted defectgateexitverdict line
A1const ABLATION_6600: number = "not a number"; in the genuine compiled blockcheck:doc-snippets1TS2322 at UI_IMPROVEMENT_PROPOSAL.md:174; 421 of 421 block(s) judged, 1 failed
A2same class of defect in a design-token blockcheck:doc-snippets0421 of 421 block(s) judged, 0 failed — the #7426 vacuity, demonstrated
Binterface ABLATION_6600 { under a bare fence in deployment.mdcheck:doc-fences1deployment.md:21 — bare fence, no info string, body interface ABLATION_6600 {
C{ "type": "ablation-6600-unregistered" } in a fenced blockcheck:doc-types1[unregistered-doc-type] type 'ablation-6600-unregistered' (tsx)

Final tree state after all four legs: git diff HEADempty.

Why check:doc-types joined, at zero present yield

Stated plainly because the alternative reading — that a widened scope was justified by a discovery — is false: the three files carry 0type literals today, and the count is unchanged at 922 after the move. This gate finds nothing on the day it lands.

The argument for joining anyway is the split-surface defect this exact gate was already burned by once. In #7115, check-doc-component-types walked content/docs; check-doc-snippet-types walked content/docs plus the package READMEs; the root README.md fell between the two and was read by neither — and it taught the unregistered type stat-card four times, in the flagship example, for as long as that example existed. Leaving this gate pointed away from a tree its two siblings now read would rebuild that geometry deliberately, in the same gate family. And UI_IMPROVEMENT_PROPOSAL.md is a proposal about console UI shape, i.e. the file in that tree most likely to grow the first type literal.

⛔ This is not a precedent for widening onto any other unscanned tree, and no allowlist mechanism is built — the population is three files.

Scope statement, not scope expansion

The ruling asked for the ownership of the unscanned markdown to be written into the gate headers as a statement of what the roots are. The full ownership map lives once in check-doc-snippet-types.mjs beside UNGATED_DOCS (widest surface, holds the ledger); the other two headers state their own roots and point at it.

Measured for that statement rather than inherited: the unscanned population is 114 files excluding the ephemeral .changeset/ — not the "roughly 50" the ruling's prose carries — the largest groups being non-README .md under packages/** (54), docs/** (17) and the published skills/objectui/** (16). The header gives the re-derivation command rather than relying on that number, matching the fix this repo already applied to UNGATED_DOCS's own header.

No gate is widened onto skills/**. It is named in the map as unclaimed, which is the opposite of a claim: a governed, published surface whose coverage is a decision for whoever owns it, never a side effect of a root move. Writing an unscanned tree down is what keeps it a known debt rather than #5174's "neither covered NOR declared ungated".

Pins added

scripts/__tests__/check-doc-fence-languages.test.ts gains three, in the shape #7115 established and check-doc-component-types' header demands of anything added later:

  • all three gates carry the same APP_DOCS constant (mirrors the existing ROOT_PAGES tri-gate pin);
  • the three guides are really in the walk by name — the deep-equality assertion does not cover this, since both walks could drop the tree together and stay equal, which is exactly the state this card was filed about;
  • the walk takes one app-directory level and does not descend into apps/site/app/docs, a Next.js route directory holding .tsx route files.

Verification

Run from the repo root at eba0a9b7a, tree clean, exit codes captured by redirect before any pipe:

  • pnpm check:doc-fences0228 document(s), baseline unchanged at 80 files / 90 blocks
  • pnpm check:doc-snippets0421 of 421 block(s) judged, 0 failed
  • pnpm check:doc-types0Every documented component type is registered.
  • pnpm check:control-bytes0 — 6131 tracked text files
  • pnpm exec vitest run over check-doc-fence-languages, check-doc-snippet-types, check-doc-component-types plus every other test that names these scripts (check-pre-install-import-graph, one-authority-per-exported-name-6273, known-schema-types-derivation-5115, report-namespace-agreement-6416) → 7 files, 196 tests, all passing
  • pnpm lint (full repo, not narrowed) → 0 — 47/47 tasks, 0 errors

The three gate workflows carry nopaths filter by deliberate design — their own tests fail if one is added — so the moved roots are exercised on every pull request, including the docs-only shape that would otherwise skip them.

Changeset

Empty-frontmatter changeset: these are gate scan roots, and scripts/** publishes nothing. skip-changeset is not used — ci-cd-pipeline-doc.test.ts pins it as a phantom label in this repository. Precedent followed: .changeset/7086-blockquoted-fence-collector.md.

Out of scope

🤖 Generated with Claude Code

https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW


Generated by Claude Code

The three documentation gates all rooted their walk at `content/docs` (plus,
for two of them, the package READMEs and the root `README.md`). None descended
into `apps/`, so `apps/console/docs/**` — the console's operator and deployment
guides — was read by no documentation gate at all. The only check whose surface
contained those files was `check:control-bytes`, which enumerates `git ls-files`
and therefore covers every tracked text file: they were checked for control
bytes and for nothing else.
`check:doc-fences` and `check:doc-snippets` move together because
`check-doc-fence-languages.test.ts` pins their document lists deep-equal.
`check:doc-types` joins them so that the tree is not left in the split-surface
geometry objectui#7115 was filed about, where a file fell between two gates'
differing roots and was read by neither.
No `UNGATED_DOCS` entry is added and no allowlist mechanism is built; both
shrink-only ledgers are byte-identical to `main`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
…#6600)
The first draft of the ownership map stated the unscanned markdown population
as 114 files. The re-derivation command the same block ships answers 111, at
both `origin/main` and this branch: the 114 counted the three
`apps/<app>/docs/` guides that this very pull request brings under the gates.
Rather than correct 114 to 111, this removes every hand-copied count from the
block — the total and the per-tree ones alike. A number in a header drifts from
the tree and nothing fails when it does, which is objectui#7448 exactly, and
which `UNGATED_DOCS`'s own header already records happening to its `12 .mdx
pages and 32 package READMEs` sentence. The trees are now named in descending
order of size, which is the ownership statement the ruling asked for, and the
command is left as the durable answer to both "how many" and "which".
Comment-only: no code line changes, and all three gates report identical
numbers before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 06:08
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit fda1247Sep 3, 2026
31 checks passed
@os-sales
os-sales deleted the claude/issue-6600-doc-gate-scan-roots branch September 3, 2026 06:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-sales@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

feat(scripts): point the three doc gates at apps/*/docs (#6600) - #7449

Merged
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots
Sep 3, 2026
Merged

feat(scripts): point the three doc gates at apps/*/docs (#6600)#7449
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes#6600

Step 2 of maintainer ruling D on #6600 (2026-09-02, verbatim 「同意」, recorded by the director seat). Step 1 was the content change, landed as PR #7425 (39af82f).

The three documentation gates all rooted their walk at content/docs — plus, for two of them, packages/NAME/README.md and the root README.md. None descended into apps/, so apps/console/docs/** was read by no documentation gate at all. The only check whose surface contained those files was check:control-bytes, which enumerates git ls-files and therefore covers every tracked text file: the console's operator guides were checked for control bytes and for nothing else.

check:doc-fences and check:doc-snippets move together because check-doc-fence-languages.test.ts pins their document lists deep-equal — that coupling is mechanical, not a convention. check:doc-types joining was the one judgement the ruling delegated to this lane; the reasoning is below and is written into that gate's header.

What moved

gatebeforeafter
check:doc-fences — documents225228
check:doc-snippets — documents225228
check:doc-snippets — covered / ungated193 / 32196 / 32
check:doc-snippets — covered blocks563577
check:doc-snippets — compiled / declared fragments417 / 146421 / 156
check:doc-types — doc files186189
check:doc-types — code blocks10901115
check:doc-typestype literals922922

The acceptance criterion the ruling set, met verbatim: check:doc-snippets judges the apps/*/docs/** blocks and reports 0 failed (421 of 421 block(s) judged, 0 failed), with the fragment blocks counted as declared fragments (+10) rather than skipped files; no UNGATED_DOCS growth (32 → 32); the three gate headers state their scope and agree with their roots.

UNGATED_DOCS and the fences shrink-only baseline are byte-identical to main, asserted rather than eyeballed — same SHA-256 for the extracted block in both cases, and KNOWN_UNHIGHLIGHTED_TS_FENCES still reports the same 80 files / 90 blocks.

⚠️ Three of the four newly-compiled blocks are green about nothing — #7426

This is stated here rather than absorbed into the green, because this card exists precisely because a gate suite reports green over a file it never opened, and shipping a gate that reports green over blocks it cannot fail is the same defect wearing the fix's clothes.

Of the 4 blocks that become live-compiled in this tree, 3 are design-token lists under "Visual Design Specs" in apps/console/docs/UI_IMPROVEMENT_PROPOSAL.md (Color Palette, Typography, Spacing). They compile only because background: "bg-background" parses as a labelled statement whose body is a string-literal expression — syntactically legal, semantically empty. So three quarters of this tree's new semantic coverage is vacuous.

That is not a prediction. It is demonstrated in leg A2 of the ablation below: replacing background: "bg-background" with background: "ABLATION_6600_this_is_not_typescript_at_all" leaves check:doc-snippetsgreen (exit 0), while the same class of defect in the one genuine block turns it red.

#7426 remains open and is the right home for it — re-fencing is a document-shape change and that card records the fence target (plain text vs. a markdown table) as undecided, which makes it domain:ui's judgement call. ⛔ The three blocks were deliberately not marked doc-snippet: fragment: that marker asserts a block cannot compile, which is false here, and it would drop them from real coverage. Out of scope for this PR: #7426.

Non-vacuity — each moved gate proven to actually judge the new population

A root that moves while the walk still misses the files passes every green check and buys nothing. Each leg plants a deliberate defect in an apps/*/docs/** block, proves the mutation reached disk before the run (HEAD blob hash != worktree blob hash, plus a marker grep -c), then restores with git checkout HEAD -- ABSOLUTE_PATH and proves the restore (blob == HEAD blob and empty git diff HEAD). The script carries trap … EXIT INT TERM. The mutated subject is markdown the gate reads by path at runtime, not an artifact resolved through a package dist/, so no rebuild leg applies; the packages the snippet gate compiles against were unchanged and already built.

legplanted defectgateexitverdict line
A1const ABLATION_6600: number = "not a number"; in the genuine compiled blockcheck:doc-snippets1TS2322 at UI_IMPROVEMENT_PROPOSAL.md:174; 421 of 421 block(s) judged, 1 failed
A2same class of defect in a design-token blockcheck:doc-snippets0421 of 421 block(s) judged, 0 failed — the #7426 vacuity, demonstrated
Binterface ABLATION_6600 { under a bare fence in deployment.mdcheck:doc-fences1deployment.md:21 — bare fence, no info string, body interface ABLATION_6600 {
C{ "type": "ablation-6600-unregistered" } in a fenced blockcheck:doc-types1[unregistered-doc-type] type 'ablation-6600-unregistered' (tsx)

Final tree state after all four legs: git diff HEADempty.

Why check:doc-types joined, at zero present yield

Stated plainly because the alternative reading — that a widened scope was justified by a discovery — is false: the three files carry 0type literals today, and the count is unchanged at 922 after the move. This gate finds nothing on the day it lands.

The argument for joining anyway is the split-surface defect this exact gate was already burned by once. In #7115, check-doc-component-types walked content/docs; check-doc-snippet-types walked content/docs plus the package READMEs; the root README.md fell between the two and was read by neither — and it taught the unregistered type stat-card four times, in the flagship example, for as long as that example existed. Leaving this gate pointed away from a tree its two siblings now read would rebuild that geometry deliberately, in the same gate family. And UI_IMPROVEMENT_PROPOSAL.md is a proposal about console UI shape, i.e. the file in that tree most likely to grow the first type literal.

⛔ This is not a precedent for widening onto any other unscanned tree, and no allowlist mechanism is built — the population is three files.

Scope statement, not scope expansion

The ruling asked for the ownership of the unscanned markdown to be written into the gate headers as a statement of what the roots are. The full ownership map lives once in check-doc-snippet-types.mjs beside UNGATED_DOCS (widest surface, holds the ledger); the other two headers state their own roots and point at it.

Measured for that statement rather than inherited: the unscanned population is 114 files excluding the ephemeral .changeset/ — not the "roughly 50" the ruling's prose carries — the largest groups being non-README .md under packages/** (54), docs/** (17) and the published skills/objectui/** (16). The header gives the re-derivation command rather than relying on that number, matching the fix this repo already applied to UNGATED_DOCS's own header.

No gate is widened onto skills/**. It is named in the map as unclaimed, which is the opposite of a claim: a governed, published surface whose coverage is a decision for whoever owns it, never a side effect of a root move. Writing an unscanned tree down is what keeps it a known debt rather than #5174's "neither covered NOR declared ungated".

Pins added

scripts/__tests__/check-doc-fence-languages.test.ts gains three, in the shape #7115 established and check-doc-component-types' header demands of anything added later:

  • all three gates carry the same APP_DOCS constant (mirrors the existing ROOT_PAGES tri-gate pin);
  • the three guides are really in the walk by name — the deep-equality assertion does not cover this, since both walks could drop the tree together and stay equal, which is exactly the state this card was filed about;
  • the walk takes one app-directory level and does not descend into apps/site/app/docs, a Next.js route directory holding .tsx route files.

Verification

Run from the repo root at eba0a9b7a, tree clean, exit codes captured by redirect before any pipe:

  • pnpm check:doc-fences0228 document(s), baseline unchanged at 80 files / 90 blocks
  • pnpm check:doc-snippets0421 of 421 block(s) judged, 0 failed
  • pnpm check:doc-types0Every documented component type is registered.
  • pnpm check:control-bytes0 — 6131 tracked text files
  • pnpm exec vitest run over check-doc-fence-languages, check-doc-snippet-types, check-doc-component-types plus every other test that names these scripts (check-pre-install-import-graph, one-authority-per-exported-name-6273, known-schema-types-derivation-5115, report-namespace-agreement-6416) → 7 files, 196 tests, all passing
  • pnpm lint (full repo, not narrowed) → 0 — 47/47 tasks, 0 errors

The three gate workflows carry nopaths filter by deliberate design — their own tests fail if one is added — so the moved roots are exercised on every pull request, including the docs-only shape that would otherwise skip them.

Changeset

Empty-frontmatter changeset: these are gate scan roots, and scripts/** publishes nothing. skip-changeset is not used — ci-cd-pipeline-doc.test.ts pins it as a phantom label in this repository. Precedent followed: .changeset/7086-blockquoted-fence-collector.md.

Out of scope

🤖 Generated with Claude Code

https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW


Generated by Claude Code

The three documentation gates all rooted their walk at `content/docs` (plus,
for two of them, the package READMEs and the root `README.md`). None descended
into `apps/`, so `apps/console/docs/**` — the console's operator and deployment
guides — was read by no documentation gate at all. The only check whose surface
contained those files was `check:control-bytes`, which enumerates `git ls-files`
and therefore covers every tracked text file: they were checked for control
bytes and for nothing else.
`check:doc-fences` and `check:doc-snippets` move together because
`check-doc-fence-languages.test.ts` pins their document lists deep-equal.
`check:doc-types` joins them so that the tree is not left in the split-surface
geometry objectui#7115 was filed about, where a file fell between two gates'
differing roots and was read by neither.
No `UNGATED_DOCS` entry is added and no allowlist mechanism is built; both
shrink-only ledgers are byte-identical to `main`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
…#6600)
The first draft of the ownership map stated the unscanned markdown population
as 114 files. The re-derivation command the same block ships answers 111, at
both `origin/main` and this branch: the 114 counted the three
`apps/<app>/docs/` guides that this very pull request brings under the gates.
Rather than correct 114 to 111, this removes every hand-copied count from the
block — the total and the per-tree ones alike. A number in a header drifts from
the tree and nothing fails when it does, which is objectui#7448 exactly, and
which `UNGATED_DOCS`'s own header already records happening to its `12 .mdx
pages and 32 package READMEs` sentence. The trees are now named in descending
order of size, which is the ownership statement the ruling asked for, and the
command is left as the durable answer to both "how many" and "which".
Comment-only: no code line changes, and all three gates report identical
numbers before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 06:08
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit fda1247Sep 3, 2026
31 checks passed
@os-sales
os-sales deleted the claude/issue-6600-doc-gate-scan-roots branch September 3, 2026 06:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-sales@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

feat(scripts): point the three doc gates at apps/*/docs (#6600) - #7449

Merged
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots
Sep 3, 2026
Merged

feat(scripts): point the three doc gates at apps/*/docs (#6600)#7449
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes#6600

Step 2 of maintainer ruling D on #6600 (2026-09-02, verbatim 「同意」, recorded by the director seat). Step 1 was the content change, landed as PR #7425 (39af82f).

The three documentation gates all rooted their walk at content/docs — plus, for two of them, packages/NAME/README.md and the root README.md. None descended into apps/, so apps/console/docs/** was read by no documentation gate at all. The only check whose surface contained those files was check:control-bytes, which enumerates git ls-files and therefore covers every tracked text file: the console's operator guides were checked for control bytes and for nothing else.

check:doc-fences and check:doc-snippets move together because check-doc-fence-languages.test.ts pins their document lists deep-equal — that coupling is mechanical, not a convention. check:doc-types joining was the one judgement the ruling delegated to this lane; the reasoning is below and is written into that gate's header.

What moved

gatebeforeafter
check:doc-fences — documents225228
check:doc-snippets — documents225228
check:doc-snippets — covered / ungated193 / 32196 / 32
check:doc-snippets — covered blocks563577
check:doc-snippets — compiled / declared fragments417 / 146421 / 156
check:doc-types — doc files186189
check:doc-types — code blocks10901115
check:doc-typestype literals922922

The acceptance criterion the ruling set, met verbatim: check:doc-snippets judges the apps/*/docs/** blocks and reports 0 failed (421 of 421 block(s) judged, 0 failed), with the fragment blocks counted as declared fragments (+10) rather than skipped files; no UNGATED_DOCS growth (32 → 32); the three gate headers state their scope and agree with their roots.

UNGATED_DOCS and the fences shrink-only baseline are byte-identical to main, asserted rather than eyeballed — same SHA-256 for the extracted block in both cases, and KNOWN_UNHIGHLIGHTED_TS_FENCES still reports the same 80 files / 90 blocks.

⚠️ Three of the four newly-compiled blocks are green about nothing — #7426

This is stated here rather than absorbed into the green, because this card exists precisely because a gate suite reports green over a file it never opened, and shipping a gate that reports green over blocks it cannot fail is the same defect wearing the fix's clothes.

Of the 4 blocks that become live-compiled in this tree, 3 are design-token lists under "Visual Design Specs" in apps/console/docs/UI_IMPROVEMENT_PROPOSAL.md (Color Palette, Typography, Spacing). They compile only because background: "bg-background" parses as a labelled statement whose body is a string-literal expression — syntactically legal, semantically empty. So three quarters of this tree's new semantic coverage is vacuous.

That is not a prediction. It is demonstrated in leg A2 of the ablation below: replacing background: "bg-background" with background: "ABLATION_6600_this_is_not_typescript_at_all" leaves check:doc-snippetsgreen (exit 0), while the same class of defect in the one genuine block turns it red.

#7426 remains open and is the right home for it — re-fencing is a document-shape change and that card records the fence target (plain text vs. a markdown table) as undecided, which makes it domain:ui's judgement call. ⛔ The three blocks were deliberately not marked doc-snippet: fragment: that marker asserts a block cannot compile, which is false here, and it would drop them from real coverage. Out of scope for this PR: #7426.

Non-vacuity — each moved gate proven to actually judge the new population

A root that moves while the walk still misses the files passes every green check and buys nothing. Each leg plants a deliberate defect in an apps/*/docs/** block, proves the mutation reached disk before the run (HEAD blob hash != worktree blob hash, plus a marker grep -c), then restores with git checkout HEAD -- ABSOLUTE_PATH and proves the restore (blob == HEAD blob and empty git diff HEAD). The script carries trap … EXIT INT TERM. The mutated subject is markdown the gate reads by path at runtime, not an artifact resolved through a package dist/, so no rebuild leg applies; the packages the snippet gate compiles against were unchanged and already built.

legplanted defectgateexitverdict line
A1const ABLATION_6600: number = "not a number"; in the genuine compiled blockcheck:doc-snippets1TS2322 at UI_IMPROVEMENT_PROPOSAL.md:174; 421 of 421 block(s) judged, 1 failed
A2same class of defect in a design-token blockcheck:doc-snippets0421 of 421 block(s) judged, 0 failed — the #7426 vacuity, demonstrated
Binterface ABLATION_6600 { under a bare fence in deployment.mdcheck:doc-fences1deployment.md:21 — bare fence, no info string, body interface ABLATION_6600 {
C{ "type": "ablation-6600-unregistered" } in a fenced blockcheck:doc-types1[unregistered-doc-type] type 'ablation-6600-unregistered' (tsx)

Final tree state after all four legs: git diff HEADempty.

Why check:doc-types joined, at zero present yield

Stated plainly because the alternative reading — that a widened scope was justified by a discovery — is false: the three files carry 0type literals today, and the count is unchanged at 922 after the move. This gate finds nothing on the day it lands.

The argument for joining anyway is the split-surface defect this exact gate was already burned by once. In #7115, check-doc-component-types walked content/docs; check-doc-snippet-types walked content/docs plus the package READMEs; the root README.md fell between the two and was read by neither — and it taught the unregistered type stat-card four times, in the flagship example, for as long as that example existed. Leaving this gate pointed away from a tree its two siblings now read would rebuild that geometry deliberately, in the same gate family. And UI_IMPROVEMENT_PROPOSAL.md is a proposal about console UI shape, i.e. the file in that tree most likely to grow the first type literal.

⛔ This is not a precedent for widening onto any other unscanned tree, and no allowlist mechanism is built — the population is three files.

Scope statement, not scope expansion

The ruling asked for the ownership of the unscanned markdown to be written into the gate headers as a statement of what the roots are. The full ownership map lives once in check-doc-snippet-types.mjs beside UNGATED_DOCS (widest surface, holds the ledger); the other two headers state their own roots and point at it.

Measured for that statement rather than inherited: the unscanned population is 114 files excluding the ephemeral .changeset/ — not the "roughly 50" the ruling's prose carries — the largest groups being non-README .md under packages/** (54), docs/** (17) and the published skills/objectui/** (16). The header gives the re-derivation command rather than relying on that number, matching the fix this repo already applied to UNGATED_DOCS's own header.

No gate is widened onto skills/**. It is named in the map as unclaimed, which is the opposite of a claim: a governed, published surface whose coverage is a decision for whoever owns it, never a side effect of a root move. Writing an unscanned tree down is what keeps it a known debt rather than #5174's "neither covered NOR declared ungated".

Pins added

scripts/__tests__/check-doc-fence-languages.test.ts gains three, in the shape #7115 established and check-doc-component-types' header demands of anything added later:

  • all three gates carry the same APP_DOCS constant (mirrors the existing ROOT_PAGES tri-gate pin);
  • the three guides are really in the walk by name — the deep-equality assertion does not cover this, since both walks could drop the tree together and stay equal, which is exactly the state this card was filed about;
  • the walk takes one app-directory level and does not descend into apps/site/app/docs, a Next.js route directory holding .tsx route files.

Verification

Run from the repo root at eba0a9b7a, tree clean, exit codes captured by redirect before any pipe:

  • pnpm check:doc-fences0228 document(s), baseline unchanged at 80 files / 90 blocks
  • pnpm check:doc-snippets0421 of 421 block(s) judged, 0 failed
  • pnpm check:doc-types0Every documented component type is registered.
  • pnpm check:control-bytes0 — 6131 tracked text files
  • pnpm exec vitest run over check-doc-fence-languages, check-doc-snippet-types, check-doc-component-types plus every other test that names these scripts (check-pre-install-import-graph, one-authority-per-exported-name-6273, known-schema-types-derivation-5115, report-namespace-agreement-6416) → 7 files, 196 tests, all passing
  • pnpm lint (full repo, not narrowed) → 0 — 47/47 tasks, 0 errors

The three gate workflows carry nopaths filter by deliberate design — their own tests fail if one is added — so the moved roots are exercised on every pull request, including the docs-only shape that would otherwise skip them.

Changeset

Empty-frontmatter changeset: these are gate scan roots, and scripts/** publishes nothing. skip-changeset is not used — ci-cd-pipeline-doc.test.ts pins it as a phantom label in this repository. Precedent followed: .changeset/7086-blockquoted-fence-collector.md.

Out of scope

🤖 Generated with Claude Code

https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW


Generated by Claude Code

The three documentation gates all rooted their walk at `content/docs` (plus,
for two of them, the package READMEs and the root `README.md`). None descended
into `apps/`, so `apps/console/docs/**` — the console's operator and deployment
guides — was read by no documentation gate at all. The only check whose surface
contained those files was `check:control-bytes`, which enumerates `git ls-files`
and therefore covers every tracked text file: they were checked for control
bytes and for nothing else.
`check:doc-fences` and `check:doc-snippets` move together because
`check-doc-fence-languages.test.ts` pins their document lists deep-equal.
`check:doc-types` joins them so that the tree is not left in the split-surface
geometry objectui#7115 was filed about, where a file fell between two gates'
differing roots and was read by neither.
No `UNGATED_DOCS` entry is added and no allowlist mechanism is built; both
shrink-only ledgers are byte-identical to `main`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
…#6600)
The first draft of the ownership map stated the unscanned markdown population
as 114 files. The re-derivation command the same block ships answers 111, at
both `origin/main` and this branch: the 114 counted the three
`apps/<app>/docs/` guides that this very pull request brings under the gates.
Rather than correct 114 to 111, this removes every hand-copied count from the
block — the total and the per-tree ones alike. A number in a header drifts from
the tree and nothing fails when it does, which is objectui#7448 exactly, and
which `UNGATED_DOCS`'s own header already records happening to its `12 .mdx
pages and 32 package READMEs` sentence. The trees are now named in descending
order of size, which is the ownership statement the ruling asked for, and the
command is left as the durable answer to both "how many" and "which".
Comment-only: no code line changes, and all three gates report identical
numbers before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 06:08
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit fda1247Sep 3, 2026
31 checks passed
@os-sales
os-sales deleted the claude/issue-6600-doc-gate-scan-roots branch September 3, 2026 06:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-sales@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

feat(scripts): point the three doc gates at apps/*/docs (#6600) - #7449

Merged
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots
Sep 3, 2026
Merged

feat(scripts): point the three doc gates at apps/*/docs (#6600)#7449
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes#6600

Step 2 of maintainer ruling D on #6600 (2026-09-02, verbatim 「同意」, recorded by the director seat). Step 1 was the content change, landed as PR #7425 (39af82f).

The three documentation gates all rooted their walk at content/docs — plus, for two of them, packages/NAME/README.md and the root README.md. None descended into apps/, so apps/console/docs/** was read by no documentation gate at all. The only check whose surface contained those files was check:control-bytes, which enumerates git ls-files and therefore covers every tracked text file: the console's operator guides were checked for control bytes and for nothing else.

check:doc-fences and check:doc-snippets move together because check-doc-fence-languages.test.ts pins their document lists deep-equal — that coupling is mechanical, not a convention. check:doc-types joining was the one judgement the ruling delegated to this lane; the reasoning is below and is written into that gate's header.

What moved

gatebeforeafter
check:doc-fences — documents225228
check:doc-snippets — documents225228
check:doc-snippets — covered / ungated193 / 32196 / 32
check:doc-snippets — covered blocks563577
check:doc-snippets — compiled / declared fragments417 / 146421 / 156
check:doc-types — doc files186189
check:doc-types — code blocks10901115
check:doc-typestype literals922922

The acceptance criterion the ruling set, met verbatim: check:doc-snippets judges the apps/*/docs/** blocks and reports 0 failed (421 of 421 block(s) judged, 0 failed), with the fragment blocks counted as declared fragments (+10) rather than skipped files; no UNGATED_DOCS growth (32 → 32); the three gate headers state their scope and agree with their roots.

UNGATED_DOCS and the fences shrink-only baseline are byte-identical to main, asserted rather than eyeballed — same SHA-256 for the extracted block in both cases, and KNOWN_UNHIGHLIGHTED_TS_FENCES still reports the same 80 files / 90 blocks.

⚠️ Three of the four newly-compiled blocks are green about nothing — #7426

This is stated here rather than absorbed into the green, because this card exists precisely because a gate suite reports green over a file it never opened, and shipping a gate that reports green over blocks it cannot fail is the same defect wearing the fix's clothes.

Of the 4 blocks that become live-compiled in this tree, 3 are design-token lists under "Visual Design Specs" in apps/console/docs/UI_IMPROVEMENT_PROPOSAL.md (Color Palette, Typography, Spacing). They compile only because background: "bg-background" parses as a labelled statement whose body is a string-literal expression — syntactically legal, semantically empty. So three quarters of this tree's new semantic coverage is vacuous.

That is not a prediction. It is demonstrated in leg A2 of the ablation below: replacing background: "bg-background" with background: "ABLATION_6600_this_is_not_typescript_at_all" leaves check:doc-snippetsgreen (exit 0), while the same class of defect in the one genuine block turns it red.

#7426 remains open and is the right home for it — re-fencing is a document-shape change and that card records the fence target (plain text vs. a markdown table) as undecided, which makes it domain:ui's judgement call. ⛔ The three blocks were deliberately not marked doc-snippet: fragment: that marker asserts a block cannot compile, which is false here, and it would drop them from real coverage. Out of scope for this PR: #7426.

Non-vacuity — each moved gate proven to actually judge the new population

A root that moves while the walk still misses the files passes every green check and buys nothing. Each leg plants a deliberate defect in an apps/*/docs/** block, proves the mutation reached disk before the run (HEAD blob hash != worktree blob hash, plus a marker grep -c), then restores with git checkout HEAD -- ABSOLUTE_PATH and proves the restore (blob == HEAD blob and empty git diff HEAD). The script carries trap … EXIT INT TERM. The mutated subject is markdown the gate reads by path at runtime, not an artifact resolved through a package dist/, so no rebuild leg applies; the packages the snippet gate compiles against were unchanged and already built.

legplanted defectgateexitverdict line
A1const ABLATION_6600: number = "not a number"; in the genuine compiled blockcheck:doc-snippets1TS2322 at UI_IMPROVEMENT_PROPOSAL.md:174; 421 of 421 block(s) judged, 1 failed
A2same class of defect in a design-token blockcheck:doc-snippets0421 of 421 block(s) judged, 0 failed — the #7426 vacuity, demonstrated
Binterface ABLATION_6600 { under a bare fence in deployment.mdcheck:doc-fences1deployment.md:21 — bare fence, no info string, body interface ABLATION_6600 {
C{ "type": "ablation-6600-unregistered" } in a fenced blockcheck:doc-types1[unregistered-doc-type] type 'ablation-6600-unregistered' (tsx)

Final tree state after all four legs: git diff HEADempty.

Why check:doc-types joined, at zero present yield

Stated plainly because the alternative reading — that a widened scope was justified by a discovery — is false: the three files carry 0type literals today, and the count is unchanged at 922 after the move. This gate finds nothing on the day it lands.

The argument for joining anyway is the split-surface defect this exact gate was already burned by once. In #7115, check-doc-component-types walked content/docs; check-doc-snippet-types walked content/docs plus the package READMEs; the root README.md fell between the two and was read by neither — and it taught the unregistered type stat-card four times, in the flagship example, for as long as that example existed. Leaving this gate pointed away from a tree its two siblings now read would rebuild that geometry deliberately, in the same gate family. And UI_IMPROVEMENT_PROPOSAL.md is a proposal about console UI shape, i.e. the file in that tree most likely to grow the first type literal.

⛔ This is not a precedent for widening onto any other unscanned tree, and no allowlist mechanism is built — the population is three files.

Scope statement, not scope expansion

The ruling asked for the ownership of the unscanned markdown to be written into the gate headers as a statement of what the roots are. The full ownership map lives once in check-doc-snippet-types.mjs beside UNGATED_DOCS (widest surface, holds the ledger); the other two headers state their own roots and point at it.

Measured for that statement rather than inherited: the unscanned population is 114 files excluding the ephemeral .changeset/ — not the "roughly 50" the ruling's prose carries — the largest groups being non-README .md under packages/** (54), docs/** (17) and the published skills/objectui/** (16). The header gives the re-derivation command rather than relying on that number, matching the fix this repo already applied to UNGATED_DOCS's own header.

No gate is widened onto skills/**. It is named in the map as unclaimed, which is the opposite of a claim: a governed, published surface whose coverage is a decision for whoever owns it, never a side effect of a root move. Writing an unscanned tree down is what keeps it a known debt rather than #5174's "neither covered NOR declared ungated".

Pins added

scripts/__tests__/check-doc-fence-languages.test.ts gains three, in the shape #7115 established and check-doc-component-types' header demands of anything added later:

  • all three gates carry the same APP_DOCS constant (mirrors the existing ROOT_PAGES tri-gate pin);
  • the three guides are really in the walk by name — the deep-equality assertion does not cover this, since both walks could drop the tree together and stay equal, which is exactly the state this card was filed about;
  • the walk takes one app-directory level and does not descend into apps/site/app/docs, a Next.js route directory holding .tsx route files.

Verification

Run from the repo root at eba0a9b7a, tree clean, exit codes captured by redirect before any pipe:

  • pnpm check:doc-fences0228 document(s), baseline unchanged at 80 files / 90 blocks
  • pnpm check:doc-snippets0421 of 421 block(s) judged, 0 failed
  • pnpm check:doc-types0Every documented component type is registered.
  • pnpm check:control-bytes0 — 6131 tracked text files
  • pnpm exec vitest run over check-doc-fence-languages, check-doc-snippet-types, check-doc-component-types plus every other test that names these scripts (check-pre-install-import-graph, one-authority-per-exported-name-6273, known-schema-types-derivation-5115, report-namespace-agreement-6416) → 7 files, 196 tests, all passing
  • pnpm lint (full repo, not narrowed) → 0 — 47/47 tasks, 0 errors

The three gate workflows carry nopaths filter by deliberate design — their own tests fail if one is added — so the moved roots are exercised on every pull request, including the docs-only shape that would otherwise skip them.

Changeset

Empty-frontmatter changeset: these are gate scan roots, and scripts/** publishes nothing. skip-changeset is not used — ci-cd-pipeline-doc.test.ts pins it as a phantom label in this repository. Precedent followed: .changeset/7086-blockquoted-fence-collector.md.

Out of scope

🤖 Generated with Claude Code

https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW


Generated by Claude Code

The three documentation gates all rooted their walk at `content/docs` (plus,
for two of them, the package READMEs and the root `README.md`). None descended
into `apps/`, so `apps/console/docs/**` — the console's operator and deployment
guides — was read by no documentation gate at all. The only check whose surface
contained those files was `check:control-bytes`, which enumerates `git ls-files`
and therefore covers every tracked text file: they were checked for control
bytes and for nothing else.
`check:doc-fences` and `check:doc-snippets` move together because
`check-doc-fence-languages.test.ts` pins their document lists deep-equal.
`check:doc-types` joins them so that the tree is not left in the split-surface
geometry objectui#7115 was filed about, where a file fell between two gates'
differing roots and was read by neither.
No `UNGATED_DOCS` entry is added and no allowlist mechanism is built; both
shrink-only ledgers are byte-identical to `main`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
…#6600)
The first draft of the ownership map stated the unscanned markdown population
as 114 files. The re-derivation command the same block ships answers 111, at
both `origin/main` and this branch: the 114 counted the three
`apps/<app>/docs/` guides that this very pull request brings under the gates.
Rather than correct 114 to 111, this removes every hand-copied count from the
block — the total and the per-tree ones alike. A number in a header drifts from
the tree and nothing fails when it does, which is objectui#7448 exactly, and
which `UNGATED_DOCS`'s own header already records happening to its `12 .mdx
pages and 32 package READMEs` sentence. The trees are now named in descending
order of size, which is the ownership statement the ruling asked for, and the
command is left as the durable answer to both "how many" and "which".
Comment-only: no code line changes, and all three gates report identical
numbers before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 06:08
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit fda1247Sep 3, 2026
31 checks passed
@os-sales
os-sales deleted the claude/issue-6600-doc-gate-scan-roots branch September 3, 2026 06:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-sales@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

feat(scripts): point the three doc gates at apps/*/docs (#6600) - #7449

Merged
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots
Sep 3, 2026
Merged

feat(scripts): point the three doc gates at apps/*/docs (#6600)#7449
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes#6600

Step 2 of maintainer ruling D on #6600 (2026-09-02, verbatim 「同意」, recorded by the director seat). Step 1 was the content change, landed as PR #7425 (39af82f).

The three documentation gates all rooted their walk at content/docs — plus, for two of them, packages/NAME/README.md and the root README.md. None descended into apps/, so apps/console/docs/** was read by no documentation gate at all. The only check whose surface contained those files was check:control-bytes, which enumerates git ls-files and therefore covers every tracked text file: the console's operator guides were checked for control bytes and for nothing else.

check:doc-fences and check:doc-snippets move together because check-doc-fence-languages.test.ts pins their document lists deep-equal — that coupling is mechanical, not a convention. check:doc-types joining was the one judgement the ruling delegated to this lane; the reasoning is below and is written into that gate's header.

What moved

gatebeforeafter
check:doc-fences — documents225228
check:doc-snippets — documents225228
check:doc-snippets — covered / ungated193 / 32196 / 32
check:doc-snippets — covered blocks563577
check:doc-snippets — compiled / declared fragments417 / 146421 / 156
check:doc-types — doc files186189
check:doc-types — code blocks10901115
check:doc-typestype literals922922

The acceptance criterion the ruling set, met verbatim: check:doc-snippets judges the apps/*/docs/** blocks and reports 0 failed (421 of 421 block(s) judged, 0 failed), with the fragment blocks counted as declared fragments (+10) rather than skipped files; no UNGATED_DOCS growth (32 → 32); the three gate headers state their scope and agree with their roots.

UNGATED_DOCS and the fences shrink-only baseline are byte-identical to main, asserted rather than eyeballed — same SHA-256 for the extracted block in both cases, and KNOWN_UNHIGHLIGHTED_TS_FENCES still reports the same 80 files / 90 blocks.

⚠️ Three of the four newly-compiled blocks are green about nothing — #7426

This is stated here rather than absorbed into the green, because this card exists precisely because a gate suite reports green over a file it never opened, and shipping a gate that reports green over blocks it cannot fail is the same defect wearing the fix's clothes.

Of the 4 blocks that become live-compiled in this tree, 3 are design-token lists under "Visual Design Specs" in apps/console/docs/UI_IMPROVEMENT_PROPOSAL.md (Color Palette, Typography, Spacing). They compile only because background: "bg-background" parses as a labelled statement whose body is a string-literal expression — syntactically legal, semantically empty. So three quarters of this tree's new semantic coverage is vacuous.

That is not a prediction. It is demonstrated in leg A2 of the ablation below: replacing background: "bg-background" with background: "ABLATION_6600_this_is_not_typescript_at_all" leaves check:doc-snippetsgreen (exit 0), while the same class of defect in the one genuine block turns it red.

#7426 remains open and is the right home for it — re-fencing is a document-shape change and that card records the fence target (plain text vs. a markdown table) as undecided, which makes it domain:ui's judgement call. ⛔ The three blocks were deliberately not marked doc-snippet: fragment: that marker asserts a block cannot compile, which is false here, and it would drop them from real coverage. Out of scope for this PR: #7426.

Non-vacuity — each moved gate proven to actually judge the new population

A root that moves while the walk still misses the files passes every green check and buys nothing. Each leg plants a deliberate defect in an apps/*/docs/** block, proves the mutation reached disk before the run (HEAD blob hash != worktree blob hash, plus a marker grep -c), then restores with git checkout HEAD -- ABSOLUTE_PATH and proves the restore (blob == HEAD blob and empty git diff HEAD). The script carries trap … EXIT INT TERM. The mutated subject is markdown the gate reads by path at runtime, not an artifact resolved through a package dist/, so no rebuild leg applies; the packages the snippet gate compiles against were unchanged and already built.

legplanted defectgateexitverdict line
A1const ABLATION_6600: number = "not a number"; in the genuine compiled blockcheck:doc-snippets1TS2322 at UI_IMPROVEMENT_PROPOSAL.md:174; 421 of 421 block(s) judged, 1 failed
A2same class of defect in a design-token blockcheck:doc-snippets0421 of 421 block(s) judged, 0 failed — the #7426 vacuity, demonstrated
Binterface ABLATION_6600 { under a bare fence in deployment.mdcheck:doc-fences1deployment.md:21 — bare fence, no info string, body interface ABLATION_6600 {
C{ "type": "ablation-6600-unregistered" } in a fenced blockcheck:doc-types1[unregistered-doc-type] type 'ablation-6600-unregistered' (tsx)

Final tree state after all four legs: git diff HEADempty.

Why check:doc-types joined, at zero present yield

Stated plainly because the alternative reading — that a widened scope was justified by a discovery — is false: the three files carry 0type literals today, and the count is unchanged at 922 after the move. This gate finds nothing on the day it lands.

The argument for joining anyway is the split-surface defect this exact gate was already burned by once. In #7115, check-doc-component-types walked content/docs; check-doc-snippet-types walked content/docs plus the package READMEs; the root README.md fell between the two and was read by neither — and it taught the unregistered type stat-card four times, in the flagship example, for as long as that example existed. Leaving this gate pointed away from a tree its two siblings now read would rebuild that geometry deliberately, in the same gate family. And UI_IMPROVEMENT_PROPOSAL.md is a proposal about console UI shape, i.e. the file in that tree most likely to grow the first type literal.

⛔ This is not a precedent for widening onto any other unscanned tree, and no allowlist mechanism is built — the population is three files.

Scope statement, not scope expansion

The ruling asked for the ownership of the unscanned markdown to be written into the gate headers as a statement of what the roots are. The full ownership map lives once in check-doc-snippet-types.mjs beside UNGATED_DOCS (widest surface, holds the ledger); the other two headers state their own roots and point at it.

Measured for that statement rather than inherited: the unscanned population is 114 files excluding the ephemeral .changeset/ — not the "roughly 50" the ruling's prose carries — the largest groups being non-README .md under packages/** (54), docs/** (17) and the published skills/objectui/** (16). The header gives the re-derivation command rather than relying on that number, matching the fix this repo already applied to UNGATED_DOCS's own header.

No gate is widened onto skills/**. It is named in the map as unclaimed, which is the opposite of a claim: a governed, published surface whose coverage is a decision for whoever owns it, never a side effect of a root move. Writing an unscanned tree down is what keeps it a known debt rather than #5174's "neither covered NOR declared ungated".

Pins added

scripts/__tests__/check-doc-fence-languages.test.ts gains three, in the shape #7115 established and check-doc-component-types' header demands of anything added later:

  • all three gates carry the same APP_DOCS constant (mirrors the existing ROOT_PAGES tri-gate pin);
  • the three guides are really in the walk by name — the deep-equality assertion does not cover this, since both walks could drop the tree together and stay equal, which is exactly the state this card was filed about;
  • the walk takes one app-directory level and does not descend into apps/site/app/docs, a Next.js route directory holding .tsx route files.

Verification

Run from the repo root at eba0a9b7a, tree clean, exit codes captured by redirect before any pipe:

  • pnpm check:doc-fences0228 document(s), baseline unchanged at 80 files / 90 blocks
  • pnpm check:doc-snippets0421 of 421 block(s) judged, 0 failed
  • pnpm check:doc-types0Every documented component type is registered.
  • pnpm check:control-bytes0 — 6131 tracked text files
  • pnpm exec vitest run over check-doc-fence-languages, check-doc-snippet-types, check-doc-component-types plus every other test that names these scripts (check-pre-install-import-graph, one-authority-per-exported-name-6273, known-schema-types-derivation-5115, report-namespace-agreement-6416) → 7 files, 196 tests, all passing
  • pnpm lint (full repo, not narrowed) → 0 — 47/47 tasks, 0 errors

The three gate workflows carry nopaths filter by deliberate design — their own tests fail if one is added — so the moved roots are exercised on every pull request, including the docs-only shape that would otherwise skip them.

Changeset

Empty-frontmatter changeset: these are gate scan roots, and scripts/** publishes nothing. skip-changeset is not used — ci-cd-pipeline-doc.test.ts pins it as a phantom label in this repository. Precedent followed: .changeset/7086-blockquoted-fence-collector.md.

Out of scope

🤖 Generated with Claude Code

https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW


Generated by Claude Code

The three documentation gates all rooted their walk at `content/docs` (plus,
for two of them, the package READMEs and the root `README.md`). None descended
into `apps/`, so `apps/console/docs/**` — the console's operator and deployment
guides — was read by no documentation gate at all. The only check whose surface
contained those files was `check:control-bytes`, which enumerates `git ls-files`
and therefore covers every tracked text file: they were checked for control
bytes and for nothing else.
`check:doc-fences` and `check:doc-snippets` move together because
`check-doc-fence-languages.test.ts` pins their document lists deep-equal.
`check:doc-types` joins them so that the tree is not left in the split-surface
geometry objectui#7115 was filed about, where a file fell between two gates'
differing roots and was read by neither.
No `UNGATED_DOCS` entry is added and no allowlist mechanism is built; both
shrink-only ledgers are byte-identical to `main`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
…#6600)
The first draft of the ownership map stated the unscanned markdown population
as 114 files. The re-derivation command the same block ships answers 111, at
both `origin/main` and this branch: the 114 counted the three
`apps/<app>/docs/` guides that this very pull request brings under the gates.
Rather than correct 114 to 111, this removes every hand-copied count from the
block — the total and the per-tree ones alike. A number in a header drifts from
the tree and nothing fails when it does, which is objectui#7448 exactly, and
which `UNGATED_DOCS`'s own header already records happening to its `12 .mdx
pages and 32 package READMEs` sentence. The trees are now named in descending
order of size, which is the ownership statement the ruling asked for, and the
command is left as the durable answer to both "how many" and "which".
Comment-only: no code line changes, and all three gates report identical
numbers before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 06:08
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit fda1247Sep 3, 2026
31 checks passed
@os-sales
os-sales deleted the claude/issue-6600-doc-gate-scan-roots branch September 3, 2026 06:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-sales@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

feat(scripts): point the three doc gates at apps/*/docs (#6600) - #7449

Merged
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots
Sep 3, 2026
Merged

feat(scripts): point the three doc gates at apps/*/docs (#6600)#7449
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes#6600

Step 2 of maintainer ruling D on #6600 (2026-09-02, verbatim 「同意」, recorded by the director seat). Step 1 was the content change, landed as PR #7425 (39af82f).

The three documentation gates all rooted their walk at content/docs — plus, for two of them, packages/NAME/README.md and the root README.md. None descended into apps/, so apps/console/docs/** was read by no documentation gate at all. The only check whose surface contained those files was check:control-bytes, which enumerates git ls-files and therefore covers every tracked text file: the console's operator guides were checked for control bytes and for nothing else.

check:doc-fences and check:doc-snippets move together because check-doc-fence-languages.test.ts pins their document lists deep-equal — that coupling is mechanical, not a convention. check:doc-types joining was the one judgement the ruling delegated to this lane; the reasoning is below and is written into that gate's header.

What moved

gatebeforeafter
check:doc-fences — documents225228
check:doc-snippets — documents225228
check:doc-snippets — covered / ungated193 / 32196 / 32
check:doc-snippets — covered blocks563577
check:doc-snippets — compiled / declared fragments417 / 146421 / 156
check:doc-types — doc files186189
check:doc-types — code blocks10901115
check:doc-typestype literals922922

The acceptance criterion the ruling set, met verbatim: check:doc-snippets judges the apps/*/docs/** blocks and reports 0 failed (421 of 421 block(s) judged, 0 failed), with the fragment blocks counted as declared fragments (+10) rather than skipped files; no UNGATED_DOCS growth (32 → 32); the three gate headers state their scope and agree with their roots.

UNGATED_DOCS and the fences shrink-only baseline are byte-identical to main, asserted rather than eyeballed — same SHA-256 for the extracted block in both cases, and KNOWN_UNHIGHLIGHTED_TS_FENCES still reports the same 80 files / 90 blocks.

⚠️ Three of the four newly-compiled blocks are green about nothing — #7426

This is stated here rather than absorbed into the green, because this card exists precisely because a gate suite reports green over a file it never opened, and shipping a gate that reports green over blocks it cannot fail is the same defect wearing the fix's clothes.

Of the 4 blocks that become live-compiled in this tree, 3 are design-token lists under "Visual Design Specs" in apps/console/docs/UI_IMPROVEMENT_PROPOSAL.md (Color Palette, Typography, Spacing). They compile only because background: "bg-background" parses as a labelled statement whose body is a string-literal expression — syntactically legal, semantically empty. So three quarters of this tree's new semantic coverage is vacuous.

That is not a prediction. It is demonstrated in leg A2 of the ablation below: replacing background: "bg-background" with background: "ABLATION_6600_this_is_not_typescript_at_all" leaves check:doc-snippetsgreen (exit 0), while the same class of defect in the one genuine block turns it red.

#7426 remains open and is the right home for it — re-fencing is a document-shape change and that card records the fence target (plain text vs. a markdown table) as undecided, which makes it domain:ui's judgement call. ⛔ The three blocks were deliberately not marked doc-snippet: fragment: that marker asserts a block cannot compile, which is false here, and it would drop them from real coverage. Out of scope for this PR: #7426.

Non-vacuity — each moved gate proven to actually judge the new population

A root that moves while the walk still misses the files passes every green check and buys nothing. Each leg plants a deliberate defect in an apps/*/docs/** block, proves the mutation reached disk before the run (HEAD blob hash != worktree blob hash, plus a marker grep -c), then restores with git checkout HEAD -- ABSOLUTE_PATH and proves the restore (blob == HEAD blob and empty git diff HEAD). The script carries trap … EXIT INT TERM. The mutated subject is markdown the gate reads by path at runtime, not an artifact resolved through a package dist/, so no rebuild leg applies; the packages the snippet gate compiles against were unchanged and already built.

legplanted defectgateexitverdict line
A1const ABLATION_6600: number = "not a number"; in the genuine compiled blockcheck:doc-snippets1TS2322 at UI_IMPROVEMENT_PROPOSAL.md:174; 421 of 421 block(s) judged, 1 failed
A2same class of defect in a design-token blockcheck:doc-snippets0421 of 421 block(s) judged, 0 failed — the #7426 vacuity, demonstrated
Binterface ABLATION_6600 { under a bare fence in deployment.mdcheck:doc-fences1deployment.md:21 — bare fence, no info string, body interface ABLATION_6600 {
C{ "type": "ablation-6600-unregistered" } in a fenced blockcheck:doc-types1[unregistered-doc-type] type 'ablation-6600-unregistered' (tsx)

Final tree state after all four legs: git diff HEADempty.

Why check:doc-types joined, at zero present yield

Stated plainly because the alternative reading — that a widened scope was justified by a discovery — is false: the three files carry 0type literals today, and the count is unchanged at 922 after the move. This gate finds nothing on the day it lands.

The argument for joining anyway is the split-surface defect this exact gate was already burned by once. In #7115, check-doc-component-types walked content/docs; check-doc-snippet-types walked content/docs plus the package READMEs; the root README.md fell between the two and was read by neither — and it taught the unregistered type stat-card four times, in the flagship example, for as long as that example existed. Leaving this gate pointed away from a tree its two siblings now read would rebuild that geometry deliberately, in the same gate family. And UI_IMPROVEMENT_PROPOSAL.md is a proposal about console UI shape, i.e. the file in that tree most likely to grow the first type literal.

⛔ This is not a precedent for widening onto any other unscanned tree, and no allowlist mechanism is built — the population is three files.

Scope statement, not scope expansion

The ruling asked for the ownership of the unscanned markdown to be written into the gate headers as a statement of what the roots are. The full ownership map lives once in check-doc-snippet-types.mjs beside UNGATED_DOCS (widest surface, holds the ledger); the other two headers state their own roots and point at it.

Measured for that statement rather than inherited: the unscanned population is 114 files excluding the ephemeral .changeset/ — not the "roughly 50" the ruling's prose carries — the largest groups being non-README .md under packages/** (54), docs/** (17) and the published skills/objectui/** (16). The header gives the re-derivation command rather than relying on that number, matching the fix this repo already applied to UNGATED_DOCS's own header.

No gate is widened onto skills/**. It is named in the map as unclaimed, which is the opposite of a claim: a governed, published surface whose coverage is a decision for whoever owns it, never a side effect of a root move. Writing an unscanned tree down is what keeps it a known debt rather than #5174's "neither covered NOR declared ungated".

Pins added

scripts/__tests__/check-doc-fence-languages.test.ts gains three, in the shape #7115 established and check-doc-component-types' header demands of anything added later:

  • all three gates carry the same APP_DOCS constant (mirrors the existing ROOT_PAGES tri-gate pin);
  • the three guides are really in the walk by name — the deep-equality assertion does not cover this, since both walks could drop the tree together and stay equal, which is exactly the state this card was filed about;
  • the walk takes one app-directory level and does not descend into apps/site/app/docs, a Next.js route directory holding .tsx route files.

Verification

Run from the repo root at eba0a9b7a, tree clean, exit codes captured by redirect before any pipe:

  • pnpm check:doc-fences0228 document(s), baseline unchanged at 80 files / 90 blocks
  • pnpm check:doc-snippets0421 of 421 block(s) judged, 0 failed
  • pnpm check:doc-types0Every documented component type is registered.
  • pnpm check:control-bytes0 — 6131 tracked text files
  • pnpm exec vitest run over check-doc-fence-languages, check-doc-snippet-types, check-doc-component-types plus every other test that names these scripts (check-pre-install-import-graph, one-authority-per-exported-name-6273, known-schema-types-derivation-5115, report-namespace-agreement-6416) → 7 files, 196 tests, all passing
  • pnpm lint (full repo, not narrowed) → 0 — 47/47 tasks, 0 errors

The three gate workflows carry nopaths filter by deliberate design — their own tests fail if one is added — so the moved roots are exercised on every pull request, including the docs-only shape that would otherwise skip them.

Changeset

Empty-frontmatter changeset: these are gate scan roots, and scripts/** publishes nothing. skip-changeset is not used — ci-cd-pipeline-doc.test.ts pins it as a phantom label in this repository. Precedent followed: .changeset/7086-blockquoted-fence-collector.md.

Out of scope

🤖 Generated with Claude Code

https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW


Generated by Claude Code

The three documentation gates all rooted their walk at `content/docs` (plus,
for two of them, the package READMEs and the root `README.md`). None descended
into `apps/`, so `apps/console/docs/**` — the console's operator and deployment
guides — was read by no documentation gate at all. The only check whose surface
contained those files was `check:control-bytes`, which enumerates `git ls-files`
and therefore covers every tracked text file: they were checked for control
bytes and for nothing else.
`check:doc-fences` and `check:doc-snippets` move together because
`check-doc-fence-languages.test.ts` pins their document lists deep-equal.
`check:doc-types` joins them so that the tree is not left in the split-surface
geometry objectui#7115 was filed about, where a file fell between two gates'
differing roots and was read by neither.
No `UNGATED_DOCS` entry is added and no allowlist mechanism is built; both
shrink-only ledgers are byte-identical to `main`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
…#6600)
The first draft of the ownership map stated the unscanned markdown population
as 114 files. The re-derivation command the same block ships answers 111, at
both `origin/main` and this branch: the 114 counted the three
`apps/<app>/docs/` guides that this very pull request brings under the gates.
Rather than correct 114 to 111, this removes every hand-copied count from the
block — the total and the per-tree ones alike. A number in a header drifts from
the tree and nothing fails when it does, which is objectui#7448 exactly, and
which `UNGATED_DOCS`'s own header already records happening to its `12 .mdx
pages and 32 package READMEs` sentence. The trees are now named in descending
order of size, which is the ownership statement the ruling asked for, and the
command is left as the durable answer to both "how many" and "which".
Comment-only: no code line changes, and all three gates report identical
numbers before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 06:08
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit fda1247Sep 3, 2026
31 checks passed
@os-sales
os-sales deleted the claude/issue-6600-doc-gate-scan-roots branch September 3, 2026 06:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-sales@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

feat(scripts): point the three doc gates at apps/*/docs (#6600) - #7449

Merged
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots
Sep 3, 2026
Merged

feat(scripts): point the three doc gates at apps/*/docs (#6600)#7449
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes#6600

Step 2 of maintainer ruling D on #6600 (2026-09-02, verbatim 「同意」, recorded by the director seat). Step 1 was the content change, landed as PR #7425 (39af82f).

The three documentation gates all rooted their walk at content/docs — plus, for two of them, packages/NAME/README.md and the root README.md. None descended into apps/, so apps/console/docs/** was read by no documentation gate at all. The only check whose surface contained those files was check:control-bytes, which enumerates git ls-files and therefore covers every tracked text file: the console's operator guides were checked for control bytes and for nothing else.

check:doc-fences and check:doc-snippets move together because check-doc-fence-languages.test.ts pins their document lists deep-equal — that coupling is mechanical, not a convention. check:doc-types joining was the one judgement the ruling delegated to this lane; the reasoning is below and is written into that gate's header.

What moved

gatebeforeafter
check:doc-fences — documents225228
check:doc-snippets — documents225228
check:doc-snippets — covered / ungated193 / 32196 / 32
check:doc-snippets — covered blocks563577
check:doc-snippets — compiled / declared fragments417 / 146421 / 156
check:doc-types — doc files186189
check:doc-types — code blocks10901115
check:doc-typestype literals922922

The acceptance criterion the ruling set, met verbatim: check:doc-snippets judges the apps/*/docs/** blocks and reports 0 failed (421 of 421 block(s) judged, 0 failed), with the fragment blocks counted as declared fragments (+10) rather than skipped files; no UNGATED_DOCS growth (32 → 32); the three gate headers state their scope and agree with their roots.

UNGATED_DOCS and the fences shrink-only baseline are byte-identical to main, asserted rather than eyeballed — same SHA-256 for the extracted block in both cases, and KNOWN_UNHIGHLIGHTED_TS_FENCES still reports the same 80 files / 90 blocks.

⚠️ Three of the four newly-compiled blocks are green about nothing — #7426

This is stated here rather than absorbed into the green, because this card exists precisely because a gate suite reports green over a file it never opened, and shipping a gate that reports green over blocks it cannot fail is the same defect wearing the fix's clothes.

Of the 4 blocks that become live-compiled in this tree, 3 are design-token lists under "Visual Design Specs" in apps/console/docs/UI_IMPROVEMENT_PROPOSAL.md (Color Palette, Typography, Spacing). They compile only because background: "bg-background" parses as a labelled statement whose body is a string-literal expression — syntactically legal, semantically empty. So three quarters of this tree's new semantic coverage is vacuous.

That is not a prediction. It is demonstrated in leg A2 of the ablation below: replacing background: "bg-background" with background: "ABLATION_6600_this_is_not_typescript_at_all" leaves check:doc-snippetsgreen (exit 0), while the same class of defect in the one genuine block turns it red.

#7426 remains open and is the right home for it — re-fencing is a document-shape change and that card records the fence target (plain text vs. a markdown table) as undecided, which makes it domain:ui's judgement call. ⛔ The three blocks were deliberately not marked doc-snippet: fragment: that marker asserts a block cannot compile, which is false here, and it would drop them from real coverage. Out of scope for this PR: #7426.

Non-vacuity — each moved gate proven to actually judge the new population

A root that moves while the walk still misses the files passes every green check and buys nothing. Each leg plants a deliberate defect in an apps/*/docs/** block, proves the mutation reached disk before the run (HEAD blob hash != worktree blob hash, plus a marker grep -c), then restores with git checkout HEAD -- ABSOLUTE_PATH and proves the restore (blob == HEAD blob and empty git diff HEAD). The script carries trap … EXIT INT TERM. The mutated subject is markdown the gate reads by path at runtime, not an artifact resolved through a package dist/, so no rebuild leg applies; the packages the snippet gate compiles against were unchanged and already built.

legplanted defectgateexitverdict line
A1const ABLATION_6600: number = "not a number"; in the genuine compiled blockcheck:doc-snippets1TS2322 at UI_IMPROVEMENT_PROPOSAL.md:174; 421 of 421 block(s) judged, 1 failed
A2same class of defect in a design-token blockcheck:doc-snippets0421 of 421 block(s) judged, 0 failed — the #7426 vacuity, demonstrated
Binterface ABLATION_6600 { under a bare fence in deployment.mdcheck:doc-fences1deployment.md:21 — bare fence, no info string, body interface ABLATION_6600 {
C{ "type": "ablation-6600-unregistered" } in a fenced blockcheck:doc-types1[unregistered-doc-type] type 'ablation-6600-unregistered' (tsx)

Final tree state after all four legs: git diff HEADempty.

Why check:doc-types joined, at zero present yield

Stated plainly because the alternative reading — that a widened scope was justified by a discovery — is false: the three files carry 0type literals today, and the count is unchanged at 922 after the move. This gate finds nothing on the day it lands.

The argument for joining anyway is the split-surface defect this exact gate was already burned by once. In #7115, check-doc-component-types walked content/docs; check-doc-snippet-types walked content/docs plus the package READMEs; the root README.md fell between the two and was read by neither — and it taught the unregistered type stat-card four times, in the flagship example, for as long as that example existed. Leaving this gate pointed away from a tree its two siblings now read would rebuild that geometry deliberately, in the same gate family. And UI_IMPROVEMENT_PROPOSAL.md is a proposal about console UI shape, i.e. the file in that tree most likely to grow the first type literal.

⛔ This is not a precedent for widening onto any other unscanned tree, and no allowlist mechanism is built — the population is three files.

Scope statement, not scope expansion

The ruling asked for the ownership of the unscanned markdown to be written into the gate headers as a statement of what the roots are. The full ownership map lives once in check-doc-snippet-types.mjs beside UNGATED_DOCS (widest surface, holds the ledger); the other two headers state their own roots and point at it.

Measured for that statement rather than inherited: the unscanned population is 114 files excluding the ephemeral .changeset/ — not the "roughly 50" the ruling's prose carries — the largest groups being non-README .md under packages/** (54), docs/** (17) and the published skills/objectui/** (16). The header gives the re-derivation command rather than relying on that number, matching the fix this repo already applied to UNGATED_DOCS's own header.

No gate is widened onto skills/**. It is named in the map as unclaimed, which is the opposite of a claim: a governed, published surface whose coverage is a decision for whoever owns it, never a side effect of a root move. Writing an unscanned tree down is what keeps it a known debt rather than #5174's "neither covered NOR declared ungated".

Pins added

scripts/__tests__/check-doc-fence-languages.test.ts gains three, in the shape #7115 established and check-doc-component-types' header demands of anything added later:

  • all three gates carry the same APP_DOCS constant (mirrors the existing ROOT_PAGES tri-gate pin);
  • the three guides are really in the walk by name — the deep-equality assertion does not cover this, since both walks could drop the tree together and stay equal, which is exactly the state this card was filed about;
  • the walk takes one app-directory level and does not descend into apps/site/app/docs, a Next.js route directory holding .tsx route files.

Verification

Run from the repo root at eba0a9b7a, tree clean, exit codes captured by redirect before any pipe:

  • pnpm check:doc-fences0228 document(s), baseline unchanged at 80 files / 90 blocks
  • pnpm check:doc-snippets0421 of 421 block(s) judged, 0 failed
  • pnpm check:doc-types0Every documented component type is registered.
  • pnpm check:control-bytes0 — 6131 tracked text files
  • pnpm exec vitest run over check-doc-fence-languages, check-doc-snippet-types, check-doc-component-types plus every other test that names these scripts (check-pre-install-import-graph, one-authority-per-exported-name-6273, known-schema-types-derivation-5115, report-namespace-agreement-6416) → 7 files, 196 tests, all passing
  • pnpm lint (full repo, not narrowed) → 0 — 47/47 tasks, 0 errors

The three gate workflows carry nopaths filter by deliberate design — their own tests fail if one is added — so the moved roots are exercised on every pull request, including the docs-only shape that would otherwise skip them.

Changeset

Empty-frontmatter changeset: these are gate scan roots, and scripts/** publishes nothing. skip-changeset is not used — ci-cd-pipeline-doc.test.ts pins it as a phantom label in this repository. Precedent followed: .changeset/7086-blockquoted-fence-collector.md.

Out of scope

🤖 Generated with Claude Code

https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW


Generated by Claude Code

The three documentation gates all rooted their walk at `content/docs` (plus,
for two of them, the package READMEs and the root `README.md`). None descended
into `apps/`, so `apps/console/docs/**` — the console's operator and deployment
guides — was read by no documentation gate at all. The only check whose surface
contained those files was `check:control-bytes`, which enumerates `git ls-files`
and therefore covers every tracked text file: they were checked for control
bytes and for nothing else.
`check:doc-fences` and `check:doc-snippets` move together because
`check-doc-fence-languages.test.ts` pins their document lists deep-equal.
`check:doc-types` joins them so that the tree is not left in the split-surface
geometry objectui#7115 was filed about, where a file fell between two gates'
differing roots and was read by neither.
No `UNGATED_DOCS` entry is added and no allowlist mechanism is built; both
shrink-only ledgers are byte-identical to `main`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
…#6600)
The first draft of the ownership map stated the unscanned markdown population
as 114 files. The re-derivation command the same block ships answers 111, at
both `origin/main` and this branch: the 114 counted the three
`apps/<app>/docs/` guides that this very pull request brings under the gates.
Rather than correct 114 to 111, this removes every hand-copied count from the
block — the total and the per-tree ones alike. A number in a header drifts from
the tree and nothing fails when it does, which is objectui#7448 exactly, and
which `UNGATED_DOCS`'s own header already records happening to its `12 .mdx
pages and 32 package READMEs` sentence. The trees are now named in descending
order of size, which is the ownership statement the ruling asked for, and the
command is left as the durable answer to both "how many" and "which".
Comment-only: no code line changes, and all three gates report identical
numbers before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 06:08
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit fda1247Sep 3, 2026
31 checks passed
@os-sales
os-sales deleted the claude/issue-6600-doc-gate-scan-roots branch September 3, 2026 06:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-sales@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

feat(scripts): point the three doc gates at apps/*/docs (#6600) - #7449

Merged
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots
Sep 3, 2026
Merged

feat(scripts): point the three doc gates at apps/*/docs (#6600)#7449
os-sales merged 2 commits into
mainfrom
claude/issue-6600-doc-gate-scan-roots

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes#6600

Step 2 of maintainer ruling D on #6600 (2026-09-02, verbatim 「同意」, recorded by the director seat). Step 1 was the content change, landed as PR #7425 (39af82f).

The three documentation gates all rooted their walk at content/docs — plus, for two of them, packages/NAME/README.md and the root README.md. None descended into apps/, so apps/console/docs/** was read by no documentation gate at all. The only check whose surface contained those files was check:control-bytes, which enumerates git ls-files and therefore covers every tracked text file: the console's operator guides were checked for control bytes and for nothing else.

check:doc-fences and check:doc-snippets move together because check-doc-fence-languages.test.ts pins their document lists deep-equal — that coupling is mechanical, not a convention. check:doc-types joining was the one judgement the ruling delegated to this lane; the reasoning is below and is written into that gate's header.

What moved

gatebeforeafter
check:doc-fences — documents225228
check:doc-snippets — documents225228
check:doc-snippets — covered / ungated193 / 32196 / 32
check:doc-snippets — covered blocks563577
check:doc-snippets — compiled / declared fragments417 / 146421 / 156
check:doc-types — doc files186189
check:doc-types — code blocks10901115
check:doc-typestype literals922922

The acceptance criterion the ruling set, met verbatim: check:doc-snippets judges the apps/*/docs/** blocks and reports 0 failed (421 of 421 block(s) judged, 0 failed), with the fragment blocks counted as declared fragments (+10) rather than skipped files; no UNGATED_DOCS growth (32 → 32); the three gate headers state their scope and agree with their roots.

UNGATED_DOCS and the fences shrink-only baseline are byte-identical to main, asserted rather than eyeballed — same SHA-256 for the extracted block in both cases, and KNOWN_UNHIGHLIGHTED_TS_FENCES still reports the same 80 files / 90 blocks.

⚠️ Three of the four newly-compiled blocks are green about nothing — #7426

This is stated here rather than absorbed into the green, because this card exists precisely because a gate suite reports green over a file it never opened, and shipping a gate that reports green over blocks it cannot fail is the same defect wearing the fix's clothes.

Of the 4 blocks that become live-compiled in this tree, 3 are design-token lists under "Visual Design Specs" in apps/console/docs/UI_IMPROVEMENT_PROPOSAL.md (Color Palette, Typography, Spacing). They compile only because background: "bg-background" parses as a labelled statement whose body is a string-literal expression — syntactically legal, semantically empty. So three quarters of this tree's new semantic coverage is vacuous.

That is not a prediction. It is demonstrated in leg A2 of the ablation below: replacing background: "bg-background" with background: "ABLATION_6600_this_is_not_typescript_at_all" leaves check:doc-snippetsgreen (exit 0), while the same class of defect in the one genuine block turns it red.

#7426 remains open and is the right home for it — re-fencing is a document-shape change and that card records the fence target (plain text vs. a markdown table) as undecided, which makes it domain:ui's judgement call. ⛔ The three blocks were deliberately not marked doc-snippet: fragment: that marker asserts a block cannot compile, which is false here, and it would drop them from real coverage. Out of scope for this PR: #7426.

Non-vacuity — each moved gate proven to actually judge the new population

A root that moves while the walk still misses the files passes every green check and buys nothing. Each leg plants a deliberate defect in an apps/*/docs/** block, proves the mutation reached disk before the run (HEAD blob hash != worktree blob hash, plus a marker grep -c), then restores with git checkout HEAD -- ABSOLUTE_PATH and proves the restore (blob == HEAD blob and empty git diff HEAD). The script carries trap … EXIT INT TERM. The mutated subject is markdown the gate reads by path at runtime, not an artifact resolved through a package dist/, so no rebuild leg applies; the packages the snippet gate compiles against were unchanged and already built.

legplanted defectgateexitverdict line
A1const ABLATION_6600: number = "not a number"; in the genuine compiled blockcheck:doc-snippets1TS2322 at UI_IMPROVEMENT_PROPOSAL.md:174; 421 of 421 block(s) judged, 1 failed
A2same class of defect in a design-token blockcheck:doc-snippets0421 of 421 block(s) judged, 0 failed — the #7426 vacuity, demonstrated
Binterface ABLATION_6600 { under a bare fence in deployment.mdcheck:doc-fences1deployment.md:21 — bare fence, no info string, body interface ABLATION_6600 {
C{ "type": "ablation-6600-unregistered" } in a fenced blockcheck:doc-types1[unregistered-doc-type] type 'ablation-6600-unregistered' (tsx)

Final tree state after all four legs: git diff HEADempty.

Why check:doc-types joined, at zero present yield

Stated plainly because the alternative reading — that a widened scope was justified by a discovery — is false: the three files carry 0type literals today, and the count is unchanged at 922 after the move. This gate finds nothing on the day it lands.

The argument for joining anyway is the split-surface defect this exact gate was already burned by once. In #7115, check-doc-component-types walked content/docs; check-doc-snippet-types walked content/docs plus the package READMEs; the root README.md fell between the two and was read by neither — and it taught the unregistered type stat-card four times, in the flagship example, for as long as that example existed. Leaving this gate pointed away from a tree its two siblings now read would rebuild that geometry deliberately, in the same gate family. And UI_IMPROVEMENT_PROPOSAL.md is a proposal about console UI shape, i.e. the file in that tree most likely to grow the first type literal.

⛔ This is not a precedent for widening onto any other unscanned tree, and no allowlist mechanism is built — the population is three files.

Scope statement, not scope expansion

The ruling asked for the ownership of the unscanned markdown to be written into the gate headers as a statement of what the roots are. The full ownership map lives once in check-doc-snippet-types.mjs beside UNGATED_DOCS (widest surface, holds the ledger); the other two headers state their own roots and point at it.

Measured for that statement rather than inherited: the unscanned population is 114 files excluding the ephemeral .changeset/ — not the "roughly 50" the ruling's prose carries — the largest groups being non-README .md under packages/** (54), docs/** (17) and the published skills/objectui/** (16). The header gives the re-derivation command rather than relying on that number, matching the fix this repo already applied to UNGATED_DOCS's own header.

No gate is widened onto skills/**. It is named in the map as unclaimed, which is the opposite of a claim: a governed, published surface whose coverage is a decision for whoever owns it, never a side effect of a root move. Writing an unscanned tree down is what keeps it a known debt rather than #5174's "neither covered NOR declared ungated".

Pins added

scripts/__tests__/check-doc-fence-languages.test.ts gains three, in the shape #7115 established and check-doc-component-types' header demands of anything added later:

  • all three gates carry the same APP_DOCS constant (mirrors the existing ROOT_PAGES tri-gate pin);
  • the three guides are really in the walk by name — the deep-equality assertion does not cover this, since both walks could drop the tree together and stay equal, which is exactly the state this card was filed about;
  • the walk takes one app-directory level and does not descend into apps/site/app/docs, a Next.js route directory holding .tsx route files.

Verification

Run from the repo root at eba0a9b7a, tree clean, exit codes captured by redirect before any pipe:

  • pnpm check:doc-fences0228 document(s), baseline unchanged at 80 files / 90 blocks
  • pnpm check:doc-snippets0421 of 421 block(s) judged, 0 failed
  • pnpm check:doc-types0Every documented component type is registered.
  • pnpm check:control-bytes0 — 6131 tracked text files
  • pnpm exec vitest run over check-doc-fence-languages, check-doc-snippet-types, check-doc-component-types plus every other test that names these scripts (check-pre-install-import-graph, one-authority-per-exported-name-6273, known-schema-types-derivation-5115, report-namespace-agreement-6416) → 7 files, 196 tests, all passing
  • pnpm lint (full repo, not narrowed) → 0 — 47/47 tasks, 0 errors

The three gate workflows carry nopaths filter by deliberate design — their own tests fail if one is added — so the moved roots are exercised on every pull request, including the docs-only shape that would otherwise skip them.

Changeset

Empty-frontmatter changeset: these are gate scan roots, and scripts/** publishes nothing. skip-changeset is not used — ci-cd-pipeline-doc.test.ts pins it as a phantom label in this repository. Precedent followed: .changeset/7086-blockquoted-fence-collector.md.

Out of scope

🤖 Generated with Claude Code

https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW


Generated by Claude Code

The three documentation gates all rooted their walk at `content/docs` (plus,
for two of them, the package READMEs and the root `README.md`). None descended
into `apps/`, so `apps/console/docs/**` — the console's operator and deployment
guides — was read by no documentation gate at all. The only check whose surface
contained those files was `check:control-bytes`, which enumerates `git ls-files`
and therefore covers every tracked text file: they were checked for control
bytes and for nothing else.
`check:doc-fences` and `check:doc-snippets` move together because
`check-doc-fence-languages.test.ts` pins their document lists deep-equal.
`check:doc-types` joins them so that the tree is not left in the split-surface
geometry objectui#7115 was filed about, where a file fell between two gates'
differing roots and was read by neither.
No `UNGATED_DOCS` entry is added and no allowlist mechanism is built; both
shrink-only ledgers are byte-identical to `main`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
…#6600)
The first draft of the ownership map stated the unscanned markdown population
as 114 files. The re-derivation command the same block ships answers 111, at
both `origin/main` and this branch: the 114 counted the three
`apps/<app>/docs/` guides that this very pull request brings under the gates.
Rather than correct 114 to 111, this removes every hand-copied count from the
block — the total and the per-tree ones alike. A number in a header drifts from
the tree and nothing fails when it does, which is objectui#7448 exactly, and
which `UNGATED_DOCS`'s own header already records happening to its `12 .mdx
pages and 32 package READMEs` sentence. The trees are now named in descending
order of size, which is the ownership statement the ruling asked for, and the
command is left as the durable answer to both "how many" and "which".
Comment-only: no code line changes, and all three gates report identical
numbers before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aCUUSwWefnbCJ4Xk1vqQW
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 06:08
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit fda1247Sep 3, 2026
31 checks passed
@os-sales
os-sales deleted the claude/issue-6600-doc-gate-scan-roots branch September 3, 2026 06:24
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-sales@claude