# Persistence, recovery, and replay

A saved conversation is evidence of what was recorded. It does not guarantee that an action was never executed twice or that every event a user saw reached storage.

Codex's `ThreadStore` distinguishes standard persistence from contexts allowed to enqueue background writes, with explicit flush/shutdown barriers. Pi's coding-agent emits message events before persistence; the separate experimental durable runtime makes stronger tool-intent and replay-policy guarantees. Goose's machine persists effects and reloads session state; live streaming may precede persistence, and pending approval execution depends on retaining a valid extension lease. [Codex storage contract][c-store], [pi events][p-events] and [durable recovery][p-recovery], [Goose effect application][g-store] and [tool execution][g-execution].

**Design implication:** model recovery around uncertainty. If a tool charged a card and the process crashed before recording success, blindly rerunning it is wrong. Use an externally enforced idempotency key, reconcile status, or surface an interrupted result. Avoid claiming exactly-once effects from the existence of a database or append-only log.

## Sidekick: recover the turn, not just the worker

Sidekick's native path separates a durable turn from the worker attempt advancing it. A heartbeat and ownership checks govern replacement attempts. A superseded attempt cannot write a valid final settlement, although this does not stop an external operation it already started. Outpost dispatch attempts, which represent separately hosted execution, are not automatically taken over and resent. [Attempt ownership][s-attempt]. Sidekick's source is private; these pinned links require repository access.

The tool boundary is equally explicit. The loop commits tool intents beside the assistant request before dispatching them. Suppose a tool changes a remote system and its worker disappears before the result enters the transcript. Recovery first inspects the intent: a stored result can be redelivered; an unclaimed call can be claimed; a running call has an uncertain outcome. After waiting for an existing result, the native recovery path reruns a running call only when both stored and current replay policy allow it. Otherwise it returns an interrupted-result explanation. Replay permission is separate from user authorization. [Intent commit][s-intent], [recovery decisions][s-recovery], [policy intersection][s-policy].

This is a stronger contract than replaying the last transcript entry, but it is not exactly-once execution. A replay-safe operation may run more than once. Post-commit hooks and completion projections also remain separate work: the sweeper logs failures rather than promising durable retries for every downstream notification. The [Sidekick chapter](../../snapshots/sidekick/463d653124e1/chapter.md#tool-recovery-records-what-is-known) traces these failure windows. [Post-commit limits][s-finish].

[c-store]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/thread-store/src/store.rs#L100-L250
[p-events]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/core/agent-session.ts#L1104-L1200
[p-recovery]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/durable/src/harness/tool.ts#L122-L210
[g-store]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/agents/state_machine/session.rs#L40-L153
[g-execution]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/agents/state_machine/ops_toolcalling.rs#L664-L1106
[s-attempt]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick/services/turn_record.py#L900-L1027
[s-intent]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick_worker/services/agent_loop.py#L675-L694
[s-recovery]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick/services/intent_recovery.py#L475-L651
[s-policy]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick/services/intent_recovery.py#L290-L299
[s-finish]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick/services/turn_sweeper.py#L627-L708
