Skip to content

ci: gate the docs build on docs/**, and openapi.json on js - #35

Merged
yashau merged 1 commit into
mainfrom
claude/docs-ci-filter
Aug 20, 2026
Merged

ci: gate the docs build on docs/**, and openapi.json on js#35
yashau merged 1 commit into
mainfrom
claude/docs-ci-filter

Conversation

@yashau

@yashau yashau commented Aug 20, 2026

Copy link
Copy Markdown
Owner

Closes #34.

The reported gap

docs/** matched no filter, so a documentation change ran no documentation build. packages/docs is a renderer rather than a container — DOCS_BASE = "../../docs" points its Astro loader at the repository's own docs/ directory — so the markdown outside that package is an input to its build, and astro check never saw a change to it. Because docs-release.yml is tag-triggered, the first failure would land on a docs-v* tag, at the moment the intent is to publish rather than to fix.

What the audit turned up

The issue suggested a look at the remaining top-level paths. Two results.

docs/openapi.json is the more serious one, and it is not documentation for this purpose. It's generated from the Hono router, and both things that compare it to the router — openapi:check and packages/app/test/http/openapi.test.ts inside test:js — run in js-check. So editing that file alone (the hand-edit the docs forbid, or a bad merge resolving it) reached main with nothing checking it against the surface it claims to describe. It joins the js filter, which also means an API-surface change now reaches e2e through the *js splice, where it arguably belongs.

One candidate was rejected on the evidence. .npmrc looks like it governs installs, but its own header records that pnpm 11 silently ignores pnpm settings written there and that the entire supply-chain policy lives in pnpm-workspace.yaml — which the js filter already carries. Adding it would have been cargo-culting.

Everything else audited clean: assets/ and screenshots/ are not build inputs (brand-mark.svelte documents itself as holding a copy of the brand mark), .dev.vars.example is asserted by nothing, .editorconfig doesn't govern the formatter — vite.config.ts states its own printWidth precisely so the two can't disagree silently — lefthook.yml is local hooks only, and the prose files need no filter because lint:typos and lint:loc run in the workflows job, which has no if: at all.

Why a separate filter and job rather than adding to js

e2e splices *js. Folding docs/** into js would have queued the browser suite behind a markdown typo — roughly five minutes of runners for a word. The content gets its own filter, its own job, and one task.

lint:docs is a mise task rather than a pnpm --filter invocation in the workflow, because ci.yml states that every step it runs is a task so the JS toolchain can be repointed without touching CI. It overlaps typecheck deliberately, the same trade lint:svelte already documents.

Verification

The anchors are the part that fails silently, so I parsed the filters block and asserted the resolved graph rather than reading it:

js       [... , "action/**", "patches/**", "docs/openapi.json"]
docs     [".github/workflows/ci.yml", "mise.toml", "mise.lock", "docs/**"]
e2e      [... , "patches/**", "docs/openapi.json", "e2e/**", "playwright.config.*"]

PASS  js includes docs/openapi.json
PASS  js does NOT include docs/**
PASS  docs includes docs/**
PASS  docs includes common (ci.yml)
PASS  e2e does NOT include docs/**
PASS  e2e still splices js
PASS  changes exposes docs output
PASS  ci-ok needs docs-check

ci-ok checks every entry in needs, so docs-check is listed there — an unlisted job cannot block a merge.

Also green: lint:actions, lint:zizmor, lint:docs, lint:typos, lint:loc, fmt:check.

This PR is its own test case. It touches ci.yml and mise.toml, which are in *common and therefore in every filter, so the whole graph runs here — including the new docs-check. What it does not demonstrate is the skip path; a docs-only PR after this merges is what shows docs-check running while js-check and e2e stay skipped.

🤖 Generated with Claude Code

Closes #34.

`docs/**` matched no filter, so a documentation change ran no documentation
build. `packages/docs` is a renderer rather than a container -- DOCS_BASE points
its Astro loader at `../../docs` -- so the markdown outside that package is an
input to its build, and `astro check` never saw a change to it. First failure
would have been on a `docs-v*` tag, at the moment the intent is to publish
rather than to fix.

Two things came out of auditing the rest of the block, as the issue suggested.

`docs/openapi.json` is the more serious one, and it is not documentation for
this purpose. It is generated from the Hono router, and BOTH things that compare
it to the router -- `openapi:check` and packages/app/test/http/openapi.test.ts
inside `test:js` -- run in js-check. Editing the file alone therefore reached
main with nothing checking it against the surface it claims to describe. It
joins the `js` filter, which also means an API-surface change now reaches e2e
through the `*js` splice, where it belongs.

The content filter is deliberately NOT folded into `js`. `e2e` splices `*js`, so
a markdown typo would have queued the browser suite behind it -- five minutes of
runners for a word. It gets its own filter, its own job, and one task.

Everything else in the block audited clean, and one candidate was rejected on
the evidence: `.npmrc` looks like it governs installs, but its own header
records that pnpm 11 silently ignores pnpm settings there and that the whole
supply-chain policy lives in pnpm-workspace.yaml -- which the `js` filter
already carries. `assets/` and `screenshots/` are not build inputs, and the
prose files need no filter because `lint:typos` and `lint:loc` run in the
unconditional `workflows` job.

`lint:docs` is a mise task rather than a `pnpm --filter` invocation in the
workflow, because ci.yml states that every step it runs is a task so the JS
toolchain can be repointed without touching CI. It overlaps `typecheck` on
purpose, the same trade lint:svelte documents.

Verified by parsing the filters block and asserting the resolved graph rather
than reading the anchors: `docs` carries `docs/**`, `js` carries
`docs/openapi.json` and not `docs/**`, and `e2e` does not pick up `docs/**`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@yashau
yashau merged commit c7c76a4 into main Aug 20, 2026
19 checks passed
@yashau
yashau deleted the claude/docs-ci-filter branch August 20, 2026 22:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs/** is in no CI path filter, so astro check never runs on documentation changes

1 participant