Glossary · agent replay

Run receipt

▸ Definition

A run receipt is a per-run audit trail for an AI agent workflow: a machine-checkable record, written at the end of each run by the harness that executed the calls, of what actually ran — every step with its outcome against an explicit assertion, its timing, and the run's measured LLM token count. A conventional audit log records that activity happened; a run receipt also carries a verdict — each step passed, failed, ran unchecked, or was skipped — so the record can be compared against the previous run and gated on without a human reading it. In Reelier, the receipt is one JSON line appended to .reelier/runs/<skill>.jsonl after every replay.

An audit trail agents can't write for themselves

Audit trail is a settled term. NIST defines it as a chronological record that reconstructs the sequence of activities surrounding an operation, and operations teams have kept them for decades: who did what, when, with what result. Nothing about the concept is new, and Reelier did not invent it.

Agents change one thing: the natural author of the record is now a model, and a model's account of its own work is more model output, subject to the same failure modes as the work itself. An agent that says "done" has made a claim. The trail is still missing. The useful version of an agent audit trail is written by the harness that executed the tool calls, from observed results — never by the model, from memory.

A run receipt is that trail, scoped to one run and given a verdict. It answers the auditor's questions — what executed, in what order, with what outcome, at what cost — in a format a scheduler can act on: pass or fail, per step, measured.

What's on a receipt — and what isn't

In Reelier, every reelier run appends one JSON line to .reelier/runs/<skill>.jsonl (a --dry-run executes nothing and writes no receipt). Per step it records the number, title, outcome (passed, failed, unchecked, or skipped), milliseconds, and the verbatim failure list; per run it records totals — step counts by outcome, total milliseconds, and two LLM token counters. When a step drifts or heals, a why field carries the trigger or the change, taken from the real assertion or tool result, never composed after the fact.

Two rules keep the format honest. A step that ran with zero assertions is recorded unchecked, never passed: success means a check held, not that code executed. And the token counters are measured. At the default --max-level 0 the runner never constructs an LLM client, so llmInputTokens: 0 reports a code path that cannot spend; when opt-in BYOK escalation (Level 1 or 2) does run, its token usage lands on the step that used it and in the totals.

One thing stays off the receipt: data values. Bound values flow between steps during the run, but the persisted record keeps structure and outcomes only. reelier diff inherits that choice — the same steps returning fresh data reads SAME; a changed outcome, or the same outcome reached at a different escalation level than last run, reads DRIFTED.

Write steps: the refusal is on the record

Replay is read-only by default. A recorded write step (effect: idempotent-write) does not re-fire unless you pass --allow-writes; without the flag, the step is recorded failed with a named refusal in its failure list — Refusing to execute a write step (effect: idempotent-write) — replay is read-only by default — and every step after it is recorded skipped. The refusal is itself an audit event: the receipt shows the gate holding.

This gate carries most of the weight in practice. In the measured corpus behind Reelier's launch writing, only about 1.1% of scanned sessions were read-only end-to-end. Almost every real agent job writes somewhere, so what an audit trail records about write steps decides whether you can trust it.

From one receipt to a gate

Because every run appends to the same file, the receipt file is also the run history. reelier diff <skill> compares the last two receipts, prints a per-step verdict, and exits 0 on SAME, 1 on DRIFTED. That exit code is the whole CI story: put the replay and the diff in a scheduled job, and a drifted step turns the build red with the failing step named.

Two boundaries, stated before you ask. The receipt attests what the replayer executed and observed; it is plain JSON on disk, nothing signs it, and anyone with the file can edit it — treat it like any other artifact you commit and review, and let diff anchor to a baseline you have read. And receipts stay on your machine by default: reelier push syncs them to a dashboard only when both REELIER_CLOUD_URL and REELIER_CLOUD_KEY are set. Opt-in, like everything that leaves the machine.

A real receipt

The receipt below was produced while writing this page, not staged for it: the vendor-status-sweep portfolio skill — two read-only HTTP GETs against npm's and GitHub's public status APIs — replayed with the published CLI, then the receipt file read back with cat. The JSON line is the audit trail itself: one line per run, appended.

Reelier · replay receipt
npx -y reelier@latest run vendor-status-sweep.skill.md

✓ Step 1 — npm status [passed] 226ms
✓ Step 2 — GitHub status [passed] 159ms

PASSED: 2/2 steps ok, 0 failed, 385ms total

cat .reelier/runs/vendor-status-sweep.jsonl

{"skill":"vendor-status-sweep","startedAt":"2026-07-21T21:53:31.788Z","finishedAt":"2026-07-21T21:53:32.175Z","passed":true,"steps":[{"n":1,"title":"npm status","level":0,"outcome":"passed","ms":226,"failures":[]},{"n":2,"title":"GitHub status","level":0,"outcome":"passed","ms":159,"failures":[]}],"totals":{"steps":2,"passed":2,"unchecked":0,"skipped":0,"failed":0,"ms":385,"llmInputTokens":0,"llmOutputTokens":0}}
verbatim output

Both commands run for real on 2026-07-21 with the published CLI (npx -y reelier@latest, resolved to reelier@0.13.0), in a scratch directory holding a verbatim copy of examples/portfolio/vendor-status-sweep.skill.md from seldonframe/reelier. Read-only, no account, no key, no model in the loop.

FAQ

Is a run receipt the same thing as agent observability or session replay?

No. A trace is a record you watch; a human reads it and decides whether the run was right. A run receipt carries the decision on the paper: each step's outcome against an explicit assertion, plus measured totals, so a cron job or CI gate can act on it with nobody reading. The two compose — keep traces for debugging depth, gate on receipts.

Can I trust a receipt that says 0 LLM tokens?

The zeros are measured, not assumed. At the default --max-level 0 the runner never constructs an LLM client, and the receipt's counters sum per-step escalation usage that never existed. Reelier's published benchmark asserts this from the run records — llmInputTokens === 0 && llmOutputTokens === 0 on every one of the 1,000 replays in its N=1000 test.

Reading as an agent? This page has a markdown twin: /learn/run-receipt/md