Skip to content

Contributing to Docs

This page explains how the beef monorepo keeps documentation honest. Every gate below runs in CI and can be run locally before pushing.

Docs are a single source of truth, not a courtesy. If a behaviour, contract, or operational procedure exists only in code, future-you (and on-call) cannot find it. The CI gates make “the docs lie” a build failure rather than a tribal-knowledge problem.

  • docs/src/content/docs/overview/ — high-level orientation.
  • docs/src/content/docs/architecture/ — cross-service architecture.
  • docs/src/content/docs/services/ — per-service deep dives. Mirrors apps/*.
  • docs/src/content/docs/standards/ — coding, testing, and review conventions.
  • docs/src/content/docs/operations/ — runbooks, on-call, deploys.
  • docs/src/content/docs/flows/ — end-to-end user/data flows that span services.
  • docs/src/content/docs/decisions/ — Architectural Decision Records (ADRs).
  • docs/src/content/docs/generated/ — machine-generated content. Do not hand-edit.

Write an ADR whenever you make a non-trivial, hard-to-reverse decision: choice of library, protocol, data model, sync vs async boundary, vendor, etc. If you would explain it in a design review, write it down.

  • Copy docs/src/content/docs/decisions/_template.md.
  • Filename: YYYY-MM-DD-kebab-slug.md.
  • Required frontmatter: title, status (Proposed | Accepted | Superseded | Deprecated), date.
  • Required sections: ## Context, ## Decision, ## Consequences. Optional: ## Alternatives Considered.

The adr-template-check CI gate enforces the above.

The Docs Quality workflow (.github/workflows/docs-quality.yml) runs on every PR, on each merge-queue group, and on push to main:

Gate What it checks
markdown-lint Markdown style across docs/**/*.md and root *.md per .markdownlint.jsonc.
link-check Scheduled external URL health via Lychee.
astro-build Locked Starlight production build.
source-checks ADR shape, proto/KB sync, frontmatter, citations, and internal routes/anchors.
docs-required-check A PR touching apps/* or proto/* must also touch docs/ (or carry a bypass marker). PR runs only; it reports skipped, which passes, in the merge queue and on push.

From the repo root:

Terminal window
# Astro build
pnpm --dir docs install --frozen-lockfile
pnpm --dir docs build
node scripts/docs-check-mermaid.mjs --built
# Markdown lint
pnpm dlx markdownlint-cli2 "docs/**/*.md" "*.md"
# ADR template
bash scripts/docs-check-adr-template.sh
# Proto sync
bash scripts/docs-check-proto-sync.sh
# KB, frontmatter, citations, and internal links
bash scripts/docs-check-kb-sync.sh
node scripts/docs-check-frontmatter.mjs
node scripts/docs-check-citations.mjs
go run scripts/check-doc-links.go
# Docs-required (simulates the PR check against main)
GITHUB_BASE_REF=main PR_TITLE="$(git log -1 --pretty=%s)" bash scripts/docs-check-required.sh

The docs-required-check gate fails when a PR changes code under apps/* or proto/* but does not change anything under docs/. Bypass it only when a docs update is genuinely not needed.

Add one of the following to the PR title:

  • [skip-docs]: <short reason> — e.g. [skip-docs]: internal log-format tweak.
  • A conventional-commit prefix that implies no behavioural change: chore:, ci:, test:, style:.

Use sparingly. If reviewers cannot tell from the diff that no doc was needed, write the doc.

Proto reference output lives at docs/src/content/docs/generated/proto-api.md and ends with a <!-- proto-checksum: <hex> --> marker. After editing any proto/**/*.proto file:

Terminal window
bash scripts/gen-proto-docs.sh

The generator and sync checker must change together if their checksum algorithm changes.