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.

Philosophy

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.

File layout

  • 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.

ADRs

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.

CI gates

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

GateWhat it checks
markdown-lintMarkdown style across docs/**/*.md and root *.md per .markdownlint.jsonc.
link-checkScheduled external URL health via Lychee.
astro-buildLocked Starlight production build.
source-checksADR shape, proto/KB sync, frontmatter, citations, and internal routes/anchors.
docs-required-checkA PR touching apps/* or proto/* must also touch docs/ (or carry a bypass marker).

Run the gates locally

From the repo root:

Terminal window
# Astro build
pnpm --dir docs install --frozen-lockfile
pnpm --dir docs build
# 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

Bypassing the docs-required check

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.

Regenerating the proto API 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.