# The docbook pipeline — guide
> **Class:** `guide` · audience: a techie authoring or driving a repo's docs.
> The *what* is in [`docbook-design.md`](docbook-design.md); the *why* in [`docbook-reasoning.md`](docbook-reasoning.md).
> This file is the CLI + the workflow.
## f0 — a full docs session (zero to published)
The end-to-end path from an un-docbook'd repo to a published book:
```console
$ nornir docs init myrepo # scaffold .nornir/, migrate README/CHANGELOG
$ $EDITOR myrepo/.nornir/README.md # author sources; add marker sections
$ nornir docs render myrepo # expand markers → repo-root README/CHANGELOG
$ nornir docs book myrepo --format pdf # whole-set book.pdf (needs docs-export)
$ nornir docs index myrepo # build the docs search index
$ nornir docs search myrepo "coverage" # BM25 query
$ nornir docs install-hooks myrepo # keep README/CHANGELOG fresh every commit
$ nornir docs publish-pages myrepo --render # stage the orphan pages branch
```
Feature notes: `book`, `export`, `publish-pages` need a nornir built
`--features docs-export` (or `cli`); plain `render` of markdown does not.
`render`/`book` also run **remote** against a `nornir-server` when one is
configured — the verb transparently forwards (`docs_remote`, `src/bin/nornir.rs`).
## The verb surface
| verb | CLI | calls | feature |
|---|---|---|---|
| init | `nornir docs init <repo>` | `layout::init_repo` | — |
| render | `nornir docs render <repo> [--markdown-only]` | `layout::render_all` | pdf/all need `docs-export` |
| check | `nornir docs check <repo> [--json]` | `layout::check_all` | — |
| install-hooks | `nornir docs install-hooks <repo>` | `hooks::install_hooks` | — |
| export | `nornir docs export <repo> [--format pdf\|html\|md]` | `export::export_repo` | `docs-export` |
| book | `nornir docs book <repo> [--format …] [--out PATH]` | `book::build_book` | `docs-export` |
| publish-pages | `nornir docs publish-pages <repo> [--remote origin] [--render]` | `pages::stage_pages_branch` | `docs-export` |
| history | `nornir docs history <repo> [filters]` | `docs::list_doc_exports` | — |
| index | `nornir docs index <repo>` | `docs::build_docs_index` | — |
| search | `nornir docs search <repo> "<query>" [--sha SHA]` | `docs::search_docs` | — |
MCP mirrors: `docs_init`, `docs_render`, `docs_check`, `docs_install_hooks`,
`docs_export`, `docs_book`, `docs_history`. Dispatch is `DocsOp` in
`src/bin/nornir.rs:1833` (`run_docs`, `:11598`).
## Worked example — a benchmark-driven README
Author `.nornir/README.md` with a zero-config comparison table and a fresh test count:
```markdown
# myrepo
<!-- nornir:gen:start:mashup -->
<!-- nornir:gen:end:mashup -->
## Tests
<!-- nornir:gen:start:tests -->
<!-- nornir:gen:end:tests -->
## Dependencies
<!-- nornir:gen:start:depgraph -->
<!-- nornir:gen:end:depgraph -->
```
Then:
```console
$ cargo build # depgraph/symbols/callgraph need a built binary
$ nornir bench … # populate the warehouse so mashup has rivals to find
$ nornir docs render myrepo
```
`render` overwrites the repo-root `README.md` (chmod -w) with the mashup table
(ours-vs-rivals, winning cells bolded), the syn-parsed test inventory, and the
committed `depgraph.svg` linked in. Re-running is a no-op if nothing changed.
## Choosing what `render` emits (`.nornir/docs.toml`)
```toml
# myrepo/.nornir/docs.toml
outputs = ["pdf"] # ["markdown"] (default) | ["pdf"] | ["all"]
```
- `["markdown"]` — the historical behaviour: managed README/CHANGELOG to the repo root.
- `["pdf"]` — the way to add docs to a repo whose `*.md` are **hand-authored and must
not be overwritten**: `render` builds `docs/book.pdf` only, writes no markdown.
- `["all"]` — both.
## How to author a marker section
1. Drop a paired marker into any `.nornir/*.md` source; the body is regenerated:
```markdown
<!-- nornir:gen:start:benches name=throughput winner=row -->
<!-- nornir:gen:end:benches -->
```
2. Args are whitespace-separated `key=value`; you may type them in any order — the
render re-emits them **sorted**, so the next `docs check` passes.
3. To *show* a marker in prose without it expanding, keep it inside a fenced code
block (```` ``` ```` / `~~~`) — fenced markers are ignored.
4. Rebuild any inputs first: DWARF sections (`symbols`, `callgraph`) need `cargo build`;
`bench*`/`mashup`/`coverage` need warehouse data; `depgraph` needs only `cargo metadata`.
To **add a new renderer**: add a `match` arm in `sections::render`
(`src/docs/sections.rs:258`) dispatching to `render_foo(ctx, args)` returning the body
only; extend `Ctx` (`sections.rs:62`) rather than threading new args; document + test it.
## How to add a class file (route a doc into a book)
1. **Name it by class suffix** — the filename suffix is the routing key:
`<topic>-design.md`, `-guide.md`, `-reasoning.md`, `-for-idiots.md`, `-history.md`,
`-benchmarks.md`, `-unsorted.md`. `classify` (`book.rs:409`) reads it; no config needed
for the three-book default.
2. **Open with `f0`, then `f1…fn`** — the whole system first, then one `##` per function.
3. **Add a blockquote Class header** and sibling cross-links (design ↔ guide ↔ reasoning),
like the head of this file.
4. **Override the class in-file if needed:** `<!-- nornir:class: guide -->` in the first
~600 bytes wins over the filename (`explicit_class`, `book.rs:474`).
5. **Custom routing** — add a `[book.<name>]` block to `.nornir/docs.toml` with
`classes = [...]` and `out = "docs/<name>.pdf"` to define your own book; else the
default `manual` / `idiot_guide` / `unsorted` split applies.
Then `nornir docs book <repo>` (or `build_books`) picks it up: it lands in the PDF whose
`classes` list names its class. A source whose class no book claims is **omitted and
logged** (fix: give it a real suffix or add it to a book's `classes`).
## Keeping docs fresh
- `nornir docs install-hooks <repo>` — a tracked `.githooks/pre-commit` that runs
`docs render --markdown-only` and `git add`s the result on every commit; it exits 0 on
any failure, so it never blocks an unrelated commit. Re-running is idempotent.
- At release, enable `[repo.<name>.gates].docs_fresh` in `nornir.toml`: the gate
renders + stages README/CHANGELOG/assets/book PDF, then `check_all` asserts no drift.
## Gotchas
- **Never edit the repo-root `README.md` / `CHANGELOG.md`** — `render` overwrites them
and the `chmod -w` bit reminds you. Edit `.nornir/README.md`.
- The `.nornir/assets/depgraph.svg` is **committed**, not gitignored — deleting it breaks
the web-UI image link. Only `.nornir/cache/` and `.nornir/warehouse/` are gitignored.
- DWARF sections (`symbols`, `callgraph`) silently render empty tables if the binary
isn't built — `cargo build` first.
- `book`/`export`/`publish-pages` on a nornir without `docs-export` fail with a message
pointing at `--features cli`.