record → replay · miss → why · 5-minute demo script
Status: Early alpha — what works and what doesn't
The problem
Coding agents (Claude Code, Codex, etc.) are nondeterministic black boxes. When they break, you can't reproduce it. When they work, you can't prove it'll work again.
┌─────────────────────────┐
The agent loop: │ Prompt → LLM → Tools │──── costs $$$
│ ↓ ↓ │──── nondeterministic
│ Prompt → LLM → Tools │──── can't replay
│ ↓ ↓ │──── CI needs API keys
│ Prompt → LLM → Done │──── flaky tests
└─────────────────────────┘
LLMReplay fixes this by recording the LLM traffic once, then replaying it from disk:
flowchart LR
A["Agent"] -->|"record"| P["LLMReplay\nProxy"]
P -->|"forward"| L["Real LLM"]
P -->|"save"| C["Cassette\n(scrubbed)"]
A2["Agent"] -->|"replay"| P2["LLMReplay\nProxy"]
P2 -->|"SHA-256\nmatch"| C2["Cassette"]
P2 -.->|"no network\nneeded"| X["Offline"]
Get started in 30 seconds
One terminal. No CCR. No free keys. No second window.
pip install coding-agent-vcr
llmreplay demo
demo starts a stub gateway + the proxy, records one turn, replays it offline, and prints the commands for a real agent. HMAC is set for you if missing.
Note: CLI / import =
llmreplay. PyPI name =coding-agent-vcr.
Real agent (still one terminal)
llmreplay run is the gateway — it starts the proxy, runs your agent, then tears down.
# keep ANTHROPIC_API_KEY in your env (forwarded upstream)
# local HMAC defaults to dev-local-hmac if unset
# Record (proxy = gateway — starts, runs child, tears down)
llmreplay run --mode record --cassette .llmreplay/demo \
--upstream https://api.anthropic.com \
-- claude --print "say hi"
# Replay offline
llmreplay run --mode replay --cassette .llmreplay/demo \
-- claude --print "say hi"
# CI check
llmreplay replay --check --cassette .llmreplay/demo
Miss? → llmreplay why --cassette .llmreplay/demo --request .llmreplay/demo/requests/<tx-id>.json
Full walkthrough: docs/quickstart.md.
How it works
Every field in an LLM request/response is classified into one of four categories:
┌─────────────────────────────────────────────────────────────────┐
│ Field Classification │
│ │
│ ┌──────────┐ Must match. Drives agent behavior. │
│ │ STATIC │ model, messages, tools, tool_choice │
│ └──────────┘ │
│ ┌──────────┐ Noise. Stripped before hashing. │
│ │ IGNORE │ timestamp, request_id, trace_id │
│ └──────────┘ │
│ ┌──────────┐ Secrets → HMAC placeholders before disk. │
│ │ SCRUB │ API keys, tokens, passwords │
│ └──────────┘ │
│ ┌──────────┐ Always hit the real endpoint for this step. │
│ │ LIVE │ mark-live Bash, mark-live __llm__ │
│ └──────────┘ │
└─────────────────────────────────────────────────────────────────┘
The match pipeline:
flowchart LR
R["Raw\nRequest"] --> C["Canonicalize\n(JCS)"]
C --> I["Strip\nIgnored"]
I --> S["Scrub\nSecrets"]
S --> T["Sort\nTool Blocks"]
T --> H["SHA-256\nHash"]
H --> K["Match\nKey"]
Normative rules: SPEC.md | Architecture: DESIGN.md
Why LLMReplay
| Without LLMReplay | With LLMReplay | |
|---|---|---|
| Flaky tool order | Re-run and hope | Sorted canonically, deterministic match |
| Prompt regressions | Unnoticed until prod | Golden cassettes catch diffs in CI |
| CI needs API keys | Expensive, slow, brittle | Fully offline replay from fixtures |
| Can't reproduce bugs | "Works on my machine" | Fork cassette at turn N, tweak, replay |
| Test isolation | Mock everything by hand | Record real traffic, replay hermetically |
Observability shows what happened.
LLMReplay decides what must match and re-executes the trajectory.
Integrations
| Platform | Quick start |
|---|---|
| Claude Code |
llmreplay run --mode record -- claude --print "hi"
|
| Codex |
llmreplay run --mode record -- codex --prompt "hi"
|
| pytest |
@pytest.mark.llmreplay(cassette=".llmreplay/cassette")
async def test_agent(llmreplay_cassette):
resp = await llmreplay_cassette.post("/v1/messages", json={...})
|
| GitHub Actions | |
| Any agent |
Set |
Use as a library
from llmreplay import ReplayTransport
import httpx
from pathlib import Path
transport = ReplayTransport(cassette_dir=Path(".llmreplay/cassette"))
async with httpx.AsyncClient(transport=transport, base_url="http://llmreplay") as client:
resp = await client.post("/v1/messages", json={...})
assert resp.status_code == 200
Full API: docs/reference/library.md
Hermetic smoke test
git clone https://github.com/dmallya93/llmreplay.git && cd llmreplay
pip install -e ".[dev]"
export LLMREPLAY_HMAC_KEY=dev-local-hmac
./scripts/smoke.sh
# ✓ smoke ok: record→replay (fake upstream)
No Ollama, no paid APIs, no network — pure in-process replay.
Learn
| Tutorials | Start here — first cassette → miss → CI → pytest → fork |
| Demo (5 min) | Scripted walkthrough for talks / lunch-and-learns |
| Case studies | When it pays off — CI cost, pytest mocks, turn-7 bugs |
| Share / launch | Paste-ready Show HN + Awesome PR copy |
Documentation
| Getting started | Quickstart · Tutorials · Demo · Case studies · Alpha limitations |
| Reference | CLI · Library API · SPEC |
| Integrations | Claude Code · Codex · pytest |
| Operations | CI · Portable cassettes · Troubleshooting |
| Security | Threat model · SECURITY.md |
| Optional | Free test-stack (CCR+Ollama, $0 local LLM) |
| Contributing | CONTRIBUTING.md · Publishing |
Apache-2.0 · LICENSE
Made for developers who are tired of "it worked when I ran it"
Release files for coding-agent-vcr 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| coding_agent_vcr-0.2.1.tar.gz | 89.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| coding_agent_vcr-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 171.8 kB
Release files / coding_agent_vcr-0.2.1.tar.gz
| Download URL | coding_agent_vcr-0.2.1.tar.gz |
|---|---|
| Size | 89.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9c983e7c0e6b8acffbb6e856d50cd3cf7d4636769fdf12ec4241ec32326948a0
|
|
BLAKE2b-256 checksum How to use checksums |
6b84dc0d0f5040f2b51418f7f45093747ac509ad87520da48429354940daae9c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.
Transparency logRelease files / coding_agent_vcr-0.2.1-py3-none-any.whl
| Download URL | coding_agent_vcr-0.2.1-py3-none-any.whl |
|---|---|
| Size | 82.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
406509455133559cdd2ac1f1246de37c32f7cf5e0aa46d3fd3a1b8bc6e50e3a9
|
|
BLAKE2b-256 checksum How to use checksums |
99997ec521770ae1e206f2f6384da1e441fed207dbe8a84d5b7dffce88a5c432
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.
Transparency log