The Docs Platform β ADR-112
The Docs Platform β ADR-112
One ruling. It selects the technology for a new deployable,
apps/descent-docs, and touches nothing that ships to a player: no wire format, no quantity, no game-route dependency. It is an ADR rather than a scaffold-and-go because P8 forbids mandating a technology without recording its seam and its exit cost at adoption time β and a documentation platform is precisely the kind of adoption whose exit cost gets discovered instead of recorded, three custom plugins too late.
ADR-112 β Descent Docs Is Built on Astro + Starlight, with Pagefind Search
Status: Accepted Β· Date: 2026-08-08 Β· No ADR is amended or superseded Β· interacts with 110, 111
Context
The architecture corpus is ~60 Markdown files with five lints and a habit of adversarial
review. It is read today through GitHubβs renderer, which costs nothing and does nothing: no
search, no cross-file Q-ID resolution, no way to render β provenance inline, and no
pipeline between the corpus and a reader that could enforce anything.
Two planned capabilities decide the shape of the platform:
- The
Q-IDRegistry Resolver β an MDX/remark plugin that turns everyQ-012-style citation into a live link carrying the quantityβs value, status and provenance fromQuantity_Registry.md, so a stale number in prose becomes a build error rather than a discovery. - The schema scrubbing pipeline. ADR-111 closed the core engines; the corpus cites their internals β ceiling names, enforcement seams, fuzz-target shapes. Publishing the docs anywhere less private than the repository requires a build stage that scrubs or gates that material, and it must run inside the site build so an unscrubbed page cannot be produced by accident.
Both are content transforms, which is what makes the platform choice mostly a choice of plugin substrate.
Decision
- Astro (7.2.0 at adoption) is the site framework, Starlight (0.41.7) the
documentation theme, Pagefind the search β the latter arriving as Starlightβs own
dependency (
pagefind ^1.5.2), so it is deliberately not declared separately: one declaration, no version pair to drift. - Static generation only, zero JS by default. The site is a rendering of a Markdown corpus; there is no server state, and every kilobyte of hydration is unearned. Pagefind fits the same constraint: it indexes at build time and serves static fragments, so search adds no server either.
- The corpus in
docs/remains the single authoritative source (P6). The site renders it; it never forks it. The scaffold ships with Starlightβs template content as placeholder, and the corpus-ingestion step β renderingdocs/through the site rather than copying into it β is explicitly future work, because a copy that drifts is the exact W6 defect this corpus keeps a name for. - The app lives at
apps/descent-docs, a standalone pnpm project pinned to the samepnpm@11.20.0bypackageManagerand guarded by the samepreinstallcheck as the VTT client β ADR-110βs installer discipline extended to a second app rather than re-decided.
The five questions (tech-stack-currency.md Β§5)
- Single-implementation / single-source: preserved β clause 3 is that property stated for
content. 2. Structural boundary: the unified/remark plugin API is typed and versioned;
a misuse is a build error in the site, never a change to the corpus. 3. Loud failure:
an Astro build failure is a red CI job; Pagefind indexing nothing is visible as an empty
search UI in the same PR preview. 4. Deployment profiles: emits static files, which is
the one artefact every profile can host. 5. Stable or new: Astro 7 is the stable line
(
latest, with 4.x relegated to alegacytag); Starlight is 0.x and gets the Kobalte treatment β pinned exact, changelog read on every bump.
Exit cost β recorded at adoption, because that is when it is cheap (P8)
High, and asymmetric across the three layers:
- The content is free to leave. It is Markdown in
docs/, owned by the corpus rules, and clause 3 means the platform never becomes its home. Whatever replaces Astro renders the same files. - The remark/rehype plugins are portable in principle. The
Q-IDresolver and the scrubberβs transform logic target the unified AST, which Docusaurus, Next MDX and every serious Markdown pipeline also consume. Written as plain unified plugins with the Astro-specific wiring kept to config, they survive a framework exit as libraries. - Everything else does not. Routing, theming, Starlightβs component overrides, content collections, the Pagefind integration and β critically β the build pipeline the scrubber hooks into are Astro-shaped. Retracting this decision means rewriting the rendering and routing pipeline on another framework (Docusaurus, Next.js, VitePress), and re-verifying the scrubbing gate end-to-end on the new pipeline, because a scrubber that silently stopped running is a disclosure incident rather than a build bug.
The mitigation is a rule, stated here so it binds the first plugin written: custom logic
lives in framework-agnostic unified plugins (and, for the scrubber, a stage that can also run
as a standalone CLI over the output directory); astro.config.mjs is wiring only. The seam
registry row in upgrade-and-supersession.md Β§4 carries this as the swap point.
Alternatives Considered and Why Rejected
- Status quo β GitHubβs renderer. Free, and does nothing: no search, no resolver, no scrubbing pipeline, which are the point. Rejected on capability, not on cost; it remains the fallback if the platform is ever retired.
- Docusaurus. The closest capability match, and React end-to-end. This workspace has
kept React out of every frontend it owns (
frontend-guidelines.mdΒ§2.3 forbids proposing it for the client; Rete.js was chosen elsewhere because it is framework-agnostic), and a docs site is not the place to adopt a second UI frameworkβs ecosystem, hydration model and upgrade cadence. - Next.js. A server runtime and a React app for a corpus that is static files; almost every capability it adds is one this site must not use.
- VitePress. Genuinely light and fast, but Vue-flavoured Markdown rather than MDX and a thinner theme/plugin surface for the resolver work; choosing it would trade the primary requirement for a smaller dependency tree.
- mdBook / plain SSG (Eleventy, Hugo). No MDX substrate for the resolver; custom work would start from raw Markdown parsing, which is the expensive half of both planned plugins.
Consequences (including negative)
- A fourth Node toolchain surface to keep current (client, tools, CI, now docs). Astro majors have shipped roughly yearly; each is a deliberate bump of this app alone, and the register row carries the trigger.
- Starlight at 0.x means minor versions may break β accepted with the exact pin, and it is the likeliest source of routine maintenance here.
- The scrubbing pipeline being inside the site build couples a security control to this framework β that is the largest single component of the exit cost, named above, and the standalone-CLI mitigation exists specifically for it.
- Until the corpus-ingestion step lands, the site shows template content, and nothing the platform serves is authoritative. Stated so the scaffold is not mistaken for a published corpus.
Rights-holders (ADR-079)
None. The platform renders documents this repository already carries; no new class of data about any person is retained.
Enforcement
docs-platform-ci.ymlbuilds the site on every change toapps/descent-docs/**β the build is the gate, with the workflow-levelshell: bash(pipefail) discipline from the 2026-08-06 audit.packageManager+ thepreinstallguard enforce the installer, exactly as ADR-110 does for the client.- The corpus lints are untouched and remain the authority over
docs/β the platform adds a renderer, never a second set of rules. - The scrubbing gate, when it lands, needs its own enforcement point (an assertion over the built output, not the pipelineβs presence) β named here as an obligation so its ADR cannot forget it.
Technology currency
Checked 2026-08-08 against npm dist-tags, read directly: astro 7.2.0 is latest (the
4.x line is tagged legacy; the alpha/beta tags predate 7.0 and are stale); @astrojs/starlight
0.41.7 is latest and peers astro ^7.0.2; pagefind 1.5.2 is latest and is a
direct dependency of Starlight at ^1.5.2, which is why it appears in no manifest of ours.
Node engine floor is 22.12; the workspace runs Node 24 in CI.