Appearance
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).
1. Diátaxis: one mode per doc
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. |
Choosing the mode
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:
- 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.
- 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) andverification-system.md's tight reference scoping. Reuse their shape.
2. Directory taxonomy & the root-docs rule
2.1 The docs/ taxonomy
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/*.mdlives in a category subdirectory. A bare*.mdat the top ofdocs/is a placement violation (the one exception is a generateddocs/index.mdnavigation root, if present). - The category must match the doc's
typefrontmatter.type: referenceindocs/how-to/is a contradiction the auditor flags. - Filenames are lowercase kebab-case (
^[a-z0-9]+(-[a-z0-9]+)*$), extension.mdfor prose — already enforced bymake 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_reviewedexpectation.
2.2 The root-docs rule
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.
3. The frontmatter contract
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.
yaml
---
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:
typeis the single source of a doc's mode. The H1 title, the directory, andtypemust agree; disagreement is an auditor finding.last_reviewedmeans "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.owneris 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/*anddocs/archive/*tombstones record a moment and don't carry a rolling review expectation; they may omit frontmatter or carry atype: referencewith the publication date.
4. Writing principles
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.
4.1 Inverted pyramid — answer first
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
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
--fooflag to…" → "--foo— …"). - Bold the load-bearing phrase in a dense paragraph so the skim catches it.
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
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. Amake checktarget 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
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
#Nfor 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`. #Ninside 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 intomake check), which flags item-word + bare#Nin the issue-authoring command templates and can lint a generated body file beforegh issue create.
5. Deprecation & archival
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.
5.1 Decide the doc's fate
| 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. |
5.2 Tombstones (redirects)
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):
markdown
# <Old Title> — Moved
This document has been replaced by **[<New Title>](<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 indocs/).
5.3 The archive/ directory
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/andaudits/as excluded from active governance but included in the link graph (so a live doc linking intoarchive/for anything other than history is flagged).
5.4 Redirect handling
- 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.
6. Enforcement & severity model
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.
6.1 What blocks vs. warns
| 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.
6.2 The baseline ratchet
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-checkruns 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,freshnessafter 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.