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.). Only regular files are hashed — a FIFO, socket, or device node is skipped rather than opened, since open() on a FIFO with no writer on the other end blocks forever (a real bug, found and fixed: a --dir /tmp scope check hung two CI runs for a full 6 hours each before this guard existed).
  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.1.tar.gz (19.9 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.1-py3-none-any.whl (13.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: receipt_evidence-0.1.1.tar.gz
  • Upload date:
  • Size: 19.9 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.1.tar.gz
Algorithm Hash digest
SHA256 7eadedbc0306088c5aa886ddd188f3df5e0313e61ce3d8314d291053a11ced63
MD5 e91486aef22c2a83d8406bd987000455
BLAKE2b-256 7e8ae9e72b2e491d87d640f0e820f3a1031a9cd0e6314ac50ca0a333bad43029

See more details on using hashes here.

Provenance

The following attestation bundles were made for receipt_evidence-0.1.1.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.1-py3-none-any.whl.

File metadata

File hashes

Hashes for receipt_evidence-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 92b1fbfec8001e07c6a97153d911ff7ec1c6ad425320774a1a66af418d5616fc
MD5 3c3a47604795b170bbc60d27410fbc96
BLAKE2b-256 877bc0a174940f7bf1e71fdc4a229dd8c751081263935289f99f63b08c0fd8f4

See more details on using hashes here.

Provenance

The following attestation bundles were made for receipt_evidence-0.1.1-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

This release

0.1.1 This release

2 files

0.1.0

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