Gunnar

nordisk/nornir

.nornir/docbook-guide.md

# 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`.