Decisions and ADRs
A decision is a file with a status. Proposed, then accepted, then, when it changes, superseded by a newer accepted one. The trail is never edited away.
What deserves an ADR
Not every change. A small fix inside an already-decided area just gets built. An architecture decision record is for the decisions that other work depends on and that an agent must not quietly contradict later:
- Architecture style and module boundaries.
- Dependencies.
- Auth, secrets, storage, networking, telemetry, or file-write behavior.
- Security posture.
- Runtime integrations such as MCP servers.
Persist OS does not judge the content. Supabase, Firebase, Postgres, or an internal system are all equally valid once the repository has accepted them. Drift means mismatch with what the repository accepted, not mismatch with a tool's preference.
Files and naming
docs/adrs/ADR-####-<slug>.md-
A numbered ADR.
persist adr createwrites one with StatusProposed;persist adr acceptflips it toAccepted. Accepted ADRs sit at the top level of the directory; there is noaccepted/subfolder. docs/adrs/proposed/ADR-PROPOSED-<slug>.md-
An unnumbered proposal, as written by
persist adoptandpersist mcp add. Accepting it assigns the next number, writes the numbered file, and removes the proposal. docs/adrs/README.md- The index the agent starts from.
The template
Every ADR from persist adr create starts as:
# ADR-0001: Use PostgreSQL For Primary Storage
## Status
Proposed
## Context
What decision needs to be made, and why now?
## Decision
What option is proposed?
## Alternatives Considered
What other options were considered?
## Consequences
What improves, what worsens, and what risks remain?
## Related Documents
- PRD:
- Architecture:
- Security:
- Feature:
The doctor reads the Consequences and Alternatives Considered sections. If either is
empty or still the prompt, that is a warning on a proposed ADR and an error on an
accepted one: an accepted decision with no recorded consequences, or no alternatives,
is not finished. When the Decision itself is about auth, secrets, or similar, the
Security: line under Related Documents must point at something, or the
doctor warns.
The lifecycle
Proposed
Someone writes the decision down. That can be a person running
persist adr create, an agent recording a decision it just made,
persist adopt inferring a framework from a manifest, or
persist mcp add proposing an integration. In every case the status is
Proposed and nothing else in the memory may treat it as settled. The doctor warns
when a feature or module cites an ADR that is not yet accepted.
Accepted
A human runs persist adr accept <slug>. From then on the ADR is
source of truth, ranked first in the source-of-truth order, and the generated agent
rules say never to contradict it.
$ persist adr accept use-postgresql-for-primary-storage
Persist OS ADR accept complete.
Accepted: docs/adrs/ADR-0001-use-postgresql-for-primary-storage.md
Overwritten:
- docs/adrs/ADR-0001-use-postgresql-for-primary-storage.md
Next steps:
- docs/adrs/ADR-0001-use-postgresql-for-primary-storage.md is now Accepted and is repository source of truth.
- Drift checks now treat references to it as resolved.
Superseded
Decisions change. The wrong way to record that is to edit the accepted file, which
erases the fact that the repository once decided otherwise. persist adr
supersede <old> <new-title> does it the other way: a new numbered ADR
is written as Accepted with a ## Supersedes link, and the old ADR's status
line is rewritten to say what replaced it. Only an accepted ADR can be superseded; the
command refuses otherwise. It also refuses a replacement title that another live
decision or proposal already holds, naming the existing file — two accepted
decisions under one title is the drift the doctor reports as an error.
$ persist adr supersede use-postgresql-for-primary-storage "Use SQLite for primary storage"
docs/adrs/ADR-0001-use-postgresql-for-primary-storage.md
## Status
-Accepted
+Accepted — superseded by ADR-0002-use-sqlite-for-primary-storage
docs/adrs/ADR-0002-use-sqlite-for-primary-storage.md (new file)
+# ADR-0002: Use SQLite For Primary Storage
+
+## Status
+
+Accepted
+
+## Supersedes
+
+- ADR-0001-use-postgresql-for-primary-storage
+
+## Context
+
+What changed, and why the previous decision no longer holds?
The old status keeps the word "Accepted" on purpose. Memory that cites ADR-0001 is still citing a real, once-accepted decision, so it is not a broken reference. It is a reference that needs a look, and that is exactly what the doctor reports:
Next steps:
- Fill docs/adrs/ADR-0002-use-sqlite-for-primary-storage.md: Context (what changed), Decision, Alternatives, Consequences.
- ADR-0001-use-postgresql-for-primary-storage stays in history as superseded; update any memory that still relies on it.
- Run persist doctor — it flags memory that still references the superseded decision.
How the doctor treats decisions
| Situation | Result |
|---|---|
| Feature or module memory cites an ADR that does not exist | error |
| Memory cites an ADR that is still Proposed | warning |
| Memory cites an ADR that has been superseded | warning |
| Two accepted ADRs share one title | error |
| A proposal shares the title of a live accepted ADR | warning |
| An accepted ADR has no substance in Consequences or Alternatives Considered | error |
| A proposed ADR has no substance in Consequences or Alternatives Considered | warning |
| A decision written in security-sensitive terms has no security document linked under Related Documents | warning |
| An ADR is missing a required section | error |
| One of the six required documents links to a doc path that does not exist, or cites an ADR that does not exist | error |
What agents are told
The generated AGENTS.md and Cursor rule carry the commands inline so the
agent runs them itself: a real decision means persist adr create then
persist adr accept; changing one means confirming with a human and running
persist adr supersede, never overwriting an accepted ADR. The SessionStart
hook lists the accepted ADRs at the start of every Claude Code session, so a fresh agent
knows they exist before it starts work.
What this does not catch
The doctor checks structure and references, not meaning. If an accepted ADR says PostgreSQL and the code quietly moved to MySQL, the file is still there, the references still resolve, and the doctor passes. Catching that needs reading, so it is left to the people and agents doing the work. The gate stays deterministic; the model does the reading.