---
key: docs.decision-one-file-per-record
title: 2026-07-25 — Decision records are one YAML-frontmatter file per record, filed by key (#610)
status: active
since: '2026-07-25'
supersedes: none
superseded-by: none
rule: Each decision record is its own file at `docs/decisions/records/<area>/<topic>.md` (archived ones at `docs/decisions/archive/<area>/<topic>.md`) with YAML frontmatter; the filename IS the key, so one-active-record-per-key is a filesystem property rather than a validator check, and supersession is a `git mv`.
signals: 'one file per record, split decisions.md, YAML frontmatter, records/<area>/<topic>.md, filename is the key, migrate_decisions_split, equivalence harness, dual-format parser, moved index, Records formerly in this file · paths: scripts/migrate_decisions_split.py scripts/decisions_lib.py scripts/tests/test_migration_equivalence.py docs/decisions/records/ · issues: #610, #603'
mechanics: 'scripts/migrate_decisions_split.py (one-shot); scripts/decisions_lib.py → has_frontmatter/_parse_frontmatter; docs/README.md → task-signal map'
sources: 'trial migrations against disposable worktree copies; the migration-time proof — 168 records before and after, zero lost, zero gained, zero field mismatches, zero rationale bodies differing'
---

**The monolith was the problem, and it was a concurrency problem before it was an aesthetic one.**
A 3,900-line append target made every parallel session collide: PR #605 and PR #614 both hit
append-vs-append conflicts in `docs/decisions.md` during routine rebases, and resolving those by
hand inside the corpus is exactly the operation the rationale-rewrite guard exists to police. One
file per record makes concurrent record-adds conflict-free by construction.

**Identity stays the key; the path is derived from it.** That is the difference between this and
adopting OKF wholesale (see `docs.decision-optional-provenance`, which rejected OKF as a format):
OKF's stable identity is the file path, which is precisely what the breadcrumb rule tells agents not
to trust. Here the filename is *generated from* the key and the validator enforces they agree, so
the two can never drift — and "exactly one active record per key" stops being a check the validator
performs and becomes a thing the filesystem cannot represent.

**The migration was made provable rather than reviewable.** A ~170-file diff cannot be meaningfully
read. So the parser was taught BOTH formats first, dispatching on a leading `---`: the body-diff
guard parses the old form at the merge-base and the new form at head, both keyed on `key`, so the
migration validates itself with no bypass and no one-time escape hatch. Correctness rests on a
field-level equivalence harness — every record survives with its key, every metadata field
round-trips, every rationale body is byte-identical — not on anyone reading the diff. Reviewers
should scrutinise the harness, because it is the actual evidence.

**Three things measuring caught that reading would not have.** (a) ~500 lines of the corpus sit
*outside* any record — `decisions.md`'s lifecycle schema and each topic file's preamble, mostly the
only copy of that narrative — so source files are kept and stripped, never deleted; they also cannot
be filed per-area, since topic files hold several areas and 4 of 23 areas span several files.
(b) Archive discovery was a non-recursive glob, which after the split found *zero* archived records
and surfaced as four bogus "supersedes points to unknown key" errors rather than an obvious failure.
(c) ~32 live docs point into the corpus *by date* (`see decisions.md 2026-07-10`), which the split
would dangle; each stripped file therefore ends with a generated "Records formerly in this file"
index, which also rescues the identical breadcrumbs in historical issue comments that cannot be
rewritten at all.

**Keys were preserved verbatim, warts included.** `sched` (12 records) and `scheduling` (1) are the
same concept under two prefixes, and seven areas hold a single record — now visible as directories.
Renaming a key is not a move: it changes a record's identity, breaks the equivalence proof, and
invalidates MemPalace's per-key drawers. Taxonomy normalisation is separate work.

**The line budget now counts prose, excluding frontmatter.** Stated plainly because it flatters the
number: this is a change of *metric*, not a consolidation. Frontmatter is the structured restatement
of what used to be one dense backtick line, so counting it would inflate the figure without any new
knowledge being added. The consolidation work is still outstanding.
