Docs / Operate
Record and replay
Changed a prompt, a skill or a model? A replay answers whether yesterday's run would still go the same way: the run's prompt goes through today's team, and every tool call is answered with what the tool said last time.
Why replay
A run depends on two things: the model and the world it reads. A replay holds the world still. Nothing reaches the network, your databases or a subprocess, and nothing in the workspace is written, so any difference you see comes from your change or from the model.
It takes two commands: ivralo record writes a stored run out as a cassette, and ivralo replay runs the run's prompt again against it.
The cassette: ivralo record
A stored run already holds every tool call it made, with its arguments and the output the model saw; that is what ivralo trace shows. ivralo record writes it out:
ivralo record last
/srv/repo/.ivralo/cassettes/1a2b3c4d-.../cassette.json
The run is named as ivralo trace names one: last (the default), or the first eight characters or more of its id, with --session for another conversation's. The cassette is JSON you can open: a header (the run, its session, who took part, when it was recorded, ivralo's version), the prompt, the reply, and one entry per tool call of the run and of every specialist it handed work to.
An output longer than 4 KB is stored once, by its SHA-256, under .ivralo/cassettes/blobs/. Fifty runs that read the same page keep one copy.
A cassette never leaves .ivralo/
A cassette holds your data. It is written only under the workspace's .ivralo/, which ivralo's file tools never open and which a repository's .git/info/exclude keeps out of your diff. Every credential ivralo holds is masked before anything is written:
- by value: the value of every environment variable named like a credential (
_KEY,_TOKEN,_SECRET,PASSWORD, ...), of every variable adatabases.jsonconnection names, and of the database URL ivralo itself uses becomes<NAME>wherever it appears: an argument, an output, the prompt or the reply; - by name: an argument whose name looks like a credential (
api_key,token) is masked whole; - in URLs: a URL's password becomes
user:***@wherever a URL appears.
Cassettes are never pruned. Delete .ivralo/cassettes/ when you no longer want them.
The replay: ivralo replay
ivralo replay last --tools mocked
The run's prompt is sent through today's team (today's roster, models, skills and prompts) in a fresh session, with long-term memory neither read nor written, so your conversation is untouched. Handing work to a specialist runs for real, so each specialist does its own work again. Every other call, such as code, a shell command, a page, a query, a file read or an edit, is answered from the cassette and never runs. When the run has no cassette yet, one is recorded first. --tools mocked is the only mode, and the default.
A call is answered only by a recorded call of the same specialist and tool whose arguments are exactly the same, code character for character. The same call made twice gets the two recorded answers in order; calls made in another order are fine.
Divergences
Anything else is a divergence. The model is told this is a replay and that the call has no recording, and the report shows the call with a diff against the nearest recorded one. It is never answered with that nearest call's output: a model doing something different is the event a replay exists to show.
replay of 1a2b3c4d: diverged - 1 call(s) without a recording, 1 recorded call(s) never made
new run 9f8e7d6c in session 4b3a2918
3 call(s) answered from the cassette, 1 delegation(s) run
divergence: Data Analyst run_python
--- recorded
+++ replayed
@@ -1,3 +1,3 @@
{
- "code": "df = pd.read_excel('sales.xlsx')\nprint(df.revenue.sum())"
+ "code": "df = pd.read_excel('sales.xlsx', sheet_name='Q3')\nprint(df.revenue.sum())"
}
never made: Chart Designer run_python {"code":"save_chart(df)"}
A recorded call the replay never made is a divergence too. The new run is stored like any other and tagged with the run it replays, so ivralo trace and ivralo why work on it.
Exit codes
| Exit | Meaning |
|---|---|
0 | The replay held to the recording |
1 | It diverged: a call without a recording, or a recorded call never made |
2 | The run or its cassette could not be read, or the replay could not run |
--json prints the report as one JSON object. The report goes to stdout and an error to stderr, so the commands fit in a script that checks a prompt or skill change against a set of stored runs.
What a replay does not claim
- The model is not made deterministic. A replay can diverge because the model sampled differently, local or hosted, and the report says so in the same words.
- MCP servers are not started. A recorded MCP call shows as never made.
- A plan step replays as a plain request with the step's prompt.