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. Mirrorsapps/*.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:
| 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). |
Run the gates locally
From the repo root:
# Astro buildpnpm --dir docs install --frozen-lockfilepnpm --dir docs build
# Markdown lintpnpm dlx markdownlint-cli2 "docs/**/*.md" "*.md"
# ADR templatebash scripts/docs-check-adr-template.sh
# Proto syncbash scripts/docs-check-proto-sync.sh
# KB, frontmatter, citations, and internal linksbash scripts/docs-check-kb-sync.shnode scripts/docs-check-frontmatter.mjsnode scripts/docs-check-citations.mjsgo 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.shBypassing 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:
bash scripts/gen-proto-docs.shThe generator and sync checker must change together if their checksum algorithm changes.