--- orphan: false --- # Versioned documentation: design and rollout ```{versionadded} v0.2 The per-version `switcher.json` scaffolding lands with v0.2. The companion changes to MICROROBOTICA's umbrella `conf.py` + docs CI are required to make the switcher live on the published site — that work has since landed in MICROROBOTICA's umbrella docs. ``` ## Settled design decisions These are the choices made for the v0.2 rollout. Captured here so that the rationale survives turnover; revisit them when the v0.3 docs cycle starts. | # | Decision | Choice | Reason | |---|---|---|---| | 1 | What does `latest` point at? | **Most recent git tag** (not main tip) | New visitors land on stable; bleeding-edge work moves to `/dev/`. Read-the-Docs convention. | | 2 | Where does `switcher.json` live? | **In each project's repo** (`MADDENING/docs/_static/switcher.json`) | Projects progress somewhat independently; each owns its release cadence. | | 3 | Version label convention | **`vMAJOR.MINOR`** for minor releases (each new minor demotes the previous), **`vMAJOR.MINOR.PATCH`** when a patch carries breaking changes worth a distinct switcher entry (e.g. v0.2.1's edge-validation flip), **`vMAJOR.MINOR-dev`** for in-flight branches | Pins to semver; the `-dev` suffix marks pre-release without polluting the tag namespace; the PATCH entries are used sparingly so the switcher doesn't bloat. | | 4 | `preferred: true` row | **Always the latest stable tag** | Standard PyData theme convention; what new readers should see by default. | | 5 | Does MICROROBOTICA + MIME get versioned too? | **Yes — all three** | MICROROBOTICA ships a C++ API that breaks compatibility on minor bumps; MIME's nodes evolve alongside MADDENING. Each release tag freezes its own docs. | | 6 | CI build strategy | **Cache + only rebuild changed versions** (option C from the brainstorm) | Old tagged builds are immutable artefacts; rebuild only on tag-cut, cache forever. `main` + the latest tag rebuild on every push; older tags reuse a `gh-pages-versions` artefact. | | 7 | Old version's incompatible `conf.py` | **For now, build all versions with the current umbrella `conf.py`**; switch to per-version pinned requirements once we have 5+ versions | Cheap until incompatibility actually bites; the pin-file pattern is a known fallback. | | 8 | Glossary across versions | **Per-version (frozen at release)** | Each version stays accurate to its source; readers searching old docs see the terms they actually shipped with. | | 9 | Switcher fallback when JSON unreachable | **Show a debug banner + console warning** | Hidden silent failures are the worst kind; make broken deploys visible immediately. | | 10 | URL shape for older releases | **Subdir** (`/maddening/v0.1/`) — not subdomain | Single TLS cert, single GitHub Pages host, fits the multiproject build pipeline. | These shape the rollout described below. The [`multiversion_preview.md`](https://github.com/Microrobotics-Simulation-Framework/MICROROBOTICA/blob/feat/versioned-docs-preview/docs/developer_guide/multiversion_preview.md) guide in MICROROBOTICA covers the local-iteration workflow that implements them. The three MSF projects (MADDENING, MIME, MICROROBOTICA) are published from a single sphinx-multiproject build at . Today that build emits a single "latest" version of each project — there's no `v0.1` URL you can deep-link to. When v0.2 lands as the new stable release, v0.1 documentation effectively disappears. This page is the rollout plan for keeping every supported version addressable at a stable URL with a UI version picker. ## The shape of the live site (target) ``` https://microrobotica.org/ — MICROROBOTICA latest https://microrobotica.org/v0.1/ — MICROROBOTICA v0.1 https://microrobotica.org/maddening/ — MADDENING latest (== current main) https://microrobotica.org/maddening/v0.2/ — MADDENING v0.2 (stable) https://microrobotica.org/maddening/v0.1/ — MADDENING v0.1 https://microrobotica.org/mime/ — MIME latest https://microrobotica.org/mime/v0.2/ — MIME v0.2 ``` * `//` always points at `latest` (= the most recent tagged release, not `main`). `main` content lives at `//dev/` if someone wants pre-release docs. * Each version is a frozen build of the docs in that release's `docs/` tree against the Sphinx config at that tag. Switching versions doesn't switch *just* the content — the navbar, theme, and inventory all freeze together. ## How a user sees it: PyData theme's version switcher PyData Sphinx Theme ships a built-in [version switcher](https://pydata-sphinx-theme.readthedocs.io/en/stable/user_guide/version-dropdown.html) that reads a JSON file at build time and renders a dropdown in the navbar. We use it because the umbrella site already runs PyData theme (`MICROROBOTICA/docs/conf.py` line ~70). Each project ships its own `switcher.json` at `docs/_static/switcher.json`, e.g. {download}`MADDENING's <../_static/switcher.json>`: ```json [ {"version": "latest", "name": "latest (main)", "url": "https://microrobotica.org/maddening/"}, {"version": "v0.2", "name": "v0.2 (stable)", "url": "https://microrobotica.org/maddening/v0.2/", "preferred": true}, {"version": "v0.1", "name": "v0.1", "url": "https://microrobotica.org/maddening/v0.1/"} ] ``` The umbrella `conf.py` wires it into the theme: ```python html_theme_options.update({ "switcher": { "json_url": f"{_BASEURLS[project]}_static/switcher.json", "version_match": os.environ.get("DOCS_VERSION", "latest"), }, "navbar_end": ["theme-switcher", "version-switcher", "navbar-icon-links"], }) ``` `DOCS_VERSION` is set by the CI build per tag — `latest` for the `main` build, `v0.2` for the `v0.2.0` tag, etc. ## How a build produces all the versions ```{mermaid} flowchart LR Tags["git tags
v0.1.0 · v0.2.0 · ..."] --> Matrix Main["main branch"] --> Matrix Matrix["docs.yml matrix:
{ tag in tags, 'main' }"] --> Build Build["checkout @ tag/main
DOCS_VERSION=tag make all
OUT=_build/html/{project}/{tag}/"] --> Merge Merge["merge into root _build/
aliases: latest → newest tag"] --> Deploy Deploy["github-pages deploy"] ``` The umbrella `Makefile` already builds each project sequentially (`make all` → `_build/html/{maddening,mime,microrobotica}/`). The multi-version extension is: 1. Loop over `git tag --list 'v*'`. 2. For each tag, `git worktree add /tmp/wt-$tag $tag`, then `cd docs && DOCS_VERSION=$tag make all OUTDIR=_build/html-$tag/`. 3. For `main`, the build is the same but with `DOCS_VERSION=latest`. 4. After all builds, symlink `_build/html/latest → _build/html-$latest_tag/` and copy each `_build/html-$tag/` into `_build/html/{project}/$tag/`. 5. Each project's root `switcher.json` lives at `_build/html/{project}/_static/switcher.json` and is the same file in every version's output (so the dropdown lists every version regardless of which one the user is currently on). ## Why this design over alternatives | Option | Pros | Cons | Verdict | |---|---|---|---| | **sphinx-multiversion** | Automatic git-tag enumeration; single command. | Each historical tag must build with the *current* `conf.py`; breaking config changes cascade across history. | Rejected — we expect `conf.py` to evolve. | | **Read-the-Docs** | Hosted, automatic. | Not the umbrella architecture; would need a separate domain per project. | Rejected — keep the unified microrobotica.org URL. | | **Manual switcher.json + CI matrix** *(chosen)* | Each version builds against its own `conf.py`; switcher UI freezes whatever we ship; clean rollback. | Switcher JSON has to be updated for every release. | **Chosen.** | | **Single-version + 404 page** | Cheapest. | Loses backwards-discoverable docs. | Rejected — v0.1 users still need their docs. | ## Rollout sequence ```{note} The first three steps are MADDENING-side; the rest require coordinated changes on MICROROBOTICA's umbrella repo. ``` 1. **(this commit)** — `docs/_static/switcher.json` in each project lists `latest` + every released tag. Today only the v0.2 row has a target (we haven't built v0.1 with this infra yet); v0.1 resolves to a 404 until step 4. 2. **(this commit)** — Document the design in this page so the handoff is in version control, not in someone's head. 3. **(MICROROBOTICA umbrella PR)** — add `switcher` + `navbar_end` to `html_theme_options` in `conf.py`. Set `DOCS_VERSION` from env var with `latest` default. 4. **(MICROROBOTICA umbrella PR)** — extend `Makefile` with a `versions` target that loops over `git tag` per project and builds each into the right subdirectory. 5. **(MICROROBOTICA docs.yml)** — switch the workflow from "build once" to "matrix of (project, tag), then merge into a single artifact". Cache builds aggressively because most tags won't change between deploys. 6. **(release process)** — every release adds a new entry to its project's `switcher.json` and a new git tag. The next CI run picks it up automatically. ## How a developer triggers a version-specific local build Until the CI changes land, the per-version path is manual: ```bash # Build v0.2 docs from a checkout at the v0.2.0 tag git worktree add /tmp/maddening-v0.2 v0.2.0 cd ../MICROROBOTICA/docs DOCS_VERSION=v0.2 make maddening # → _build/html/maddening/ # Same for main → _build/html-main/ git worktree add /tmp/maddening-main main DOCS_VERSION=latest make maddening ``` The switcher will appear in the navbar; clicking the v0.1 entry 404s until v0.1 is built into the right subdir. ## What goes in a new release's switcher.json When cutting a new release N: 1. Update `docs/_static/switcher.json`: * Add a new entry for version N marked `"preferred": true`. * Flip the previous stable's `"preferred"` to `false`. * `latest` always points at `//` (the unversioned URL, which the CI maps to the newest tag). 2. Tag the release. 3. Push. 4. CI picks up the new tag, builds it into the right subdir, and the switcher picks it up automatically on the next umbrella build. ## Open questions * **MIME's switcher.json** lives at `MIME/docs/_static/switcher.json` and is authored as of the v0.2 cycle (single `latest` entry today; populates per-version rows as MIME's own release cadence kicks in). * **MICROROBOTICA's switcher** is also authored at `MICROROBOTICA/docs/_static/switcher.json` — same scaffolding, same per-project release lifecycle. * **Do we want a `dev/` alias?** Probably useful for users tracking unstable APIs. Easy add: `/dev/` always maps to the most recent `main` build alongside the tag-pinned versions.