docs/adr/0068-a-playthrough-leaves-a-durable-trail.md

ADR 0068: A playthrough leaves a durable trail

Status: accepted (James, 2026-07-26). Implemented across the Clankie play host, free-play session composer, environment runtime, and GBA MCP server.

Current status (2026-08-19)

Play journals, bounded environment records, and lifecycle events remain current. The possession-log/body-lock artifact described below is retired by ADR 0129. Existing files remain inert historical evidence; current code neither migrates, deletes, reads, nor appends them. Each GBA MCP process has a separate private runtime and no shared-body ownership trail.

Context

An operator needs to reconstruct what Clankie presses, why he chooses it, whether he is making progress, what he says, and which independently owned body produced the evidence. The bounded environment record alone cannot hold that full trail. FreePlayTurn contains monologue, intent, objective, notes, reply, speech intent, outcome, and effect; durable journals and lifecycle records assign each concern an owner.

Decision

Observability and operational state are split into two artifacts with different rules:

  • The play journal is the observability artifact. openFreePlayJournal (gba-emulator) writes one append-only JSONL file per run under ~/.local/state/clankie/gba-play/: a header (run identity, stable journey identity, environment, venue, environment session, scenario, resume lineage), one line per validated FreePlayTurn as it settles, and a summary line carrying the outcome plus the end-of-run metrics. Journal V2 turn lines carry the bounded causal packet from ADR 0117: decision, immediate pre-action, and post-action semantic observations plus the complete structured result and available progress/provenance signals. The production play execution always journals; a failed append is reported and costs the record, never the playthrough. The journal is never rewritten or pruned by code. V1/V2 remain readable; V3 headers add cross-run journey identity without changing V2 turn evidence. When a room report is attempted, speechDeliveryId and the bounded narration event join the turn to content-free voice receipts. The id alone proves no delivery, and generated audible wording remains unknown by policy. Selected V2 turns also reference a native-resolution PNG stored under .screenshots/<journal-stem>/: the first turn, each 25-turn interval, map/scene/objective transitions, noteworthy speech-intent turns, and failures. At most 63 turn frames are captured so the terminal summary can add the 64th. Each reference records dimensions, byte size, SHA-256, and capture reasons; PNG bytes never enter the JSONL.
  • The environment session record is operational state, bounded and per-run. createFreePlaySession embeds a start-stamped run id in the session id, so runs stop overwriting each other, and opts into EnvironmentRuntimeRetention: the newest 128 action records are kept (only terminal results roll, counted on the record — the no-silent-caps rule from ADR 0061), and the newest 16 ended session records survive on disk. Frozen scenario drivers configure no retention and keep everything.
  • The retired shared-body implementation recorded possession transitions. Older MCP servers appended lease events to possession-events.jsonl beside body.lock. ADR 0129 replaces that model with process-owned runtimes; current play and GBA MCP paths neither read nor append those files.
  • The play host logs every lifecycle transition (claimed, running, settled with its receipt, refused, failed) through the service's structured logger, so the service log narrates the same story as lifecycle events.
  • The free-play CLI's default trace path is per-run (artifacts/gba-free-play/trace-<stamp>.jsonl) instead of one fixed file truncated at startup.

Consequences

  • A finished run is reconstructable end to end from files: why each action is chosen and what it did (journal), how the run is going (journal summary and receipt), which body generation produced each causal stage (journal provenance), and how the lifecycle unfolds (service log and lifecycle events).
  • A run with no summary still has truthful terminal accounting when its run id joins a durable terminal embodiment event. That lifecycle outcome does not become a synthetic summary.
  • Idempotency for environment actions is bounded by the retention window: a retry of an action older than the newest 128 dispatches anew. Retries are immediate in practice; the bound is the price of a bounded record.
  • Observer MCP servers create a session record per launch instead of sharing one; ended-record pruning is what keeps that finite.
  • The journal directory grows one JSONL file and up to 64 native-resolution screenshots per run and is deliberately not pruned by code — deleting play history is an owner's call, never an agent's.
  • This ADR remains the stable record for Clankie journal ownership and retention. The journal outlived the local body it was written for: since ADR 0145 every new run records a hosted seat, while older local runs on disk keep parsing unchanged.