# Step 4.5: Architecture Decision Records (ADRs) **Role**: Architect / technical writer **Goal**: Capture every major architecture, tech-stack, data-model, and integration decision made during Steps 2–4 as a durable, dated, immutable record under `_docs/02_document/adr/`. **Constraints**: ADRs only — do not re-open architecture; do not make new decisions in this step. Document what has been decided, not what is still open. ADRs are the single thing in `_docs/` that explains the **why** of each major decision after the conversation history is gone. They are consumed by: - `decompose` Step 1.5 (`steps/01-5_module-layout.md`) — every Accepted ADR is cross-checked against the module-layout proposal; conflicts trigger an explicit Choose between supersede / exception / re-open. - `new-task` Step 4.5 (`SKILL.md` § "Step 4.5: Contract & Layout Check") — every new task is classified against Accepted ADRs as Conflict / Drift / Aligned; conflicts STOP the task with a Choose A/B/C; drift adds an `### ADR Impact` section; alignment adds an `### ADR Compliance` section. - `refactor` Phase 2b.1 (`phases/02-analysis.md`) — every Accepted ADR is diffed against the proposed roadmap; Violations trigger a BLOCKING supersede gate that produces a `supersede_adr_NNN.md` task before any refactor task is created. - `code-review` Phase 7 (`SKILL.md` § "Phase 7: Architecture Compliance") — every changed-files batch is checked against Accepted ADRs; ADR-Violation findings are Critical, ADR-Drift findings are High. Discipline that still relies on the human: when a downstream skill detects a Drift case, the resulting task spec MUST land its `## ADR Impact` / `## ADR Compliance` section; the implementer must address it; the next code-review batch then has the context it needs. Drift left undocumented is the silent-failure path — every consumer hook above is designed to make it visible. ## Inputs - `_docs/02_document/architecture.md` (incl. confirmed `## Architecture Vision`) - `_docs/02_document/glossary.md` - `_docs/02_document/data_model.md` - `_docs/02_document/system-flows.md` - `_docs/02_document/risk_mitigations.md` (and any `risk_mitigations_NN.md` iterations from Step 4) - `_docs/02_document/components/[##]_[name]/description.md` - `_docs/02_document/deployment/` (CI/CD, environments, observability) - `_docs/00_problem/restrictions.md` and `_docs/00_problem/acceptance_criteria.md` (each ADR must reference relevant constraints / AC by ID) - Optional: `_docs/01_solution/solution.md` and `_docs/01_solution/tech_stack.md` (research output) - Optional: `_docs/LESSONS.md` — surface any lesson categories of `architecture` / `dependencies` that bias the recommendation ## What is an ADR (and what is not) Capture an ADR when **all** of the following hold: 1. The decision picks between two or more genuinely valid approaches with meaningful trade-offs. 2. The decision has **downstream consequences** that other decisions, code, or tasks inherit from. 3. The decision is **non-obvious** to a future reader who only sees the final code — they would ask "why was it built this way?" rather than discovering the answer by reading the source. Do NOT create an ADR for: - Naming, formatting, or purely cosmetic choices. - A choice that is fully implied by a single explicit restriction (`restrictions.md` is itself the record — link to it from the architecture doc instead). - A choice the team has not actually made yet — open questions live in `risk_mitigations.md` or `_docs/_process_leftovers/`, not in ADRs. - A technology selection where research already produced an exact-fit selection with one viable option (the research doc is the record — link to the relevant `solution_draft*.md` section). ## Process ### Phase 4.5a: Decision Inventory Walk the inputs and list candidate decisions. For each candidate, record a one-liner: ``` - [decision] — [trade-off summary] — [downstream consumers] — [evidence file:section] ``` Inspect at minimum: | Inspection target | Typical decisions surfaced | |-------------------|----------------------------| | `architecture.md` § layering | Layering style (clean vs hex vs n-tier), which layer owns transactions, how cross-cutting concerns enter | | `architecture.md` § Architecture Vision | The North Star principle (e.g., "edge-first, sync-second"); ADR captures the implication for one specific subsystem | | `data_model.md` | Datastore choice (Postgres vs Mongo), partitioning, soft vs hard deletes, schema evolution strategy | | `system-flows.md` | Sync vs async boundaries, idempotency strategy, retry policy ownership, error envelope shape | | `components/*/description.md` § interfaces | Public-API style (REST vs RPC vs event), versioning strategy, auth/authorization placement | | `deployment/containerization.md` | Single container vs sidecar vs init container, base image lineage | | `deployment/ci_cd_pipeline.md` | Trunk-based vs feature-branch, gate ordering, deploy strategy (blue-green / canary / all-at-once) | | `deployment/observability.md` | Logging stack, metric backend, sampling rate decisions, retention | | `risk_mitigations.md` | Risk-acceptance trade-offs (e.g., "we accept N% data loss in exchange for sub-100ms p99") | | Tech-stack from `_docs/01_solution/tech_stack.md` | Anything where research recorded ≥2 candidates and a winner | Drop any candidate that fails the three "what is an ADR" criteria above. Keep the rest. ### Phase 4.5b: Numbering and Slugs ADRs are numbered globally per project, monotonically, never re-used. 1. List existing files under `_docs/02_document/adr/` matching `^[0-9]{3}_.+\.md$`. 2. The next ADR number is `max(existing) + 1`, zero-padded to 3 digits. 3. The slug is kebab-case, ≤6 words, derived from the decision summary. Example: `001_use-postgres-for-transactional-data.md`, `004_event-driven-cross-component-comms.md`. ### Phase 4.5c: Render One ADR Per Decision For each kept candidate, render the ADR using `templates/adr.md`. Required sections (do NOT omit any): | Section | Content | |---------|---------| | **Number** | `NNN` | | **Title** | One-line decision statement (matches slug) | | **Status** | `Proposed` (only during Step 4.5 iteration) → `Accepted` (after user confirmation at the BLOCKING gate) | | **Date** | YYYY-MM-DD (the date the user confirmed) | | **Deciders** | The user (project owner) — the AI is not a decider | | **Context** | The problem this decision addresses, including links to AC IDs, restriction IDs, risks, and (where relevant) the research draft section | | **Decision** | The chosen approach in one sentence, then the supporting detail | | **Alternatives Considered** | Each alternative with a one-line "rejected because…" | | **Consequences** | Positive (what becomes easier / cheaper / faster) and negative (what becomes harder / locked in / costly to undo). Be honest — every decision has a downside. | | **Supersedes / Superseded by** | Empty initially; updated when a future ADR overturns this one | | **Evidence** | File-and-section pointers into `_docs/` showing where the decision is reflected (architecture.md § layering, components/02_*/description.md § interface, etc.) | After rendering, write each file to `_docs/02_document/adr/NNN_.md`. Keep `Status: Proposed` until the BLOCKING gate. ### Phase 4.5d: Maintain the ADR Index Write or update `_docs/02_document/adr/README.md` with this exact shape: ```markdown # Architecture Decision Records This index lists every ADR for this project, in number order. ADRs are immutable once `Accepted` — new decisions that overturn a prior ADR are recorded as new ADRs whose `Supersedes` field points back, and the original ADR's `Superseded by` field is updated. | # | Title | Status | Date | Supersedes | |---|-------|--------|------|------------| | 001 | Use Postgres for transactional data | Accepted | 2026-05-21 | — | | 002 | Event-driven cross-component comms | Accepted | 2026-05-21 | — | | ... | ... | ... | ... | ... | ``` Sort by `#` ascending. Include all ADRs ever written, even superseded ones — the audit trail is the point. ### Phase 4.5e: Cross-Link from architecture.md In `architecture.md`, every section that reflects an ADR decision gets a one-line trailing reference: ```markdown > See ADR 001 (Use Postgres for transactional data), ADR 003 (Event-driven cross-component comms). ``` Place the reference at the end of the section, after the prose. This lets a future reader of `architecture.md` jump straight to the rationale. ### Phase 4.5f: BLOCKING Gate — User Confirmation Present the ADR set to the user using the Choose format from `.cursor/skills/autodev/protocols.md` (or plain text if AskQuestion is unavailable): ``` ══════════════════════════════════════ DECISION REQUIRED: ADR set captured (N records) ══════════════════════════════════════ 001 — [title] 002 — [title] ... ══════════════════════════════════════ A) Accept all ADRs as written B) Edit specific ADRs (numbers and edits) C) Add a missed decision (description) D) Remove an ADR (number and reason) ══════════════════════════════════════ Recommendation: A — review the rendered set and confirm; corrections are quick on Round 2 ══════════════════════════════════════ ``` Loop: - **A** → flip every ADR's `Status` from `Proposed` to `Accepted`, set `Date` to today's date, save, exit step. - **B** → apply edits, re-present the modified ADRs, loop. - **C** → run Phase 4.5a–4.5e for the missed decision only, append to the set, re-present, loop. - **D** → confirm with the user that the candidate fails the three "what is an ADR" criteria, remove the file, update the index, loop. Do NOT mark `Accepted` without an explicit user A. ## Self-verification - [ ] Every kept candidate from Phase 4.5a has a corresponding file under `adr/` - [ ] Every ADR has all required sections (none empty except `Supersedes` / `Superseded by`) - [ ] `Decision` sections are one-sentence-then-detail, not "we'll figure it out" - [ ] `Alternatives Considered` lists at least one rejected alternative per ADR - [ ] `Consequences` lists both positive AND negative consequences (an ADR with no negatives is suspect) - [ ] `Evidence` points at real `_docs/` sections that exist on disk - [ ] `adr/README.md` index lists every file in the directory and matches their `Status` / `Date` - [ ] `architecture.md` has a trailing `See ADR …` reference at every section that an ADR reflects - [ ] The user confirmed the set via Choose A; every ADR is `Accepted` with today's date ## Common mistakes - **Re-opening architecture**: Step 4.5 records, it does not decide. If a candidate decision turns out to be unsettled, that's a Step 2 / Step 4 gap — return there, do not paper over it with a wishy-washy ADR. - **Decision-of-the-week**: do not write an ADR for every minor pattern choice. The bar is "non-obvious to a future reader". 5–15 ADRs is typical for a planning round; 40+ is over-capture. - **Negative consequences left empty**: every real decision has costs. If you cannot name one, the decision was not actually weighed. - **Vague evidence**: `architecture.md` is not enough — point at the specific section. `architecture.md § Layering` ≠ `architecture.md`. - **Numbering reuse**: never recycle a number from a deleted ADR. The audit trail is more important than tidy numbering. - **Superseding without recording**: when a later cycle overturns an ADR, the new ADR must point at the old one via `Supersedes`, AND the old ADR's `Superseded by` field must be updated. Index reflects both. (This is enforced when `decompose` or `refactor` later updates ADRs.) ## Escalation | Situation | Action | |-----------|--------| | Candidate decision is unsettled (the team has not actually decided) | Return to the originating step (2 / 3 / 4); do NOT write a placeholder ADR | | Two candidates in Phase 4.5a turn out to be the same decision phrased differently | Merge into one ADR, list both phrasings in `Context` | | User picks D (remove an ADR) and the AI judges the decision is genuinely worth recording | Surface the disagreement, ASK why the user wants it removed, defer to user | | Existing `adr/` directory has files but `adr/README.md` is missing or stale | Rebuild the index from the directory before adding new ADRs |