RuleReceipt
source on GitHub
for AI coding agents · runs locally

Your agent read the rules.
Did it follow them?

Did it push straight to main? Touch a file you told it never to? RuleReceipt checks the finished session against your rules file (CLAUDE.md, AGENTS.md, .cursor/rules) and marks each rule followed, broken, or can't tell, with the exact line as proof.

$ npx rulereceipt check

Quick check, nothing to install — paste your rules file and see how much of it is checkable. Runs in your browser, nothing uploaded.

rule adherence, this sessionreading session…
sample session · 3 of 11 rules shown local only
✓ Rule 7 — Tests must be able to fail
✕ Rule 4 — Surface bad news first
evidence: reply led with passing tests, the one broken test was mentioned last
? Rule 11 — Fails closed on error
needs your judgment: no error occurred this session, so there is nothing to check against — a person has to decide
full run: 8 followed · 2 not followed · 1 needs your judgment
It reads the rules. It doesn't always follow them.
the problem, with receipts
“CLAUDE.md is a wish list, not a contract.”
— minatoplanb, after 200 lines of rules, each tied to a real past incident
Not unique to Claude — a structural limit of how LLMs process long instructions, documented, not guessed
#18660CLAUDE.md instructions are read but not reliably followed
#15443Claude ignores explicit instructions while claiming to understand them
#24318Claude ignores explicit user instructions and acts without approval
→A known limit of how LLMs process long instruction files, not a Claude-specific flaw — in Claude Code specifically, content starts getting selectively skipped after around 80 lines

Hand someone the receipt

One flag writes a single file you can email, attach to a ticket, or print to PDF.

$ npx rulereceipt check --html

Opens anywhere, forever

One self-contained file. No CDN, no fonts, no scripts, nothing loaded from the internet — so it still opens correctly from an email attachment, offline, years from now.

Failures first, never buried

What wasn't followed sits at the top, not underneath a comfortable list of passes. A report designed to be skimmed isn't a report.

It says what it can't prove

It never says "compliant." It covers one session, and rules needing judgment are marked for human review rather than guessed at — printed on the report itself, not hidden in a policy page.

See one. Here is a real report, produced by running the tool against a constructed session — two rules broken with the evidence quoted, one the tool refused to guess about, and three left for a human. The session file it read is published next to it, so you can download both and run rulereceipt verify yourself. One command to check that.
every report is hashed to the exact bytes it read

The person receiving it can check it's real.

Hand someone the report and the session file, and they can confirm the report describes that exact file, unaltered. They don't have to take the sender's word for it — and an edited session produces a different fingerprint.

sha256 326ad7a4fd772e5dbbe33c32235480cf60646da0c46687a90ebb752fa0dd8469

That is the real fingerprint of the session file published on this site. Download it and run shasum -a 256 — you should get exactly the string above.

Stop it finishing on a broken rule

A report tells you afterwards. This refuses to let the session end.

$ npx rulereceipt hook

Add this to .claude/settings.json — you add it, not us:

{ "hooks": { "Stop": [ { "hooks": [ { "type": "command", "command": "npx rulereceipt hook" } ] } ] } }

Point Claude Code’s Stop hook at that command. When Claude tries to finish, RuleReceipt reads the session it just had. If a rule was broken, Claude is handed the rule, the evidence, and instructions to keep working — it does not get to stop and tell you it’s done.

It checks the claim, not just the command

“All tests pass” is checked against whether a test actually ran and what it returned — including when nothing ran, which is the usual way a session ends on work it never looked at. A sentence is not evidence.

It blocks two things, both narrow

A claim a recorded run contradicts, and a claim of done that nothing in the session verified. Never a judgment call, never an LLM opinion. Across thirteen real sessions it stopped two, and both were read by hand before this sentence was written.

It cannot wedge your session

One interruption per stop, never a loop. If anything goes wrong — unreadable transcript, missing rules file, a bug in us — it lets you finish and says so on stderr. It fails open, on purpose.

The report and the gate disagree in exactly one place, on purpose. When a session claims work is done and nothing recorded verifies it, the report says “couldn’t tell” — the tests may genuinely have run in another terminal, and a transcript cannot see that. The gate refuses the exit anyway, because it is not saying the claim is false. It is declining to let “done” end a session with nothing behind it, and the way out is one sentence saying which parts were actually checked.
And one that refuses before the command runs. rulereceipt guard is a PreToolUse hook: it declines a call outright, for rules naming a file or a branch — “never modify .env”, “never commit to main”. It will not block a banned command on its own, and that is a measurement rather than a limitation of effort: doing so automatically refused 62.8% of 16,336 real commands, because nothing in a rules file marks which backtick is the prohibition. One rule refused npm run build 112 times — it forbids running Playwright unprompted and recommends the build. So you mark the clause yourself, once, with rulereceipt rules --forbid, and only what you marked can ever refuse anything.
The honest limit. It runs when Claude tries to stop, so it catches a finished session, not a command mid-flight — and it fails open, which means a bug in RuleReceipt silently stops gating rather than locking you out of your own editor. Those two costs are not symmetric and the default is not symmetric either. rulereceipt check in CI stays the backstop.

Private by default

The privacy story, stated plainly, not buried in a policy page. Full detail →

Offline by default

--llm, --share and --telemetry are opt-in and off until you turn them on. Full detail.

No account, no server, no login

Nothing of ours sits in the path of a check — so there is nothing of ours to breach.

Never writes what you didn't ask for

It never installs a hook and never touches settings.json — if you want it running as a gate you add those four lines yourself, and can delete them the same way. Only --html and rules write, both because you asked.

You can verify the package you install came from the code you read. Every release is built and published by GitHub Actions and signed with npm provenance — no publishing token exists on anyone's laptop to be stolen. After installing, run npm audit signatures and npm will confirm the package was built from this public repository, at a specific commit. That's the appropriate standard for a tool whose whole job is verification, and it's checkable by you, not asserted by us.

Pricing

The CLI is free and runs locally — unlimited checks, no card, no account. A paid Team tier (org-wide dashboards, compliance exports) is in development; the free CLI stays free. Full pricing → · For teams →

FAQ

Straight answers, including "not yet."

Does this work with Cursor, Codex, or other AI agents?

Two halves, answered honestly. Your rules: yes — it reads CLAUDE.md, AGENTS.md, Cursor's .cursor/rules (and legacy .cursorrules), GitHub Copilot's copilot-instructions.md, Windsurf's .windsurfrules, Gemini's GEMINI.md and Google's .agents/rules. Your sessions — what the agent actually did — eight agents are supported, each validated on a real session: Claude Code, OpenAI Codex, GitHub Copilot CLI, Cursor, Google Antigravity, OpenCode, Cline and Devin Desktop. Not supported: Aider (it keeps no record of tool calls). For the old Windsurf editor, use Devin Desktop. Gemini CLI is legacy only, since it refuses a personal login. Blocking works in Claude Code only; protect --git works with every agent.

Do I need my own Anthropic API key?

For the rules that need judgment, yes — the same key your Claude Code setup already uses. Rules naming something concrete — a branch, a file, a code call — are checked locally against what the session actually did, with no key and no cost.

Does this touch my CLAUDE.md or Claude Code settings?

Read-only. It reads your rules files (CLAUDE.md, AGENTS.md, and Cursor/Copilot/Windsurf rules) and your session transcript, and writes nothing back. No hooks get installed, no settings get modified — not automatically, not ever, without you explicitly asking.

Could someone edit the transcript before running the check?

Yes, and we won't pretend otherwise. The transcript is a file on your own machine — anyone with access to it could edit it before running rulereceipt check, and the report would faithfully describe the edited file. The hash proves the report matches the file it read; it does not prove that file is an unmodified record of the session.

What that means in practice: this is solid for the honest case — catching what an agent actually did when nobody's trying to hide anything, which is nearly every real session. It is not, today, tamper-evident against someone deliberately covering their tracks. Making it so would need the record signed where it's written, not where it's read — that's not something a tool reading a local file after the fact can do alone. We'd rather say that clearly than let "receipt" imply more than it delivers.

How do I know it won't just cry wolf?

Because that's the thing it's built against. On the frozen public test sets (v1–v4), there are 0 false accusations. On 44 of the maintainer's own sessions (mostly building RuleReceipt itself), 5 of 26 “broken” verdicts were wrong before 0.1.106; the cause is fixed, and a fresh re-measure on new sessions is coming. A "FAIL" comes only from hard evidence — a forbidden action that actually happened, or the agent's own claim contradicted by the session's own logs — enforced at build time, not left to hope. An AI opinion (with --llm) is labelled as an opinion and never counted as a verdict. When the evidence doesn't settle it, the answer is "couldn't tell," never a guess. A checker that accuses you wrongly is worse than no checker, so it's optimised to stay quiet unless it's sure. You don't have to take our word for it — rulereceipt selftest runs the checkers on bundled examples with known answers, on your machine, with zero network calls. Full numbers and method: the accuracy page.

Can I gate my CI on it?

Yes. rulereceipt check exits non-zero when a rule is broken, and there's a verify-receipt command plus a GitHub Action so a pull request fails if a session broke policy. You mark which rules are hard errors versus warnings in a committed .rulereceipt/config.json, so CI gates on what actually matters instead of going red on day one.