The docs site
make docs # serve on :8001 (DOCS_PORT=8002 if it is taken)
make docs-build # --strict; CI runs this, a dead link fails the build
docs/ is both the published site and the repository's own engineering notes — one
copy, on purpose. A second copy of "how a capability works" written for outsiders is
a copy that disagrees with the one contributors read. So: do not write a page that
restates another page. mkdocs.yml's nav decides what an outside reader meets first;
nothing is duplicated for them.
The same principle governs these skills. A skill routes to the doc and adds the operational layer; it does not paraphrase the doc.
Where to write when behaviour changes
The table in CLAUDE.md under ## Documentation maps topic → page. Keep it current
when you add a page.
Three things that silently render wrong
These have all shipped broken. Check the rendered page, not the Markdown.
Mermaid needs the custom fence. Without custom_fences on
pymdownx.superfences, a ```mermaid block goes to the syntax highlighter and
the reader gets the graph's source in a code box. Configured now — do not remove it.
Verifying is harder than it looks: Material renders into a closed shadow root, so
the div.mermaid in the DOM is empty and querySelector('.mermaid svg') finds
nothing. That is indistinguishable from a broken render. A screenshot is the only
honest check. Mermaid itself loads from unpkg.com at page-view time, so diagrams do
not render offline or behind a strict CSP.
Icons need pymdownx.emoji. Without the twemoji index, :material-download:{ .lg .middle } reaches the page as that literal string — the home page's grid cards
rendered their own source for a while.
--strict does not validate anchors. A #fragment that matches no heading passes
the build. Check fragments by hand, or with a script over the headings.
mkdocstrings
The API reference is generated from docstrings rather than written twice — this codebase puts its reasoning in docstrings, so a hand-written reference would be a worse copy of something already there.
The collector is static, and app/services/, app/api/ and app/worker/ have no
__init__.py, so it cannot traverse into them: ::: app.services.foo fails the
build. Reference those from prose with a source link until those packages are made
explicit.
Because the reference is generated: when behaviour changes, fix the docstring. A prose page that repeats it will drift.
The seven tabs
AgenticOS · Features · Learn · Reference · Resources · About · Release Notes —
FastAPI's arrangement, organised by what the reader is doing rather than by what a
page is.
Learn is a sequence, not a bag: Get started → Build the agent → Put it in front of people → Keep it under control → the recipes. A page added there belongs at a position.
Files stay flat in docs/. The nav does the grouping. Those paths are named in
CLAUDE.md, in these skills, in scripts/docs_drift.py and in backend docstrings, so
moving a file to get a prettier URL invalidates all of them.
release-notes.md is CHANGELOG.md, substituted at build time by
scripts/mkdocs_hooks.py. That is a hook rather than a snippets include because
snippets expand after every hook, so nothing could reach the included text to
rewrite its docs/-prefixed links — and --strict fails on the docs/docs/… they
resolve to.
Two pages are in the repository and not on the site
docs/ROADMAP.md and docs/about/design.md sit in exclude_docs beside
design/, plans/ and audits/. Delivery state written in board shorthand and
the six decisions a contributor needs before their first change are read by
somebody who has already chosen to work on this; published, they put an internal
status report in a tab a prospective user meets first.
A published page that links to either one fails --strict, because an
excluded file is not built and the link resolves to nothing. Link them by GitHub
blob URL instead - docs/about/index.md and docs/reference/capabilities.md
are the two that do.
Adding a page
- Write it in
docs/(ordocs/howto/,docs/reference/). - Add it to
navinmkdocs.yml, at the position it belongs at — a page outside the nav is a build warning under--strict. - Cross-link it from the pages a reader arrives from, and from
CLAUDE.md's table. make docs-buildand check the anchors.
Voice
Use second person and present tense for user instructions. Explain behaviour and relevant reasons; distinguish verified facts from uncertainty. Choose prose, lists or tables according to what helps the reader.
- Use sentence case headings, preserving product names and acronyms.
- Keep paragraphs within the limit enforced by
scripts/check_docs_paragraphs.py. Split by idea; use an admonition when a reader needs a warning before acting. - Add a recap only when it helps a reader retain the decisions from a long page. Do not repeat short pages or force a fixed number of bullets.
Icons
app/core/catalog/icons/<name>.svg is served by GET /catalog/icons and drawn for a
catalog entry or provider whose id matches and which no compiled-in icon set carries.
The file's own colours are ignored — it renders as a currentColor silhouette, so
the console's monochrome register holds by construction. See icons/README.md.