Receipts for pull requests written by Claude Code: every hunk stamped with the last test, lint or build the agent ran after writing it, and the hunks it never ran. Reads the transcripts already on your machine. 0 LLM tokens, nothing uploaded. Try: npx hunkproof --pr <n>
See the codegit diff shows what the agent changed. hunkproof shows what it ran after each hunk, and the hunks it never ran. Reads the Claude Code transcripts already on your disk. 0 LLM tokens, nothing leaves your machine.
On the laptop that built it, 69% of the agent's commits that followed its own edits (270 of 392, over 380 transcripts) included a file edited after the last run. npx hunkproof stats prints yours (snapshot).

fixtures/unrun-example: the 6 of 26 hunks with no run after their last edit, the README and CHANGELOG lines written after the last npm test (still image).
npx hunkproof --pr <n>
claude plugin marketplace add JulienAu/hunkproof && claude plugin install hunkproof@hunkproof
Line 1: the CLI, Node 22 or later, from the repository. Line 2: the Claude Code plugin, with the skill, /hunkproof:receipt and the Stop hook. Or paste into Claude Code: "Install github.com/JulienAu/hunkproof and run it on this branch"; docs/install/agent.md is the page it follows.
ran, passed, ran, failed, ran, outcome not parsed, unrun (no run after its last edit) or unattributed (no session edited that file), decided by the last run after the hunk's last edit.--open): the diff lines, the sentence the agent wrote with the edit when there is one, the rewrites, the timeline of runs.--post): the unrun hunks with links and the run that decided each hunk, sent to the PR with gh after you type y.<repo>/.hunkproof/ (add it to .gitignore) and to a git note on the head commit. npx hunkproof --base main when there is no PR yet.npx hunkproof --base 559fee6 --head 7fbda28 on the pull request that prepared v0.1, taken before anything else ran: its docs commits edited the README and the CHANGELOG after the last npm test. Output kept in fixtures/unrun-example. Line-matched: the hunk's added lines were found in a session edit; file-level: the file was edited but the lines were not found, so the timing is relative to the file's last edit.
hunkproof · 559fee6...7fbda28 · 20 of 23 line-matched hunks had a run after their last edit (20 passed, 0 failed, 0 outcome not parsed) · 3 file-level (lines not matched to an edit) · 6 unrun · 0 unattributed · 6 sessions · hunkproof used 0 LLM tokens
The 6 unrun rows of the terminal output:
CHANGELOG.md +18,3 unrun none after edit at 17:53:47
README.md +9 unrun none after edit at 17:51:34
README.md +31,3 unrun none after edit at 17:53:42
README.md +47 unrun (file-level) none after edit at 17:53:42
README.md +61,0 unrun (file-level) none after edit at 17:53:42
README.md +72 unrun (file-level) none after edit at 17:53:42
The 20 other hunks had npm test run after them at 17:45:10; the 6 unrun ones (3 line-matched, 3 README hunks at file level) are the lines written after it. The next npm test in that session stamps them ran, passed: a receipt says what had run when it was written, so take it before the review, not after.
npx hunkproof --pr 2 --base 1af692a4 --head 102eae0 on the private repository where v0.1 was built, 2026-10-14, output kept in fixtures/self-pr; test/fixture.test.js holds these numbers and rebuilds the HTML and Markdown from the saved evidence byte for byte.
hunkproof · PR #2 · 64 of 64 line-matched hunks had a run after their last edit (64 passed, 0 failed, 0 outcome not parsed) · 3 file-level (lines not matched to an edit) · 0 unrun · 1 unattributed · 9 sessions · hunkproof used 0 LLM tokens
| hunks | attributed | run after last edit | unrun | unattributed | sessions | elapsed | LLM tokens used by hunkproof |
|---|---|---|---|---|---|---|---|
| 68 | 64 | 64 | 0 | 1 | 9 | 3.5 s | 0 |
The 3 file-level hunks are CONTRACT.md edits whose exact lines were not found in an edit; the unattributed hunk is a .gitignore line edited before the branch base. A merged PR is diffed from its merge commit and transcript events after the merge are left out.
hooks/hooks.json). When the agent is about to hand back, it prints hunkproof: 2 hunks edited in this session have no run after their last edit, then one file:line per hunk, 10 at most.HUNKPROOF_DEBUG=1 names the reason on stderr. It writes nothing.HUNKPROOF_STOP=block returns the hook's block decision instead, with the same list and the project's checks (npm test, make check, whichever exist), so the agent runs the unrun hunks before it stops. HUNKPROOF_STOP=off turns it off.HUNKPROOF_BASE=<ref> picks the base (default: origin's HEAD, main or master)..claude/settings.json: the snippet, the version to pin, the timing (1.4 to 1.7 s on this repository) and how Claude Code shows a block are in docs/stop-hook.md.| shows what changed | shows what ran after the change | per hunk | LLM tokens | leaves your machine | |
|---|---|---|---|---|---|
| hunkproof | yes | the last run after each hunk's last edit, with its outcome | yes | 0 | no (transcripts); gh fetches the PR metadata, --post sends the comment after you type y |
| git diff | yes | no | yes | 0 | no |
| GitHub PR review | yes | CI status on the head commit, not per hunk | comments per line | 0 | the PR is already there |
| Entire CLI | yes: transcript, prompts, files touched, tool calls per checkpoint | ? | per commit; per line with entire blame | optional, for summaries written by an agent at commit time | yes: transcripts live in git refs with the repository; anonymous usage stats unless --telemetry=false |
| git-ai | yes: agent, model and prompt per line | ? | per line (git ai blame) | none mentioned | no: offline, no login |
| claude-replay | the whole session, with a diff view per edit | the commands in session order, not tied to a change | no | none mentioned | no, but the HTML embeds the full transcript |
| mindwalk | files touched, on a 3D map | one session-level signal, "edits after the last verify" | no, per file | 0 to view; the optional evaluation calls your claude or codex CLI | nothing while viewing; a session summary when you run the evaluation |
Filled from each project's README on 2026-10-14: Entire CLI, git-ai, claude-replay, mindwalk. A "?" means the README does not say. The git diff and GitHub rows describe them as used, there is no README to cite. Also nearby: Anthropic's receipts plugin (a personal usage report from the same transcripts) and pr-provenance-stamp (a provenance note in the PR body at creation time).
git diff -U0 base...head gives the hunks: one contiguous block of added or removed lines each, identified by file and first new line.~/.claude/projects: transcripts that mention the PR, edited the diff's files in the branch's time window, or ran on its branch or in its directory, subagents included.--post sends the redacted Markdown to the PR only after you type y.Receipts for pull requests written by Claude Code: every hunk stamped with the last test, lint or build the agent ran after writing it, and the hunks it never ran. Reads the transcripts already on your machine. 0 LLM tokens, nothing uploaded. Try: npx hunkproof --pr <n>
See the codegit diff shows what the agent changed. hunkproof shows what it ran after each hunk, and the hunks it never ran. Reads the Claude Code transcripts already on your disk. 0 LLM tokens, nothing leaves your machine.
On the laptop that built it, 69% of the agent's commits that followed its own edits (270 of 392, over 380 transcripts) included a file edited after the last run. npx hunkproof stats prints yours (snapshot).

fixtures/unrun-example: the 6 of 26 hunks with no run after their last edit, the README and CHANGELOG lines written after the last npm test (still image).
npx hunkproof --pr <n>
claude plugin marketplace add JulienAu/hunkproof && claude plugin install hunkproof@hunkproof
Line 1: the CLI, Node 22 or later, from the repository. Line 2: the Claude Code plugin, with the skill, /hunkproof:receipt and the Stop hook. Or paste into Claude Code: "Install github.com/JulienAu/hunkproof and run it on this branch"; docs/install/agent.md is the page it follows.
ran, passed, ran, failed, ran, outcome not parsed, unrun (no run after its last edit) or unattributed (no session edited that file), decided by the last run after the hunk's last edit.--open): the diff lines, the sentence the agent wrote with the edit when there is one, the rewrites, the timeline of runs.--post): the unrun hunks with links and the run that decided each hunk, sent to the PR with gh after you type y.<repo>/.hunkproof/ (add it to .gitignore) and to a git note on the head commit. npx hunkproof --base main when there is no PR yet.npx hunkproof --base 559fee6 --head 7fbda28 on the pull request that prepared v0.1, taken before anything else ran: its docs commits edited the README and the CHANGELOG after the last npm test. Output kept in fixtures/unrun-example. Line-matched: the hunk's added lines were found in a session edit; file-level: the file was edited but the lines were not found, so the timing is relative to the file's last edit.
hunkproof · 559fee6...7fbda28 · 20 of 23 line-matched hunks had a run after their last edit (20 passed, 0 failed, 0 outcome not parsed) · 3 file-level (lines not matched to an edit) · 6 unrun · 0 unattributed · 6 sessions · hunkproof used 0 LLM tokens
The 6 unrun rows of the terminal output:
CHANGELOG.md +18,3 unrun none after edit at 17:53:47
README.md +9 unrun none after edit at 17:51:34
README.md +31,3 unrun none after edit at 17:53:42
README.md +47 unrun (file-level) none after edit at 17:53:42
README.md +61,0 unrun (file-level) none after edit at 17:53:42
README.md +72 unrun (file-level) none after edit at 17:53:42
The 20 other hunks had npm test run after them at 17:45:10; the 6 unrun ones (3 line-matched, 3 README hunks at file level) are the lines written after it. The next npm test in that session stamps them ran, passed: a receipt says what had run when it was written, so take it before the review, not after.
npx hunkproof --pr 2 --base 1af692a4 --head 102eae0 on the private repository where v0.1 was built, 2026-10-14, output kept in fixtures/self-pr; test/fixture.test.js holds these numbers and rebuilds the HTML and Markdown from the saved evidence byte for byte.
hunkproof · PR #2 · 64 of 64 line-matched hunks had a run after their last edit (64 passed, 0 failed, 0 outcome not parsed) · 3 file-level (lines not matched to an edit) · 0 unrun · 1 unattributed · 9 sessions · hunkproof used 0 LLM tokens
| hunks | attributed | run after last edit | unrun | unattributed | sessions | elapsed | LLM tokens used by hunkproof |
|---|---|---|---|---|---|---|---|
| 68 | 64 | 64 | 0 | 1 | 9 | 3.5 s | 0 |
The 3 file-level hunks are CONTRACT.md edits whose exact lines were not found in an edit; the unattributed hunk is a .gitignore line edited before the branch base. A merged PR is diffed from its merge commit and transcript events after the merge are left out.
hooks/hooks.json). When the agent is about to hand back, it prints hunkproof: 2 hunks edited in this session have no run after their last edit, then one file:line per hunk, 10 at most.HUNKPROOF_DEBUG=1 names the reason on stderr. It writes nothing.HUNKPROOF_STOP=block returns the hook's block decision instead, with the same list and the project's checks (npm test, make check, whichever exist), so the agent runs the unrun hunks before it stops. HUNKPROOF_STOP=off turns it off.HUNKPROOF_BASE=<ref> picks the base (default: origin's HEAD, main or master)..claude/settings.json: the snippet, the version to pin, the timing (1.4 to 1.7 s on this repository) and how Claude Code shows a block are in docs/stop-hook.md.| shows what changed | shows what ran after the change | per hunk | LLM tokens | leaves your machine | |
|---|---|---|---|---|---|
| hunkproof | yes | the last run after each hunk's last edit, with its outcome | yes | 0 | no (transcripts); gh fetches the PR metadata, --post sends the comment after you type y |
| git diff | yes | no | yes | 0 | no |
| GitHub PR review | yes | CI status on the head commit, not per hunk | comments per line | 0 | the PR is already there |
| Entire CLI | yes: transcript, prompts, files touched, tool calls per checkpoint | ? | per commit; per line with entire blame | optional, for summaries written by an agent at commit time | yes: transcripts live in git refs with the repository; anonymous usage stats unless --telemetry=false |
| git-ai | yes: agent, model and prompt per line | ? | per line (git ai blame) | none mentioned | no: offline, no login |
| claude-replay | the whole session, with a diff view per edit | the commands in session order, not tied to a change | no | none mentioned | no, but the HTML embeds the full transcript |
| mindwalk | files touched, on a 3D map | one session-level signal, "edits after the last verify" | no, per file | 0 to view; the optional evaluation calls your claude or codex CLI | nothing while viewing; a session summary when you run the evaluation |
Filled from each project's README on 2026-10-14: Entire CLI, git-ai, claude-replay, mindwalk. A "?" means the README does not say. The git diff and GitHub rows describe them as used, there is no README to cite. Also nearby: Anthropic's receipts plugin (a personal usage report from the same transcripts) and pr-provenance-stamp (a provenance note in the PR body at creation time).
git diff -U0 base...head gives the hunks: one contiguous block of added or removed lines each, identified by file and first new line.~/.claude/projects: transcripts that mention the PR, edited the diff's files in the branch's time window, or ran on its branch or in its directory, subagents included.--post sends the redacted Markdown to the PR only after you type y.