Capabilities
Accountability
A run you can reconstruct: what it was allowed to do, what it cost, what it changed, and how to get back.
The session is a file
Every session is a .jsonl file — a header line, then one JSON line per entry, appended in order. Sessions are bucketed per working directory under ~/.arcturn/sessions/<hash>/, so resuming only ever offers you sessions from the same project root.
The structure is a tree, not a flat log: every entry carries a parentId, so appending after an older entry starts a new branch instead of overwriting what came after. Resuming from three turns ago and trying a different approach is an ordinary operation, and both branches stay walkable afterwards.
Checkpoints and /rewind
Before a write or edit touches a file for the first time in a turn, Arcturn records that file’s content — or its absence, if it did not exist yet. Snapshots are content-addressed blobs with an append-only manifest, and a snapshot failure is written to the manifest as an error record rather than blocking the call that triggered it.
/rewind picks a turn, restores the files that changed after it, and forks the conversation rather than deleting it — every branch you rewound past stays reachable by resuming its own leaf.
The limit, exactly: it covers write and edit. A shell command that mutates the tree is not checkpointed, so sed -i and rm are invisible to /rewind. The conversation side is genuinely non-destructive; the file side is a real disk mutation.
~/.arcturn/checkpoints/<sessionId>/
├── manifest.jsonl append-only log of turn / file / error records
└── blobs/<sha256> content-addressed file snapshotsA /rewind in an arcturn session: two files are restored to their pre-turn content and the conversation forks at turn 3, leaving both branches walkable.
Replay
arcturn replay <session> pulls the original prompts back out and re-runs them, one at a time, in order — against the same model or, with -m, another one. Progress goes to stderr and results are one JSON object per turn on stdout, carrying the prompt, final text, tool-call order and cost, so a replay pipes straight into a diff without cleanup.
Replay is live, which makes it the tool for cross-model comparison and regression testing against a real provider — not for reproducing a run byte for byte. A turn that errors is recorded and the next prompt still runs.
$ arcturn replay 019c4a2f --model openai/gpt-5.1
arcturn: replaying 6 prompts on GPT-5.1
arcturn: [1/6] rate-limit the login route
{"prompt":"rate-limit the login route","finalText":"Added a 5-req…","toolCalls":["read","edit"],"costUsd":0.0412}
arcturn: [2/6] add a test for the limiter
{"prompt":"add a test for the limiter","finalText":"Added auth.test…","toolCalls":["read","write"],"costUsd":0.0388}
arcturn: replay total $0.2317Bisect
arcturn bisect binary-searches the same prompts for the turn where behaviour left a recorded cassette. Each probe replays against a freshly loaded copy of the cassette, so the run is hermetic: no provider, no network, and the underlying tool’s execute is never invoked at all.
A cassette is a JSONL recording of everything Arcturn does not control — LLM stream events and tool results, keyed by a content hash rather than by position. Cassettes are recorded through the SDK today; there is no CLI flag for it yet.
arcturn bisect <session> --cassette <file> [--model <id>] [--cwd <dir>]Blame and provenance
arcturn blame <file> answers, per line, which turn wrote it and what evidence that turn was working from: files read, pages fetched, commands run — with anything that arrived from a fetch or an MCP server marked untrusted. Not who wrote a line, but which reasoning step did.
It is opt-in, because the recording costs disk: set "provenance": true in config to start capturing it.
Audit trail and cost
arcturn audit answers a different question than blame — not why a line is here, but what happened in this session and what approved it. Every completed tool call, every interactive permission decision and every hook verdict is recorded as the run happens, not reconstructed afterwards. It is off by default; enable it with "audit": true.
Cost accounting has no opt-in — it is always on, updated after every turn. /cost prints current spend; --max-cost and /cost limit set a ceiling that aborts the run the moment cumulative spend reaches it, at the next turn boundary. Sub-agent spend counts against the same ceiling, which is otherwise trivially easy to treat as free.
14:03:12 tool bash git status ✓
14:03:20 perm write src/auth.ts ask-allow
14:03:21 tool write src/auth.ts ✓
14:03:44 perm bash rm -rf dist ask-deny
14:04:02 hook preToolUse deny: no writes under infra/
14:04:19 tool edit src/auth.test.ts ✗
4 tool calls, 1 denied, 1 hook vetoEvery turn counts.
Start a session, watch every tool call ask first, then go back and read exactly what happened.