Summary
deno task docs:snippets (landed in #1374 / PR #1537) walks docs/site/** with no exclusion for
docs/site/_site/ — Lume's build output, which is gitignored. So the gate fails on any
checkout where the documentation site has been built, on content that is entirely unmodified.
$ deno task docs:snippets
docs snippets: FAIL _site/ai/chat-ui/index.md:86: unclosed ``` fence
EXIT=1
_site/ai/chat-ui/index.md is generated output. The "unclosed fence" is an artifact of rendering,
not a defect in any source page.
Why this is more than a local annoyance
The doc-audit profile and this gate are mutually incompatible in the same worktree.
.llm/harness/workflow/doc-audit.md gate 2 is "Site build (Lume) clean with the changeset applied",
so every audited docs changeset builds the site by design — and from that moment the snippet gate
fails in that worktree until someone deletes 24MB of build output and knows to.
This already cost a real diagnostic detour: the 0.0.6 docs lane hit it immediately after #1537
merged, while running the new gate against the next docs PR. The failure names a file nobody edited,
so the natural first read is "the changeset broke a page".
CI is currently unaffected — pages.yml:48 runs docs:snippets immediately before
deno task build at :51, on a clean checkout. That ordering is load-bearing and undeclared: swap
those two steps, add a second consumer of the task, or run on a warm workspace, and CI inherits the
same false red.
Evidence
Verified at d558f9ab2:
docs/site/.gitignore:2 — _site/. The directory is untracked build output (~24MB).
grep -nE "_site|skipDir|exclude|ignore" .llm/tools/docs/snippet-extractor.ts .llm/tools/docs/check-snippets.ts → no matches. The walker has no exclusion mechanism at all.
- Removing
docs/site/_site/ and re-running: exit 0, scanned=578 … tier1=35 checked=21 exempt=14 outside_floor=260 malformed=0 — the census is unchanged, confirming the failure was
entirely build output.
deno task docs:snippets is invoked from exactly one place (pages.yml:48), three lines before
the Lume build.
Scope
- Exclude generated and ignored paths from the walk.
_site/ at minimum; the robust form is to skip
anything git ignores, so the next generated directory does not reintroduce this.
- Make the failure legible if it ever recurs: a diagnostic naming a path under a build directory
should say so rather than reporting it as a source-page defect.
- Consider asserting the
pages.yml step order (snippet gate before Lume build) in the existing
pages-workflow_test.ts, which already asserts the trigger arms — the ordering is currently an
undeclared invariant that CI silently depends on.
Boundaries
Acceptance criteria
The second negative test is the one that matters — an exclusion that is too broad would silently
shrink coverage, which is the defect class #1374 exists to end.
Provenance
Found by the 0.0.6 documentation-lane orchestrator immediately after #1537 merged, while running the
newly landed gate against PR #1541's content. The interlock worked exactly as intended — this is the
gate's own blind spot, not the content's.
Summary
deno task docs:snippets(landed in #1374 / PR #1537) walksdocs/site/**with no exclusion fordocs/site/_site/— Lume's build output, which is gitignored. So the gate fails on anycheckout where the documentation site has been built, on content that is entirely unmodified.
_site/ai/chat-ui/index.mdis generated output. The "unclosed fence" is an artifact of rendering,not a defect in any source page.
Why this is more than a local annoyance
The doc-audit profile and this gate are mutually incompatible in the same worktree.
.llm/harness/workflow/doc-audit.mdgate 2 is "Site build (Lume) clean with the changeset applied",so every audited docs changeset builds the site by design — and from that moment the snippet gate
fails in that worktree until someone deletes 24MB of build output and knows to.
This already cost a real diagnostic detour: the 0.0.6 docs lane hit it immediately after #1537
merged, while running the new gate against the next docs PR. The failure names a file nobody edited,
so the natural first read is "the changeset broke a page".
CI is currently unaffected —
pages.yml:48runsdocs:snippetsimmediately beforedeno task buildat:51, on a clean checkout. That ordering is load-bearing and undeclared: swapthose two steps, add a second consumer of the task, or run on a warm workspace, and CI inherits the
same false red.
Evidence
Verified at
d558f9ab2:docs/site/.gitignore:2—_site/. The directory is untracked build output (~24MB).grep -nE "_site|skipDir|exclude|ignore" .llm/tools/docs/snippet-extractor.ts .llm/tools/docs/check-snippets.ts→ no matches. The walker has no exclusion mechanism at all.docs/site/_site/and re-running: exit 0,scanned=578 … tier1=35 checked=21 exempt=14 outside_floor=260 malformed=0— the census is unchanged, confirming the failure wasentirely build output.
deno task docs:snippetsis invoked from exactly one place (pages.yml:48), three lines beforethe Lume build.
Scope
_site/at minimum; the robust form is to skipanything git ignores, so the next generated directory does not reintroduce this.
should say so rather than reporting it as a source-page defect.
pages.ymlstep order (snippet gate before Lume build) in the existingpages-workflow_test.ts, which already asserts the trigger arms — the ordering is currently anundeclared invariant that CI silently depends on.
Boundaries
its contract. The census, floors, marker grammar and negative controls are all unaffected — the
only change is which files the walker visits.
leave
scanned=578 … checked=21 exempt=14exactly as it is today.Acceptance criteria
deno task docs:snippetsexits 0 on a checkout wheredocs/site/_site/exists.does not blunt the gate.
The second negative test is the one that matters — an exclusion that is too broad would silently
shrink coverage, which is the defect class #1374 exists to end.
Provenance
Found by the 0.0.6 documentation-lane orchestrator immediately after #1537 merged, while running the
newly landed gate against PR #1541's content. The interlock worked exactly as intended — this is the
gate's own blind spot, not the content's.