ADR conventions and documentation pipeline
Ce contenu n’est pas encore disponible dans votre langue.
Status note (2026-07-13): the
site/paths referenced below moved to the privateartist-alley-siterepo at the 1.55.Z site split (v0.1.0, 2026-07-11). The ADR source of truth (docs/adr/) stays in this repo; the render pipeline (sync-adrs.mjs, Astro components) now lives in the site repo.
Context
Section titled “Context”The first 34 ADRs accreted organically. Each carried date + status as plain markdown bullets, sections were inconsistent (some carried “Implementation”, others didn’t; some had “References”, others “Reference”), and cross-references between ADRs were prose mentions the docs pipeline couldn’t index. As the catalogue grew past ~30 entries, the operational pain showed:
- No way to ask “which ADRs affect Phase 1.42?” without grep.
- No way to know an ADR was superseded except by reading it.
- No filter UI on the docs site — every ADR was equally surfaced regardless of status.
- No automated lint — a typoed reference to “ADR 0038” silently pointed at nothing.
This ADR establishes the conventions that turn the ADR catalogue from a folder of markdown files into a structured, queryable, interconnected docs-site artefact.
Decision
Section titled “Decision”Every ADR ships with YAML frontmatter, follows a strict section structure, and is consumed by a build-time pipeline that validates references, derives inverse links, and renders an automated metadata card at the top of each page.
Frontmatter schema
Section titled “Frontmatter schema”---id: "0035" # zero-padded string, matches filename prefixtitle: ADR conventions ... # short title, matches first H1status: accepted # see lifecycle belowdate: 2026-05-31 # YYYY-MM-DD, the decision datearea: process # see area taxonomy belowphases: # roadmap phase IDs this ADR informs - "1.42" - "1.18.B-3"supersedes: [] # ADR IDs this replacesrelated: # other ADRs that interact - "0017"tags: # free-form topical tags - documentation - toolingexcerpt: >- # one-sentence summary, shown on the index Short single-sentence excerpt for catalogue list view.---Required fields: id, title, status, date, area,
excerpt. Optional: phases, supersedes, related, tags.
Strings stay quoted; arrays may be empty ([]).
Status lifecycle
Section titled “Status lifecycle”| Status | Meaning |
|---|---|
proposed | Drafted, open for discussion. Renders with a yellow “Proposed” badge. |
accepted | Decision is in effect. Renders with a green badge. The default for landed ADRs. |
superseded | A later ADR replaces this one. Renders with a grey badge + a Superseded by ADR XXXX callout. Body content stays intact as historical record. |
deprecated | The thing this ADR governs no longer exists or no longer matters. Renders with a grey badge + a “Deprecated” callout. |
rejected | Drafted but not adopted. Kept as historical record. |
Status transitions are one-way per row — once superseded, an ADR
does not return to accepted. A new ADR with a clean id is the
correct path forward.
Area taxonomy
Section titled “Area taxonomy”| Area | Examples |
|---|---|
architecture | Backend shape, data model, service topology |
security | Auth, encryption, secrets management |
licensing | License model, dual-license direction |
monetization | Pricing tiers, ad / commerce model |
process | Conventions, workflow, branching, release |
ux | Frontend patterns, design system, accessibility |
ops | Operability, observability, deployment |
infrastructure | Storage, federation, networking |
extensibility | Add-ons, plugins, integrations |
Areas are used for grouping in the sidebar and as filter facets on the ADR index page. Adding a new area is a code change in the sync script (so it’s intentional, not accidental).
Section structure
Section titled “Section structure”The body uses a fixed section ordering. Sync-time linter rejects ADRs that diverge:
# ADR XXXX: <Title>
## Context
Why we are deciding this. What constraints / problems / failuremodes drove the decision. Cite specific failure modes that motivateeach major decision point.
## Decision
What we are deciding. State it crisply. Use sub-headings(`### Sub-decision`) when the decision has multiple components.
## Consequences
### Positive
Expected benefits.
### Negative
Costs, risks, accepted tradeoffs. Be honest — a consequencessection that has only positives is a tell that the decision wasnot actually weighed.
## Alternatives considered
What we rejected and why. One bullet per alternative. Brief — ifan alternative is complex enough to need paragraphs, it shouldprobably be its own ADR.
## Implementation # optional
Concrete shipping plan: phase breakdown, code locations, contracts.Omit if the decision is purely architectural.
## References # optional
External links + cross-references the prose section didn't alreadymention. The pipeline auto-renders the related-ADR card fromfrontmatter — references here are for *external* resources.Cross-reference rules
Section titled “Cross-reference rules”- Phase IDs match the roadmap JSON exactly:
1.42,1.18.B-3,1.18.A. Sync-time linter rejects unknown phase IDs. - ADR IDs match the filename prefix:
0017,0034. Sync-time linter rejects unknown IDs and emits a warning when an ADR has no inbound links (potential orphan). - External links use fully-qualified URLs.
File naming
Section titled “File naming”docs/adr/NNNN-slug-with-hyphens.md where NNNN is zero-padded
four-digit. New ADRs claim the next free number. Renaming an
existing ADR’s slug is fine; the id field is the stable handle.
Pipeline behaviour (sync-adrs.mjs)
Section titled “Pipeline behaviour (sync-adrs.mjs)”The sync script does five things on every site build:
- Parse + validate every ADR’s frontmatter against the schema. Fail the build on missing required fields, unknown status, unknown area, unknown phase / ADR cross-references.
- Derive inverse maps —
superseded_byfromsupersedes,back_linksfromrelated, andadrs_by_phaseso the roadmap visualization can show “see ADRs 0034, 0026” next to each phase. - Emit augmented MDX into
site/src/content/docs/adr/— each ADR’s frontmatter gains the derived fields and the rendered page lays them out via the<AdrCard>component. - Generate the index page
site/src/content/docs/adr/index.mdxwith a filterable table of every ADR (status, area, date, title, excerpt). - Lint orphan warnings — ADRs with zero inbound links + no
roadmap-phase reference probably need either a
related:entry somewhere or a check that they’re still relevant.
Site-side rendering
Section titled “Site-side rendering”Each ADR page renders an <AdrCard> block at the top:
- Status badge colored by lifecycle state.
- Area + date as the secondary line.
- Supersedes trail — visible block showing this ADR replaces ADR XXXX (with link), or is replaced by ADR YYYY.
- Related ADRs — chip row of linked ADR IDs + titles.
- Related phases — chip row linking to roadmap entries.
The body follows below in regular Starlight prose styling.
The ADR landing page at /adr/ is a filterable table — facets
for status, area, and (related-)phase — so anyone landing on the
docs site can sweep the catalogue.
Consequences
Section titled “Consequences”Positive
Section titled “Positive”- Cross-references become real graph edges rather than prose mentions — broken links are caught at build, the docs site can render bidirectional connections.
- The catalogue scales: 100 ADRs is queryable through the index filters; the sidebar groups stay coherent through area buckets.
- Frontmatter is the source of truth for ADR metadata, so external tooling (changelog generators, project planners) can consume the catalogue programmatically.
- Status lifecycle is explicit. A superseded ADR no longer looks like an active recommendation.
- Authoring a new ADR is paint-by-numbers: copy a template, fill the frontmatter, write the body in the standard sections.
Negative
Section titled “Negative”- Existing 34 ADRs need a one-time migration to the new format. Mitigation: script-assisted bulk migration (see Implementation).
- The frontmatter schema is now a contract — adding fields needs care so older ADRs don’t break. Mitigation: every field is optional except the documented required set; defaults are applied for omitted fields.
- Authors need to remember the section order. Mitigation: linter fails the build with a clear error message naming the missing section.
Alternatives considered
Section titled “Alternatives considered”- Keep the loose markdown shape and add a docs-site table by hand. Doesn’t scale past a handful of ADRs; broken cross-refs stay broken. Rejected.
- Use a third-party ADR-tooling project (adr-tools, adr-manager, etc.). Most tools target the standalone CLI / repo flow, not docs-site integration. We’d end up with two formats to reconcile. Rejected.
- YAML-only ADRs with rendered markdown body. Removes the human-readable plain-markdown source. Authors edit prose, not data structures. Rejected.
Implementation
Section titled “Implementation”Sub-phases all land together (single PR / commit on
feat/viewer-polish):
- This ADR (0035) ships first to establish the standard.
- Bulk migration script adds frontmatter to ADRs 0001–0034 by
parsing existing
- Date:+- Status:lines and inferringarea+phases+relatedfrom body content. Output is reviewed before commit. site/scripts/sync-adrs.mjsupgraded to validate, derive inverse maps, augment MDX, and emit the index page.site/src/components/AdrCard.astrorenders the metadata block.site/src/components/AdrIndex.astrorenders the filterable landing page; consumed by the newsite/src/content/docs/adr/index.mdx.site/astro.config.mjssidebar grouping switches from flat autogenerate to area-grouped + status-aware ordering.
References
Section titled “References”- Michael Nygard’s original ADR pattern — the structural inspiration.
docs/adr/— the catalogue this ADR governs.