Skip to content

Decisions

This section contains decision records for the Vultron project.

What is an ADR?

An architectural decision record (ADR) is a document that captures an important architectural decision made along with its context and consequences. We're using the expanded concept of an Any Decision Record (ADR) to capture any decision that is important to the project, not just architectural decisions. We use Markdown Any Decision Records (MADR) to document our architectural decisions.

When to write an ADR

The primary signal for an ADR is evaluated alternatives: if you considered more than one option and rejected at least one, document the decision. The record preserves context for future maintainers who might otherwise re-open a settled question.

Concretely, write an ADR when:

  • You adopted a structural or architectural approach over one or more alternatives (e.g., hexagonal architecture over layered, SQLModel over TinyDB).
  • You made a one-time process or tooling decision with lasting project-wide impact (e.g., CalVer over SemVer, pinning CI action SHAs).
  • A decision will be hard or costly to reverse, so the rationale should be preserved explicitly.

ADR vs. spec: an ADR records why a choice was made; a spec entry records what the system must do going forward. When a significant decision also generates recurring testable requirements, create both — see notes/specs-vs-adrs.md for the full delineation guidelines and worked examples.

You do not need an ADR for:

  • Uncontested conventions with no real alternatives (write a spec entry instead).
  • Small tactical choices where the rationale is obvious from the code.

If you're unsure, err on the side of writing one — a brief ADR is better than losing context.

Revising vs. amending an ADR

An ADR is a historical record. It answers what we decided and why, given what we knew at the time — a question whose answer cannot go stale, because the past does not change. It is not a description of the current codebase.

That distinction decides most editing questions. When an ADR names a function, a module path, or a class that was later renamed or deleted, the decision still stands and the record is still accurate about the decision; only its description of the code has aged.

A refactor is never responsible for updating an ADR. Renaming a symbol or splitting a module does not oblige you to touch any ADR that mentions it. If a decision has consequences that future code must obey, those belong in a spec requirement or a notes/ file, which carry an obligation to stay current that an ADR does not. Record them there and cite the ADR as provenance — see notes/specs-vs-adrs.md for the delineation and MS-11-004 for the bidirectional citation pattern. A spec or note MUST NOT resolve its own meaning by pointing into an ADR body; cite the ADR for why, and state the rule where the reader already is. MS-11-007 makes that normative for any numbered part of a body, whether a detail, rule, step, option, part, invariant or section number, and a test fails any such citation under specs/ or notes/. An ADR citing a numbered part of another ADR is fine — that is history citing history.

An aged code reference in an ADR body is not a defect. Per ADR-0127, the existing stale paths and symbols across the corpus are not a backlog and no scrub is owed: a reference in a record's context or validation prose is an accurate account of the code as it then stood. Enforcement is forward-only — a reference newly written into an ADR should resolve on the day it is written. The adr-added-reference-check pre-commit hook and its pull-request twin fail a backticked path or code symbol that an edit adds and that resolves nowhere, and never judge a reference already on the merge base (MS-15-006). A record that deliberately names a removed symbol opts out with lint_suppress: [phantom_symbol_ref], or [phantom_path_ref] for a path (MS-15-007).

How to edit an ADR: follow the edit tiers in ADR-0120, which key off the ADR's epoch: how long ago it was last materially changed, and whether a tested spec requirement depends on it yet. Editorial fixes and appended annotations are allowed at any time and change neither updated nor revision. A clarification that does not alter what anything built on the ADR must do is appended as a dated note, rather than written over the original text. A material change bumps updated and revision; on an accepted-provisional ADR, ask the human whether to edit in place or supersede. On an accepted ADR, a material change to one detail with the chosen option intact is a dated Amendment that quotes the text it replaces. A change of the chosen option is a new ADR that supersedes the old one; the old one is restored to its original text, marked superseded, and moved to archived/ (MS-14-004). Never rewrite an accepted ADR under its own number. The adr-lifecycle-check pre-commit hook holds one part of this mechanically: on an ADR that is accepted, or was last changed 28 or more days ago, it refuses a changed ## Decision Outcome or ## Considered Options section that adds no new dated Amendment heading (MS-14-009). It also refuses an updated bump that carries no status_override on an ADR past its first epoch (MS-14-009). It reads no other section and does not check that an Amendment quotes what it replaces, so the remaining tiers are yours to honour.

What not to write into an ADR body. Implementation detail that drifts independently of the decision costs a future reader — including an agent with a limited context window — attention on claims that were true once and are not checkable now.

  • A statement about a completed migration step — "X is deleted", a struck-through progress list, a renaming checklist — is project tracking, not a decision. Record it in the issue that does the work. Deleting it is an editorial edit wherever it sits outside ## Decision Outcome and ## Considered Options. Inside either of those, on an accepted ADR, the hook above refuses the deletion on its own, so leave it for the next material change or supersession to carry.
  • A count, a metric, or a file.py:line citation goes stale on the next unrelated edit. State the rule and point at where the live answer lives. MS-16-001 and MS-16-002 in specs/meta-specifications.yaml set out the same principle for specs, notes and AGENTS.md; extending it to ADR bodies, with a check behind it, is tracked in #4389.
  • A description of how the code looked when the decision was made is legitimate context, because it is part of why the decision was needed. Write it so it reads as of its time, and annotate anything since removed. ADR-0063 is the house style, noting inline that "that mechanism and that module were deleted in #2940". A reference marked that way is dead but not misleading.

On the former guidance in this section. Until 2026-10-09 this section asked authors to revise an accepted ADR body in place so that a reader would meet only currently-accurate statements (ISSUE-1777), and discouraged appended amendments for the same reason. That pursued the goal above — keeping stale detail out of a reader's way — through the only mechanism available while every ADR body was read as routine context: keeping the bodies current. Keeping implementation detail out of the body in the first place serves the same goal without asking a historical record to change, and without conflicting with the lifecycle rules that ADR-0120 and MS-14-009 now set. Records revised in place under the former guidance, such as ADR-0098, are left as they are.

How to write an ADR

For new ADRs, please use adr-template.md as basis. More information on MADR is available at https://adr.github.io/madr/. General information about architectural decision records is available at https://adr.github.io/.

Accepted ADRs

Proposed ADRs

Rejected ADRs

  • none

Superseded / Archived ADRs

Retired ADRs (status: deprecated or superseded) are moved to docs/adr/archived/ so they stay out of the default docs/adr/ context sweep. Each is listed here with a forward link to its replacement.