Skip to main content

receipt

Run a command. Get a receipt for what it actually touched — not just what it was asked to do.

An AI agent (or a script, or a CI job) says it's going to fix a bug in one file. Nothing checks whether that's what it actually did until someone reviews the diff by hand, if they do at all. receipt snapshots the working directory before and after, and reports pass, fail, or unverified — same three-status shape as invariant, firedrill, and carabiner.

$ receipt run --task "fix the auth bug" --declare app/auth.py \
    -- python fix_auth.py
[FAIL] touched 1 undeclared file(s): app/payments.py
receipt written to receipts/20260908T121251Z-4f2c9a1b.json

Three statuses, one of them meaning something different here

pass — touched only what was declared. fail — touched something outside the declared scope, named exactly. unverified — no scope was declared for this run at all.

That third one is a deliberate difference from invariant/firedrill/ carabiner, where unverified means "a check that should have run, didn't." Here it means "no promise was made this time" — plain audit logging is a normal, legitimate use of this tool, not a degraded one. So unverified does not fail the build; only a broken declared promise (fail) does.

What it actually does

  1. Hashes and permission-bits every file under the watched directory (sha256
    • mode, skipping .git, __pycache__, etc.).
  2. Runs the given command, captures stdout/stderr/exit code/timing, and redacts secret-shaped text (env-var-style API_KEY=... assignments, credentialed URLs, well-known token prefixes, PEM key blocks) before any of it is stored — see "What redaction doesn't mean" below. A command that never launches at all (bad --dir, missing binary) still produces a receipt — fail, with the launch error as the detail — instead of a Python traceback and no evidence.
  3. Snapshots the directory again, diffs the two. A removed path and an added path with identical content are reported as one renamed pair, not an unrelated delete-plus-create; a path whose content is byte-identical but whose permission bits changed is reported as mode_changed — see "What touched means" below.
  4. If a scope was declared (exact paths, or glob patterns like app/*.py), checks the diff against it.
  5. Writes the whole thing — command, task, diff, declared scope, verdict — to receipts/<timestamp>-<random>.json alongside a sha256 of the receipt itself, same evidence-bundle idiom as invariant's --evidence.

Zero dependencies — stdlib only (hashlib, subprocess, argparse, fnmatch).

Install

pip install receipt-evidence        # the command it installs is `receipt`

Or from a checkout, for development:

pip install -e .

Use

receipt run --task "what this is supposed to do" \
  --declare path/one.py,app/*.py \
  --dir . --out receipts/ \
  -- your-command --with --args

Omit --declare to just log what happened without a scope to check it against (unverified, still a written receipt, still exit 0).

Test

python tests/test_receipt.py

What touched means

touched is the union of every file that was added, removed, had its content modified, was renamed (a removed path and an added path sharing a content hash), or had its permission bits changed with content otherwise identical. A rename or a chmod on a path outside the declared scope is a real fail, named clearly — sneaky.txt (renamed from output.txt), or secret.env (permissions changed, content unchanged) — not silently folded into "nothing happened" the way a plain content-hash diff would.

What pass doesn't mean

pass only means "touched nothing outside the declared scope within --dir." A write anywhere outside that tree — /tmp, ~, a sibling directory, an absolute path elsewhere in a monorepo — is invisible to receipt and won't affect the verdict. Point --dir at the smallest tree that actually bounds what the task could legitimately touch; don't read pass as "touched nothing on the filesystem."

What redaction doesn't mean

Captured stdout/stderr and the command's own argv are swept for secret-shaped text (receipt/redact.py) before a receipt is written — this closes a real gap found during review: a wrapped command that echoed API_KEY=sk-... landed that value verbatim in the receipt JSON. The sweep is a regex net for common shapes, not a guarantee. It will not catch a secret with no recognizable shape (e.g. a bare 40-character hex string with no key name attached, split across two log lines, or base64-wrapped). If a command's output might contain something sensitive in an unusual shape, don't assume the receipt is safe to share as-is — read it first.

What's deliberately not here yet

No network/API-call capture — only filesystem diffing. "What did this agent touch" is answerable this way; "what did this agent call" isn't, without hooking into a specific agent framework's own trace or intercepting traffic, which is a real, separate, much bigger project.

No policy evaluation or rule composition beyond a flat declared-scope check — that's deliberately a different tool's job. receipt stays the evidence producer; invariant is where richer policy (is this evidence actually OK, across multiple runs, with other checks composed in) belongs.

MIT licensed.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

receipt_evidence-0.1.0.tar.gz (18.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

receipt_evidence-0.1.0-py3-none-any.whl (13.4 kB view details)

Uploaded Python 3

File details

Details for the file receipt_evidence-0.1.0.tar.gz.

File metadata

  • Download URL: receipt_evidence-0.1.0.tar.gz
  • Upload date:
  • Size: 18.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for receipt_evidence-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0de5d6dd3031fdc6b1923c143c2633bfd44d8f15aab3d68885bbba0f43647bb5
MD5 d1e51dff31c4996485ff354632b01444
BLAKE2b-256 860d6ace1d1640412f940873d46b6d56380620b2ccd946af335b665450e77b63

See more details on using hashes here.

Provenance

The following attestation bundles were made for receipt_evidence-0.1.0.tar.gz:

Publisher: release.yml on MaXiMo000/receipt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file receipt_evidence-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for receipt_evidence-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5716b0596a452ab6a2b2c44f211e319a8b8bb4c2284b4c9c988b37a7c9eb9724
MD5 e0748a6f848b3f69172e5fd0e8e6bee3
BLAKE2b-256 916b4f9a96f82fd93037c84e4dca79313129e33455bf8f8cc21ec87a5518475c

See more details on using hashes here.

Provenance

The following attestation bundles were made for receipt_evidence-0.1.0-py3-none-any.whl:

Publisher: release.yml on MaXiMo000/receipt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.1.1

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page