# ADR-0004 — Beat persistence canonical home

**Status:** Accepted
**Date:** 2026-05-05
**Deciders:** JT, Claude (in dialogue)
**Supersedes:** none

> *This ADR was drafted by AI (Claude Opus 4.7) during the CP-10 build.*

## Context

CP-7 shipped Take/Beat/Scene as in-memory dataclasses with `to_dict` /
`from_dict` round-trip but no disk layer. CP-10 needs durable Scene state
for the overnight loop and Workspace dailies surface.

The cross-engine consult (Gemini 3.1 Pro + Opus 4.6 R3) split on module
location: Gemini argued `recoil/core/persistence.py` (cross-engine
neutrality); Opus argued `recoil/pipeline/core/persistence.py` (the
serialized models live in pipeline; serialization is a pipeline concern).

## Decision

Beat persistence lives at `recoil/pipeline/core/persistence.py`.

The Scene/Beat/Take dataclasses are pipeline-specific (`pipeline/core/take.py`).
The narrative engine has no use for them. Putting persistence in
`recoil/core/` would force the narrative engine to import pipeline
internals — a layering violation.

## Consequences

**Pipeline owns its state.** Scene JSONs at
`projects/{project}/state/orchestration/scenes/{episode}_{seq_id}.json`.

**Atomicity protocol locked.** tmp + flush + fsync + os.replace. POSIX-atomic.

**Schema versioning.** Top-level `schema_version: 1` field on every saved
file. Future versions must increment.
