Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ADR-0061: Regenerate rendered docs on main in CI, not in the pre-commit hook

StateAccepted
Architectural SignificanceLOW
DomainData Platform
Document version0.1

Reference

Amends ADR-0059 (public documentation rendering pipeline: self-hosted rustdoc into docs/site/api/ and mdBook into docs/site/reference/, regenerated locally and committed). Interacts with ADR-0052 (Cloudflare Pages serves docs/site/ verbatim via Git integration, build command NONE) — unchanged by this ADR.

Context

ADR-0059 has the .githooks/pre-commit hook regenerate the rendered surfaces — docs/site/api/ (rustdoc, ~705 files) and docs/site/reference/ (mdBook, ~63 files) — and commit them in the same commit as the source change, so Cloudflare (which serves the tree verbatim) is always current.

That made every branch mutate ~768 generated files. Two branches that each touch a crate or a doc regenerate overlapping subsets, so they conflict on merge — and did: a routine feature PR hit 74 generated-file conflicts (api/*, reference/searchindex.js), dwarfing the 13 real code conflicts. The generated tree is a pure function of the source; a hand-resolved merge of it carries no information and is pure toil (and risks a corrupt, half-merged rendered page).

Decision

Stop regenerating the rendered surfaces in the pre-commit hook. Regenerate them once, on main, in CI after each merge, and commit them back.

  • The .githooks/pre-commit hook keeps the graphify map sync (small, canonical) but no longer runs gen-api-docs.sh / gen-reference-book.sh. Branches therefore never mutate docs/site/api or docs/site/reference, so PRs cannot conflict on them.
  • A new workflow .github/workflows/docs-regen.yml (push to main, guarded to the paths that affect the rendered output) runs the same two scripts whole-workspace, refreshes the hand-authored index.html counts that check-docs-sync.sh gates, and commits the result back to main with a [skip ci] message (so the bot commit does not re-trigger the workflow). A concurrency group serialises regens.
  • The files stay tracked and Cloudflare’s Git-integration deploy (ADR-0052) is unchanged — it still serves the committed docs/site/ verbatim; only the writer moved from every developer’s commit to one post-merge CI job.

Options considered + consequences

Option 1 (chosen) — Regenerate on main in CI, keep the files tracked

  • Pros: eliminates the entire class of generated-doc merge conflict with no change to the Cloudflare deploy model or any new secret; the rendered site stays in the repo (diffable, servable verbatim); index.html counts self-heal so a PR that adds a crate/ADR no longer has to hand-edit them.
  • Cons: the ~768 files remain in the repo (history churn moves to a post-merge bot commit); the deployed site is briefly stale between a merge and the regen job finishing; the bot needs permission to push to main.
  • Cost: one CI job per docs-affecting merge (~a couple of minutes).

Option 2 — Untrack the generated trees; build + deploy from CI via Wrangler

  • Pros: the generated artifacts leave the repo entirely (no churn, no tracked build output).
  • Cons: replaces ADR-0052’s Cloudflare Git-integration with a Wrangler direct-upload deploy, needs a CLOUDFLARE_API_TOKEN secret, and is a larger change to a working deploy path.
  • Recommendation: rejected for now as disproportionate; revisit if the tracked generated tree becomes a repo-size problem. Recorded so the trade-off is explicit.

Option 3 — Do nothing; keep resolving the conflicts by hand

  • Rejected: the conflicts are pure toil on a generated artifact and recur on every docs-touching PR.

Operational note

The regen job pushes to main, so it authenticates as a dedicated GitHub App (Contents:write, installed on this repo) rather than the default GITHUB_TOKEN: the job mints a short-lived installation token with actions/create-github-app-token and pushes as the App’s bot identity. That bot must be added to main’s ruleset / branch-protection bypass list, or the push to the protected branch is rejected. Secrets required: DOCS_APP_ID, DOCS_APP_PRIVATE_KEY. If the bypass is ever withdrawn, switch the final step to open a PR instead of pushing directly.

Document version history

VersionDateNotes
0.12026-08-05Initial draft alongside the pipeline change; amends ADR-0059.