Start here

ADR-0005: Docs for agents

Status: accepted · 2026-10-18

Context

An app built from botgumi has many documents: the PRD, the brand and design work (system, lo-fi, hi-fi, binding maps), the backend, iOS and Android specs, the OpenAPI contract, the dashboard, the landing page, the ADRs, the tracking plan, the store and go-live checklists. Agents read them on every task; people read them at every gate.

The first app built this way kept its docs as Markdown next to the code, plus one-page spec sites built from them by a script with a --check flag that CI ran. That worked where it was used: the pages could not drift from the docs. Where nothing checked, links and pages went stale. The docs were also only readable by cloning the repo or opening a hand-made page; an agent outside the repo had no index to start from.

Cloudflare publishes its docs for agents in a fixed shape (developers.cloudflare.com/docs-for-agents): a site index at /llms.txt grouped by product, each product's /{product}/llms.txt and /{product}/llms-full.txt, a site-wide /llms-full.txt, and every page as Markdown at {page}/index.md or for Accept: text/markdown. It follows the llms.txt convention and needs nothing but static files.

Decision

D1. The docs live in the app repo as Markdown, next to the code. docs/ holds the cross-track pages (PRD, tracking plan, API, ADRs, checklists); each track keeps its own pages in its folder (design/, backend/, ios/, android/, landing/, dashboard/). No second copy anywhere.

D2. One manifest lists every page by module. docs/manifest.json: the site title and summary, then modules, each with a slug, a title, a one-line description, the track that owns it, its pages (slug, source, title, optional description and section) and files served as they are. The template's modules:

Module Pages Owner
overview the repo README, how the docs work docs
product PRD, tracking plan docs
design brand, design system, lo-fi, hi-fi screens and their binding maps, screenshot direction design
api the API page; openapi.yaml, linked as it is docs
backend, ios, android, landing, dashboard the track's README and specs; the store checklists under ios and android that track
decisions the ADRs docs
go-live the go-live checklist, the secrets inventory docs

D3. One generator, @botgumi/docs. build(manifest, readFile) returns the files; it is pure, so the CLI, the tests, the dev server and botgumi.com run the same code. It writes:

Path What
/llms.txt the site index: every module with its description and links to its llms.txt and llms-full.txt, then each of its pages and files
/llms-full.txt every page in one file
/{module}/llms.txt the module's index, pages grouped by section, then its files
/{module}/llms-full.txt the module's pages in one file
/{module}/{page}/index.md the page: front matter (title, description, source), a line linking its module and the site index, the Markdown
/{module}/{name} a file as it is (the OpenAPI contract at /api/openapi.yaml)
/{module}/{page}/, /, /404.html a small HTML site of the same pages

Links between pages are rewritten to the published pages; a relative link to a file outside the docs becomes its text and a build warning. Raw HTML in a page shows as text in the HTML site.

D4. The output is committed and checked. pnpm docs:build writes docs/public/. pnpm docs:check builds in memory and fails when a source is missing or docs/public/ differs from the build in any file. CI runs it on every PR, so a PR that changes a doc rebuilds the output in the same PR. Agents in the repo read docs/public/llms.txt and the index.md files directly.

D5. The same build serves locally and hosted. pnpm docs:dev serves the docs on localhost, rebuilt on every request. Hosted, docs/public/ is the static assets of a Cloudflare Worker on docs.<app domain> (the core stack); the Worker runs first to answer Accept: text/markdown with a page's index.md and to set content types (text/markdown for .md, text/plain for .txt, application/yaml for the contract). The dev server and the Worker share one request-to-file function.

D6. Private by default. A PRD and a tracking plan are not public. The docs hostname sits behind Cloudflare Access (one application, allowing the owner), and the Worker's workers.dev and preview URLs are off so Access cannot be bypassed. Making the docs public is the owner's choice: remove the Access application.

D7. Creation. botgumi new writes docs/manifest.json and the first pages from the templates (PRD review, tracking plan, ADR-001, the API page, the docs README, the design README), copies botgumi's checklists, drops the modules of tracks the app does not have, and runs docs:build, so the first commit passes docs:check.

D8. Updates. Each worker's "Done" includes its module's pages updated when the PR changes behaviour, a contract or a design, and docs:build run. A new page is a Markdown file plus its manifest line. Review rule "docs updated for changed behaviour" is blocking. The upgrade bot's PR refreshes the copied checklists and @botgumi/docs, then rebuilds; the output diff is part of that PR.

D9. botgumi.com serves its own docs the same way. The site builds its /llms.txt, /llms-full.txt, /docs/llms.txt, /docs/llms-full.txt and /docs/{slug}/index.md with @botgumi/docs from its sidebar manifest and the Markdown the HTML pages render. Its docs pages also answer Accept: text/markdown.

Consequences

  • An agent outside the repo needs one URL (/llms.txt) and, for a private app, an Access service token.
  • Every doc change carries a generated diff in docs/public/. Reviewers skip it; docs:check proves it matches.
  • Docs content stays English, like botgumi.com's docs (ADR-0001, decision 7 covers UI strings). The generated HTML site has no prose of its own: titles come from the manifest, the rest are file names.
  • docs/public/ is rebuilt whole, so nothing hand-made may live in it.
  • Follow-up: a CI workflow in the app template that runs docs:check with the other tracks' checks; an Access service token for agents that read hosted docs; the design stage pages (ADR-0004) join the design module.