# Decision migration map (legacy heading → active record / archive)

The full corpus (`docs/decisions.md` and the `docs/decisions/*.md` topic files) was **fully migrated
to the lifecycle schema on 2026-07-21 (#520/#521)**: `PYTHONPATH=. python3 scripts/decisions_validate.py`
reports 0 `legacy-unmigrated` headings, and the catalog (`docs/decisions/README.md`, built by
`scripts/build_decisions_catalog.py`) lists 108 active records. Every active decision heading now
carries a metadata block (`key:`/`status:`/`since:`/`supersedes:`/`superseded-by:`). Most headings were
migrated **in place** — the heading text is unchanged, only the metadata block was added — so their key
is discoverable via the catalog or `rg '^\`key:\`' docs/decisions/`, not via this file. This table
records only the **non-trivial** mappings produced during the migration: splits, supersessions, and
prose-only reversal notes. It is not, and was never meant to be, a 1:1 index of all 108 records.

| Legacy heading | Key | Status | Location |
| --- | --- | --- | --- |
| 2026-07-17 — No persistent compiler servers in CI; every `services:` container gets an explicit cap; #390's small-lane move reversed (#406) | `ci.runner-placement` | active | `docs/decisions.md` |
| 2026-07-19 — CI `test` job reports a sampled true peak-anon, not cache-inflated `memory.peak` (#412) | `ci.peak-anon-measurement` | active | `docs/decisions.md` |
| 2026-07-12 — decisions.md is append-only, enforced; root-screenshot guard (#303 H9/H3) — heading kept identical to the pre-split original (only the body/metadata were split) so the lifecycle validator's relocation check matches it | `docs.append-only-guard` | superseded (`superseded-by: docs.decision-lifecycle@2026-07-21`) | `docs/decisions/archive/release-ci-governance.md` |
| 2026-07-12 — Root-screenshot guard: pre-commit refuses root-level `*.png` (#303 H3) — split from the original H9/H3 bundle | `ci.root-screenshot-guard` | active | `docs/decisions/release-ci-governance.md` |
| 2026-07-21 — Decision records carry a lifecycle schema, validated by a script; append-only-by-diff is retired (#521) | `docs.decision-lifecycle` | active (`supersedes: docs.append-only-guard@2026-07-12`) | `docs/decisions.md` |
| 2026-07-11 — Queue state lives in the pinned Gitea tracker (#237), not in the handoff file | `docs.queue-state-gitea-tracker` | superseded (`superseded-by: startup.parallel-orientation@2026-07-21`) | `docs/decisions/archive/startup.md` |
| 2026-07-21 — Parallel orientation + selection is the startup protocol; #237 retired (#520) | `startup.parallel-orientation` | active (`supersedes: docs.queue-state-gitea-tracker@2026-07-11`) | `docs/decisions.md` |

## Migration conventions

A legacy `##` entry that bundles an active sub-decision with a superseded/retired one is SPLIT during
migration — the superseded half moves to `archive/`, the active half becomes its own `status: active`
record in the active files. Never relocate an active decision to `archive/`. First applied to the
#303 H9/H3 bundle (below): H9 (docs.decisions.md append-only) is genuinely superseded and archived as
`docs.append-only-guard`; H3 (the root-screenshot pre-commit guard) is independent and still active, so
it was split out into its own active record, `ci.root-screenshot-guard`, in
`docs/decisions/release-ci-governance.md` rather than being carried into archive with its superseded
sibling.

## Notes on records considered but NOT given a standalone entry

- **#390 (small-lane move for `api-docs`/`format`).** Investigated whether #390's reversal (described
  in #406's prose, point 3: "#390's `small`-lane move ... reversed") ever existed as its own `##`
  record. It did not — grep for `#390` across `docs/decisions.md` and `docs/decisions/*.md` finds it
  only inside #406's own body text (and one other unrelated mention of #390's apt-ffmpeg estimate,
  also inside #406). #390 was **prose-only**: no separate heading, so nothing to mark `superseded` or
  move to archive. `ci.runner-placement` (#406) therefore carries `supersedes: none`; the reversal is
  recorded only in its `**Signals:**` line (`#390 (prose-reversed, no standalone record)`) and in this
  note.
- **#411 (the earlier `memory.peak`-headline measurement).** Investigated the "#411 obsolescence" the
  brief references. #411 is mentioned twice in `docs/decisions.md`, both inside #412's own body
  ("the `test` job's memory instrument (added in #411) now reports ..." and "the older #411 probe
  ... is superseded"). Like #390, #411 never had its own `##` heading — its "decision" was
  narrated retroactively inside #406 (the "Measurement is now continuous" paragraph, which describes
  the instrument #411 actually shipped) and explicitly marked obsolete inside #412's own prose. Since
  there is no standalone #411 record, there is nothing to move to `archive/`; `ci.peak-anon-measurement`
  (#412) — the record that supersedes #411's approach — carries `supersedes: none` and documents the
  prose reversal in its `**Signals:**` line. The governed surface (CI memory measurement) still exists
  and is very much active, so this is a straightforward **supersession of an unnamed predecessor**, not
  a retirement — there's no case to make for `retired` here since the surface it measures is live.

## Still open

- **DONE**: `docs.queue-state-gitea-tracker` (#237) — superseded by `startup.parallel-orientation`
  (2026-07-21, #520), archived at `docs/decisions/archive/startup.md`. See the table above.
- Nothing else is open from this migration — the corpus is fully migrated (0 legacy-unmigrated). Future
  new decisions are simply added directly in the lifecycle schema; there is no further migration pass
  to track here.
