Skip to content

Documentation Standard

This is the spec every BoB doc must obey. Each doc declares one Diátaxis mode, lives in the matching docs/ category, carries a frontmatter contract (type / owner / last_reviewed), and states each fact once (link, don’t duplicate). The governance tooling (R2) and CI (R3) enforce what follows; this document is the source of truth they read.

This doc is itself reference mode (look things up; don’t read end-to-end). It lives in docs/reference/ and carries the frontmatter it mandates — it is its own first conformance test. It grounds out in doc-ia-audit-2026-06-22.md, which catalogues the disorder this standard exists to end.

Principles it encodes: Krug (scannable, self-evident), Rosenfeld & Morville (findability through organization / labeling / navigation), Diátaxis (one mode per doc), inverted pyramid (answer first), and the Prime Directive (one owner per fact).


Every doc serves exactly one of four user needs. Mixing modes is the single biggest failure in BoB’s current docs — the 1,455-line handbook is all four at once, so no reader’s need is met cleanly. Pick one mode before you write; if a doc needs two, it is two docs.

Mode Serves Reader is… Answers Voice
Tutorial Learning a beginner, learning by doing “Teach me, start to finish” “We will… now do this.” Lessons, not options.
How-to A task a practitioner with a goal “How do I X?” Imperative steps. Assumes competence.
Reference Looking up someone who knows what they need “What exactly is X?” Dry, exhaustive, scannable. Tables over prose.
Explanation Understanding someone building a mental model “Why is it this way?” Discursive. Trade-offs, history, rationale.

Ask what the reader is doing when they open the doc:

  • Studying with no specific goal yet → tutorial (docs/tutorials/).
  • Working, with a concrete task in hand → how-to (docs/how-to/).
  • Pausing mid-task to confirm a name, flag, field, or value → reference (docs/reference/).
  • Stepping back to understand why a thing exists → explanation (docs/explanation/).

Two tests resolve almost every case:

  1. The “during vs. between” test. Tutorials and how-tos are read while doing; reference and explanation are read between doing. If your steps and your rationale fight for space, split them.
  2. The completeness test. Reference must be complete and accurate (every flag); a how-to must be sufficient for the goal (only the flags that matter). A doc that can’t decide how much to include is mixing the two.

Templates that already do this well: warp-drive.md’s TL;DR-first lede (how-to done right) and verification-system.md’s tight reference scoping. Reuse their shape.


2. Directory taxonomy & the root-docs rule

Section titled “2. Directory taxonomy & the root-docs rule”

Findability comes from organization + labeling (Rosenfeld & Morville). Every doc lives in exactly one category, and the category name is the Diátaxis mode — so a doc’s location declares its contract. No more flat pile at the top of docs/.

Directory Holds Mode Examples (target home)
docs/tutorials/ Learning paths, getting-started walkthroughs tutorial Quick Start, first-project setup
docs/how-to/ Task recipes — “how do I X?” how-to deploy, new-machine-setup, cdfork
docs/reference/ Lookup material — contracts, glossaries, tables, this standard reference hooks reference, CLI tables, frontmatter contract
docs/explanation/ Architecture, rationale, the conceptual spine explanation prime-directive, orchestration/ (handbook + orchestrator)
docs/audits/ Dated point-in-time analyses (named *-YYYY-MM-DD.md) reference (frozen) doc-ia-audit-2026-06-22
docs/research/ Exploratory notes, not-yet-normative findings explanation (draft) model-routing research
docs/archive/ Retired docs & tombstones (see §5) superseded guides

Rules:

  • Every docs/*.md lives in a category subdirectory. A bare *.md at the top of docs/ is a placement violation (the one exception is a generated docs/index.md navigation root, if present).
  • The category must match the doc’s type frontmatter. type: reference in docs/how-to/ is a contradiction the auditor flags.
  • Filenames are lowercase kebab-case (^[a-z0-9]+(-[a-z0-9]+)*$), extension .md for prose — already enforced by make check-doc-naming.
  • Audit/research artifacts are dated and frozen: they record a moment, are never edited after publication, and don’t carry a rolling last_reviewed expectation.

The repository root is not a documentation directory. Stray root docs (plan.md, bob-identity.md) are how BoB’s IA rotted. The root *.md allowlist is closed:

File Allowed at root? Rationale
README.md ✅ Required Repository entry point; GitHub renders it.
CLAUDE.md ✅ Required Claude Code reads it at the root by contract.
CHANGELOG.md ✅ Allowed Release tooling convention; tools expect it at root.
LICENSE / LICENSE.md ✅ Allowed Legal convention; GitHub detects it at root.
CONTRIBUTING.md ✅ Allowed GitHub surfaces it from the root in PR/issue flows.
Anything else Move into docs/<category>/ No exceptions; relocate and leave a tombstone if linked.

Why these five are exempt: each is read at the root by a tool or platform contract (GitHub, Claude Code, release automation), not by human browsing. A doc a person would seek belongs in the taxonomy. R3 enforces this allowlist in CI.


Every doc under docs/ (excluding frozen audits/ and archive/ tombstones) MUST open with a YAML frontmatter block. This is the metadata the freshness, ownership, and mode-purity checks read — no frontmatter, no governance.

---
type: reference # required — one of: tutorial | how-to | reference | explanation
owner: paulirv # required — GitHub handle accountable for accuracy
last_reviewed: 2026-06-22 # required — ISO 8601 (YYYY-MM-DD) date last verified true
expires: 2026-12-22 # optional — ISO date after which the doc is presumed stale
---
Field Required Type Contract
type Yes enum Exactly one Diátaxis mode. Must match the directory (§2.1).
owner Yes string A GitHub handle (not a team, not “team”) — one accountable human.
last_reviewed Yes ISO date YYYY-MM-DD. Bumped only when the content is re-verified, never as a side effect of an unrelated edit.
expires No ISO date If set and past, the auditor flags the doc stale regardless of last_reviewed. Use for docs with a known shelf life (migration guides, dated plans).

Rules:

  • type is the single source of a doc’s mode. The H1 title, the directory, and type must agree; disagreement is an auditor finding.
  • last_reviewed means “a human confirmed this is true on this date.” Sourced via timelord, never from memory. A mechanical edit (fixing a count, repairing a link) does not reset it — only re-reading for accuracy does.
  • owner is one accountable handle. “One owner per fact” (§4) starts here: every doc has exactly one person who answers for it.
  • Frozen artifacts are exempt. docs/audits/* and docs/archive/* tombstones record a moment and don’t carry a rolling review expectation; they may omit frontmatter or carry a type: reference with the publication date.

Mode and metadata get a doc filed; these principles get it read. They apply to every mode, though the emphasis shifts (reference leans hardest on scannability; explanation on the inverted pyramid’s “why”).

Scope: this section owns documentation prose. For prose rules that span all authored artifacts — commit messages, PR/issue bodies, comments — see the Writing Styleguide, which references these principles rather than restating them.

Lead with the conclusion, then the detail, then the background. A reader who stops after the first paragraph should still have the answer. This is why every BoB doc opens with a TL;DR blockquote (see this doc’s lede, and warp-drive.md’s).

  • First screen carries the payload. Put the “what is this / what do I do” above the fold; push history, edge cases, and rationale down.
  • Never bury the lede in setup. A how-to that spends two screens on context before the first command has the pyramid upside-down.

4.2 Scannability — readers skim, they don’t read

Section titled “4.2 Scannability — readers skim, they don’t read”

Krug’s law: people scan for what they need and ignore the rest. Write for the scan.

  • Descriptive headings that say what the section answers — a reader navigating by heading alone should find their answer. Avoid cute or generic headings (“Overview”).
  • Tables over prose for any enumerable set (flags, fields, modes, options). If you’re writing “X does A, Y does B, Z does C,” it’s a table.
  • Short paragraphs (≤ 3–4 sentences) and bulleted lists for steps or sets.
  • Front-load the keyword. Start list items and table rows with the term being defined, not with filler (“You can use the --foo flag to…” → “--foo — …”).
  • Bold the load-bearing phrase in a dense paragraph so the skim catches it.

4.3 Controlled vocabulary — one name per thing

Section titled “4.3 Controlled vocabulary — one name per thing”

Findability dies when the same concept has three names (Rosenfeld & Morville). BoB uses one canonical term per concept, everywhere:

  • One name per concept. BOB_SOURCE (not “the source repo” / “the git repo” interchangeably); “requirement” (not “ticket” / “story” / “task”); “warp-drive” (not “the loop” / “auto-dev”). Pick the canonical term; use it verbatim.
  • One casing/spelling per name. dev.json, cdprov, make check — as written, every time.
  • Define a term once, at its canonical home, and link to that definition rather than re-defining it inline (this is §4.4 applied to vocabulary).

4.4 Single source of truth — one owner per fact

Section titled “4.4 Single source of truth — one owner per fact”

The Prime Directive, applied to prose: every fact is maintained in exactly one doc; everywhere else links to it. Duplication guarantees drift — the audit found the architecture diagram copied in four places and no two component counts in the repo agreeing.

  • State each fact once, at its owning doc, then link. A count, a table, a diagram, a procedure lives in one place.
  • Link, don’t paste. If you’re tempted to copy a table “for convenience,” link to it instead. Convenience copies are drift waiting to happen.
  • The owner is the doc whose type/topic the fact most belongs to. A make check target table belongs in the verification reference; everything else links there.
  • If two docs legitimately need the same fact, one owns it and the other transcludes or links — never two hand-maintained copies.

The R2 auditor’s cross_cutting[] / duplication detector exists to enforce this mechanically. Until it ships, treat duplication as a review-blocking defect.

4.5 Issue/PR bodies — never use a bare #N as an item number

Section titled “4.5 Issue/PR bodies — never use a bare #N as an item number”

This applies to any text a command authors into a GitHub issue, PR, or comment body (not docs/ prose). GitHub autolinks every #<digits> in rendered markdown into a cross-reference to the issue/PR holding that number. In a mature repo those low numbers are unrelated closed issues, so a body that numbers its own items — “Critical #1”, “High #6”, “fix Medium #10” — sprouts a cluster of spurious, misleading links (the real cause of nanawallweb/nanawalld8#674; tracked here as #520).

  • Reserve #N for genuine issue/PR references only (Part of #42, fixes #51, the (#260) provenance tag).
  • For an intra-document item / finding / step number, use a non-linking form: C1 / H2 / M3 (severity+ordinal), “item 1”, “finding 3”, or backtick it — `#1`.
  • #N inside a fenced code block or backticks does not autolink and is safe (e.g. a terminal mockup listing real issue numbers).

Enforced by scripts/checks/check-issue-autolink.sh (wired into make check), which flags item-word + bare #N in the issue-authoring command templates and can lint a generated body file before gh issue create.


Docs die. An orderly death — tombstone, archive, redirect — keeps the namespace clean and link graph intact; the current docs/ pile of six stray tombstones and orphaned artifacts is what disorderly death looks like. Never silently delete a doc that anything links to.

Situation Action
Content moved into another doc Tombstone in place (§5.2), then move the file to archive/ once links are repointed.
Doc is obsolete, nothing replaces it, but it has inbound links or historical value Archive it (§5.3) with a tombstone redirect.
Doc is obsolete and orphaned (nothing links in, no historical value) Delete it — but only after confirming zero inbound links (link-graph check).
Doc is a dated artifact (audit, status report) Leave in audits/; it’s frozen, not deprecated. Never edit; never archive.

When content moves, replace the old file with a tombstone so existing links don’t 404 and readers are forwarded. BoB’s established tombstone shape (e.g. docs/pm-cheatsheet.md):

# <Old Title> — Moved
This document has been replaced by **[<New Title>](/reference/<relative-path-to-the-new-doc>)**.
<One line on why / what absorbed it.>
  • Keep the filename and path of the original — that’s what inbound links target.
  • A tombstone is a redirect, not content. It carries no frontmatter contract and is exempt from freshness checks.
  • Tombstones live where the original lived until the next restructuring sweep, then move to archive/ (so the live namespace isn’t cluttered — the audit’s complaint about six tombstones in docs/).

docs/archive/ is the graveyard: retired docs and old tombstones that are kept for history but are out of the live IA.

  • Archived docs are frozen. Not edited, not reviewed, not counted in freshness or mode-purity checks.
  • Preserve the original basename so old links resolving through a tombstone still land.
  • Add a one-line banner at the top noting it’s archived and what superseded it, if not already a tombstone.
  • The auditor treats archive/ and audits/ as excluded from active governance but included in the link graph (so a live doc linking into archive/ for anything other than history is flagged).
  • Internal links: the tombstone is the redirect — its single link forwards the reader. Repoint inbound links to the new home opportunistically; the tombstone covers the gap until you do.
  • External / deep links (issues, external sites) can’t be repointed, so the tombstone at the original path must persist — this is the reason tombstones are kept rather than deleted outright.
  • Generated nav/index (a future docs/index.md) is regenerated from the taxonomy, so it never points at a dead doc; archived docs simply drop out of it.

This standard is enforced by the doc-keeper auditor (scripts/doc-keeper/audit.js, run via make docs-check) and in CI (.github/workflows/ci.yml). Each finding carries a severity; whether it blocks (fails CI) or warns (reported, non- blocking) is set here — a single source of truth the tooling reads.

Finding Category Blocks CI?
Broken relative link broken-link Blocks (severity high)
Short-form priority label (p1 vs p1-critical) label-name Blocks (severity high)
Orphan doc (nothing links to it) orphan Blocks — via ratchet (§6.2)
Root *.md outside the allowlist root-placement Blocks — via ratchet (§6.2)
Missing / stale required frontmatter freshness Blocks — via ratchet (§6.2)
Filename not lowercase-kebab filename-violation Blocks (make check-doc-naming, now in CI)
Single-source-of-truth duplication duplication Warns
Diátaxis mode-mixing mode-mixing Warns
Component / registry count drift component-count etc. Warns (mechanically fixable via make docs-sync)

Blocking means make docs-check exits non-zero, failing the build. Warning findings still appear in the audit report and feed the curation worklist, but never fail CI — they require human judgement (which copy is canonical, how to split a doc) that a gate can’t make.

The three governance categories (orphan, root-placement, freshness) block new violations but grandfather the existing ones, via a version-controlled baseline at scripts/doc-keeper/audit-baseline.json. This is what lets the gate go live on a repo that predates the standard without a flag-day cleanup:

  • make docs-check runs the auditor with --fail-on-categories orphan,root-placement,freshness --baseline scripts/doc-keeper/audit-baseline.json. A finding fails CI only if its signature is not already in the baseline — so a PR that adds a new orphan or a stray root doc is blocked, while the pre-existing debt is tolerated.
  • The baseline is accepted documentation debt, and the only correct direction is down. Remediation work (restructuring, archival, adding frontmatter) shrinks it; regenerate with audit.js --write-baseline scripts/doc-keeper/audit-baseline.json --fail-on-categories orphan,root-placement,freshness after a cleanup. When it reaches [], the gate is fully hot with zero suppression.
  • Never grow the baseline to silence a new finding — that defeats the ratchet. New violations are fixed, not baselined.