Appearance
Docs-Site Lifecycle (#153, #689)
Provision and operate the engine-pluggable docs site. Extracted from the dev lifecycle reference.
Docs Site Lifecycle
Every BoB-provisioned project can host a docs site that reads its own docs/, README.md, and CLAUDE.md. The lifecycle is orthogonal to the dev environment lifecycle but follows the same declarative pattern.
The tooling is engine-pluggable (#689). The engine field in docs-site.json selects the static-site generator:
| Engine | Value | Notes |
|---|---|---|
| VitePress (default) | vitepress | Flat .html with native .md link rewriting — correct under flat Cloudflare-Pages serving with no post-build step. Folder-autogenerated Diátaxis sidebar (vitepress-sidebar), built-in local (MiniSearch) search. |
| Astro Starlight (alternative) | starlight | build.format: 'file' + trailingSlash: 'never' for the same flat serving. Folder-autogenerated nav, Pagefind search, richest theming. |
Both render the same docs/ taxonomy and resolve cross-directory links correctly under flat serving with no post-build link rewriting. Quartz — and its fix-links.js hack — was retired in #689 (building on the #487 SSG research).
Accepted regression: Quartz's graph view and automatic backlinks have no equivalent in either engine;
[[wikilinks]]need a plugin or a one-time conversion.
Node requirement: the Starlight engine (Astro 7) needs Node ≥ 22.12; VitePress is fine on Node ≥ 18. CI pins Node 22 in
docs-deploy.ymlfor this reason.
| Sub-command | Script | Purpose |
|---|---|---|
/docs-site init | ~/.claude/scripts/docs-site/init.sh | Scaffold .docs-site/<engine>/ for the resolved engine + a default docs-site.json (if missing). Idempotent on re-run. --engine forces an engine. |
/docs-site build | ~/.claude/scripts/docs-site/build.sh | Sync the docs/ taxonomy + run the engine build. Flat output at .docs-site/public/. --engine overrides. |
/docs-site dev | ~/.claude/scripts/docs-site/dev.sh | Sync content + engine dev server with hot reload. Binds the project's docs port from the port ledger (base+3), so it never collides with the app or another project. --port overrides; --engine overrides the engine; --print-port resolves and prints the port without starting a server. |
/docs-site deploy | ~/.claude/scripts/docs-site/deploy.sh | Build, then publish to the configured target (cloudflare-pages, static, custom). The primary engine deploys to deploy.project; a comparison engine deploys to <deploy.project>-<engine>. |
/docs-site clean | ~/.claude/scripts/docs-site/clean.sh | Drop build artifacts (preserves installed deps). --hard removes the whole .docs-site/ workspace. |
Per-project config (docs-site.json at project root):
json
{
"engine": "vitepress",
"title": "Bodmail Docs",
"description": "Documentation for the Bodmail email platform",
"base_url": "https://docs.bodmail.app",
"sources": ["docs/", "README.md", "CLAUDE.md"],
"exclude": ["docs/archive/**", "docs/audits/**"],
"deploy": { "target": "cloudflare-pages", "project": "bodmail-docs" }
}
base_urldrives the serve path. The enginebaseis the path component ofbase_url, normalised to/…/—/for a root-served Pages project (the BoB default, empty/host-onlybase_url), or e.g./docs/only if the site is genuinely served under that subpath. Abasethat doesn't match where the site is actually served makes every asset 404 (an unstyled page). The flat-link gate (make docs-verify) runs at this same derived base.
Adopting in a project:
cd /path/to/project && /docs-site init— scaffolds.docs-site/<engine>/and a defaultdocs-site.json.- Edit
docs-site.json— set the engine, title, base URL, and deploy target. /docs-site dev— preview at the printed URL (the project's ledger docs port; run/docs-site dev --print-portto see it)./docs-site deploywhen ready.
Trying the other engine, ad hoc (no config change): /docs-site build --engine starlight or /docs-site dev --engine starlight.
Flat-serving link gate (make docs-verify): builds both engines against the real docs/ and asserts every in-docs cross-directory link resolves under flat Cloudflare serving with no rewriting (#689 AC-04; scripts/docs-site/verify-engines.sh + verify-links.js). Links pointing outside the published docs (repo source) are skipped, not failed.
Universal portability: the scripts read --root, $PROJECT_ROOT, or cwd — they never hardcode BOB_SOURCE. The .docs-site/<engine>/ workspace is entirely generated from templates/docs-site/<engine>/ + the synced docs/, so the whole directory is gitignored.
Auto-deploy on doc changes (#155). A GitHub Actions workflow at .github/workflows/docs-deploy.yml rebuilds, verifies, and publishes the site whenever a published doc source changes (docs/**, README.md, CLAUDE.md, docs-site.json, the docs-site scripts, or the engine templates). On master push it deploys both engines for a live A/B; on PRs it deploys previews and posts the URLs back as a sticky comment.
The pipeline is:
checkout → cache .docs-site/{vitepress,starlight}/node_modules
→ make docs-check (high-severity drift fails the build, blocks deploy)
→ verify-engines.sh (build + flat-link-verify BOTH engines)
→ build vitepress → wrangler pages deploy → bigbrain-docs (primary)
→ build starlight → wrangler pages deploy → bigbrain-docs-starlight (comparison, best-effort)
→ comment preview URLs on the PRRequired repo configuration:
- Secret
CLOUDFLARE_API_TOKEN— Cloudflare API token withPages:Editscope. - Secret
CLOUDFLARE_ACCOUNT_ID— the Cloudflare account UUID. - Variable
DOCS_SITE_DEPLOY_ENABLED=true— feature flag that gates the wrangler steps. Until set, the workflow still builds, verifies, and runs the drift gate (catching breakage) but skips the deploy. - A one-time
bigbrain-docs-starlightCloudflare Pages project for the comparison build — the Starlight deploy is best-effort, so a missing project never blocks the primary production deploy. See #187 for the Cloudflare connection step.
NPM script aliases live in the root package.json so users can run npm run docs:build / docs:dev / docs:deploy from anywhere in the repo.
Drift report on the site (#156)
Every /doc-audit run writes two artifacts to docs/audits/:
doc-audit-YYYY-MM-DD.{md,json}— date-stamped historical recordlatest.{md,json}— canonical aliases the docs site reads
The site (when built via /docs-site build) consumes docs/audits/latest.md for a "Docs last verified" badge on the landing page and an "Audits" entry in the nav. latest.json is exposed as a downstream API endpoint at /audits/latest.json. See docs/audits/ for the canonical-path contract and history convention.
The nav + landing-badge component wiring lands as a follow-up — the data plumbing (latest aliases + history) is in place.