Skip to main content

amberfork_replay/
lib.rs

1//! The replay path: re-drive a recorded agent run from its cassette.
2//!
3//! Where `amberfork-record` writes a cassette by *capturing* what crossed the provider
4//! boundary, this crate *serves* it back — the VCR to record's tape deck. It is the substrate
5//! counterfactual attribution stands on (epic #35): to ask "what if step N had gone the good
6//! run's way", you re-run the agent against the recording, patch one response, and watch what
7//! it does next. That re-run needs something that answers the agent's requests from the tape
8//! and only reaches a live provider once the run has branched off it.
9//!
10//! This crate is an I/O edge, alongside `amberfork-record` and `amberfork-server`: `tokio`/`axum`
11//! live here, never in the engine crates. The matching core in [`Replay`] is deliberately sync and
12//! pure; the async surface is confined to the [`Upstream`] seam and the [`ReplayServer`] listener
13//! the re-driven agent talks to.
14//!
15//! ## Matching is content-addressed, not positional
16//!
17//! A re-issued request is matched to a recorded exchange on `(method, path, body)` — the same
18//! key `amberfork-record`'s cassette contract names as load-bearing — and never on call order.
19//! In an agent loop the request body carries the whole accumulated message history, so each
20//! turn's body is distinct and content-addressing distinguishes turns for free; it is also what
21//! lets a patched re-run still match the turns it did *not* change while missing on the one it
22//! did.
23//!
24//! ## Fidelity limit (stated, not solved)
25//!
26//! Clock and seed virtualization are not attempted: a re-run that reads wall-clock time or a
27//! fresh RNG seed can diverge for reasons unrelated to a patch. Making replay bit-exact against
28//! a nondeterministic provider is physically impossible; the counterfactual oracle (epic #35)
29//! tolerates this by consensus over multiple runs, degrading to `Unverified` rather than
30//! asserting a false result. Keeping that limit here, in the crate that could pretend otherwise,
31//! is the honest place for it.
32
33mod canon;
34mod proxy;
35mod replay;
36mod server;
37mod upstream;
38
39pub use proxy::ReplayProxy;
40pub use replay::Replay;
41pub use server::{ReplayError, ReplayServer};
42pub use upstream::{LiveUpstream, ScriptedUpstream, Upstream, UpstreamError};