---
schema_id: decision
schema_version: 1
applies_to: aiwiki/decisions/**/*.md
filename_pattern: "{nnnn}-{slug}.md"
hard_cap_lines: 400
soft_target_lines: [100, 200]
required_frontmatter:
  schema_id: { type: string, equals: decision }
  schema_version: { type: integer }
  status: { type: enum, values: [proposed, accepted, superseded, deprecated] }
  date: { type: date }
  supersedes: { type: string, optional: true }
  superseded_by: { type: string, optional: true }
required_sections:
  - "## Context"
  - "## Decision"
  - "## Consequences"
  - "## Review"
optional_sections:
  - "## Alternatives"
section_order: strict
citation_rule: required-where-claims-about-code
---

# Schema: decision (ADR)

## Purpose

An ADR records a decision that is hard to reverse — architectural choice, public-surface naming, security/data tradeoff, schema design, cross-slice contract. The page answers "why was this chosen?" so future sessions don't relitigate it.

## When to write one

Write an ADR for any decision that's hard to reverse. Specifically:

- Architectural choice (system shape, data flow, technology selection)
- Public-surface naming (APIs, schemas, file paths users will import)
- Security or data-handling tradeoff
- Schema design (DB, API, file format) — anything other code will depend on
- Cross-module contract — what one module exposes that other modules depend on
- Any other irreversible design choice that survives prototype iteration into production

Do NOT write an ADR for: variable naming, inline-vs-extract refactors, choosing between known-good libraries with no real tradeoff. Agent judgment is sufficient there.

## File location and naming

- Path: `aiwiki/decisions/{nnnn}-{slug}.md`
- Numbering: zero-padded sequential, project-wide (e.g. `0042-token-storage.md`)
- Slug: kebab-case, ≤6 words, descriptive

## Required frontmatter

| Field | Type | Notes |
|---|---|---|
| `schema_id` | string | Must equal `decision` |
| `schema_version` | integer | Bumped only when schema itself changes |
| `status` | enum | `proposed` / `accepted` / `superseded` / `deprecated` |
| `date` | ISO date | When the decision was accepted (not when drafted) |
| `supersedes` | string (optional) | Path to ADR this replaces |
| `superseded_by` | string (optional) | Set when this ADR is itself replaced |

## Required sections

| Section | Purpose | Citation requirement |
|---|---|---|
| `## Context` | What's the situation that demands a decision | Cite code if context is grounded in specific code |
| `## Decision` | What we chose (one paragraph, imperative mood) | Cite the prototype file or convention this comes from |
| `## Alternatives` (optional) | What we rejected and why | Omit if the alternatives are obvious |
| `## Consequences` | What changes downstream | Cite affected files where known |
| `## Review` | `review:` block — reviewers, raised objections, how each was resolved, final verdict | — |

The `## Review` block is required for `accepted` status. Format:

```yaml
review:
  reviewers: [critic, codex]
  claims: |
    <what the decision claims to be true/best>
  objections:
    - reviewer: critic
      objection: "..."
      resolution: "..."
  verdict: approved | escalate | reject
  reviewed_at: 2026-05-10T11:42:00Z
```

## Line caps

- Hard cap: 400 lines (LINT fails above)
- Soft target: 100-200 lines

## Citation rules

- Code references use `file:line@<sha7>` (e.g. `src/auth.ts:42@a3f2bc1`) or `symbol` form (e.g. `src/auth.ts#login`)
- LINT auto-fills missing `@<sha7>` on first save
- Stale citations (hash mismatch) fail LINT — resolve by updating the citation, removing the claim, or annotating `// ack-stale: <reason>`

## Skeleton

```markdown
---
schema_id: decision
schema_version: 1
status: accepted
date: 2026-05-10
---

## Context

The cache layer needs durable storage. Read-heavy workload (~95% reads); single writer pattern; ≤1M entries projected.

## Decision

Use SQLite for the cache layer.

## Alternatives

- **Postgres**: rejected — overkill for single-writer cache; operational burden of a separate service.
- **In-memory only**: rejected — process restart loses cache; eviction strategy adds complexity not warranted at projected size.

## Consequences

- One additional dependency (`better-sqlite3`)
- WAL mode enabled for concurrent reader safety
- Migration path documented at [src/cache/migrations/README.md](src/cache/migrations/README.md)

## Review

```yaml
review:
  reviewers: [critic, codex]
  claims: |
    SQLite is sufficient for the cache layer at projected scale and read pattern.
  objections:
    - reviewer: critic
      objection: "Concurrent write throughput at scale"
      resolution: "Cache is read-heavy; single-writer pattern documented; WAL mode handles reader concurrency"
    - reviewer: codex
      objection: "No atomic multi-row updates"
      resolution: "Acknowledged; not needed for cache semantics (per-key invalidation only)"
  verdict: approved
  reviewed_at: 2026-05-10T11:42:00Z
```
```
