Ask Claude What Your Agent Actually Did
There are times (unattended sessions, parallel sessions, or work from weeks ago) when I cannot reconstruct what I asked Claude Code to do and what it actually ended up doing. Yes, I can scroll the session. I can grep the JSONL files. I can replay it. But that is an hour of trawling, and then there are the git diffs. Developer velocity stopped being my bottleneck months ago. Comprehension velocity is the bottleneck now: the agents write faster than I can understand what they wrote.
The obvious fix is to ask the agent. “My brain is fried, catch me up.” It half works, and the half that fails is structural. After compaction, the session cannot really remember what it did — it can only summarize what the transcript says it did. And either way, the summary is written by the same party that did the work. It will never say “I claimed this edit but it never landed.” Not because it lies, but because a status report and its author are the same process. Nobody audits their own memoir.
Git, on the other hand, was there the whole time, and git does not summarize. It records.
Recap: the session as a story, checked
So I built gitreceipts, and the way I actually use it every day is through the Claude Code plugin:
/plugin marketplace add cloudcraft-ai/claude-plugins
/plugin install gitreceipts@cloudcraft
Then, in any session, in any repo:
/gitreceipts:recap
Recap reconstructs the session as a story, interval by interval, prompt to commit: what was asked, what was claimed, what actually landed in git. The story is not just file edits: the commands the session ran and the MCP calls it made are part of the recap too, errors included. Every claim about the repo is checked against git history before it reaches you. It is built for whoever was not there: the human coming back to an unattended session, or the agent itself after compaction, when the receipt hands back a prompt-to-commit chain the session no longer holds.
Under the plugin is a plain Rust CLI (git receipts, via brew or cargo), and recap is its default command. The deterministic binary produces the evidence. Claude narrates it. The division of labor matters: the story can be told well because the facts underneath cannot be sweet-talked.
Two questions, two commands
Recap answers “where are things.” Audit answers “where should I look.”
/gitreceipts:audit takes the same evidence and climbs it as a ladder: 🟢 a claim that landed, verified. ⚪ a finding with an explanation: a scratch file deleted, a failed command, a gitignored build artifact doing what gitignore is for. 🟡 residue nobody explains. 🔴 a broken promise: the agent said it did something, and git has no record that it happened.
One honesty rule shapes the ladder: a verdict is only ever derived from what git can witness. Commands and MCP calls have no such oracle. The tool cannot know what a command was supposed to accomplish, so their errors are reported, marked grey, and offered as “this looks interesting, take a look,” never converted into convictions. Deriving anything stronger there is a problem for the future, and it is labeled as one.
A red is a question, not a conviction. The first time audit flagged one on a real project of mine, Claude explained it in the context of what the session had been trying to do. It was right to flag it, and the explanation was right too. That is the experience that convinced me the plugin is the product, not just a wrapper: an auditor that explains its findings beats a wall of diffs every time.
The week the tool audited itself
Here is the part I am most obliged to tell you, because this tool’s entire premise is that confident reports need checking.
In one week of running gitreceipts against real machines and real history (950 agent commits across my own repos), I found and removed five provenance claims the tool itself was making that its evidence could not support. Labels like “created elsewhere (pulled/fetched),” which actually measured how far back the clone’s reflog went, and mislabeled 110 of 180 commits made on the very machine I was sitting at. Labels like “possibly a human edit,” which survived three review sweeps precisely because it was hedged. Hedging is where this failure mode hides: it reads as caution while still naming a person.
The biggest fix was identity. Early versions built the commit timeline from the session’s time window, so in a shared repo, a colleague’s commit landing mid-session could be counted as the agent’s, and its unexplained files became your residue and your verdict. That is a false accusation from the one tool that must never make them. Now a commit is yours only when git records you as author or committer, by name or email, with .mailmap honored. One repo in my corpus carries three different emails under one name; matching on email alone would have silently discarded three quarters of the author’s history as somebody else’s.
When I started this corpus, the tool reported 50 broken promises. After the week of fixes: 5. Four of every five reds were the tool’s own inference errors — each one found by the tool auditing itself. I know of no better argument for the product than the fact that it caught its own bugs, and no more honest one than admitting they were there.
The sixth instance was in my README
After the release was frozen, I asked one more question: “we are doing blob check, not just whether the file is in git, correct?”
Not uniformly, it turned out. A claim that lands in the next commit was verified at path level: the commit touched the file, so the claim counted. Only late landings got true content verification against the blob. My README said otherwise. The sentence the product’s central promise rests on was overstated, and it survived because nobody re-read it against the code.
It is corrected in the README, in KNOWN-LIMITATIONS, and as a public issue (#5) with a measured direction for deeper per-commit diff verification. But I am putting it here, in the announcement, rather than burying it: if a week of this taught me that agent self-reports need checking, it also taught me that documentation is a self-report too.
That is also why the issue tracker went public the same day we submitted to directories, and why every open entry in KNOWN-LIMITATIONS links to an issue. A tool that exists to catch confident-but-wrong reports should not keep a private list of the ways it is wrong.
The day Claude found a use case I had not
I am building a macOS app (still in stealth), and I run it as four parallel Claude Code sessions on one repo: backend engine, app development, UI design, and a PM session coordinating the other three. The PM’s job is relaying claims it cannot check: a lane reports “done, all green” over MCP, and that gets passed upward as fact.
One evening I asked the PM session to run a recap. It scoped the recap to its own session first, and it was politely unimpressed: it already knew all of this — it had just done it. Fair. An agent with its context intact does not need a recap; it is the state. Recap is for humans, not for agents whose context is intact. That is the least useful case for this tool, and Claude found it in one try.
Then I asked it to widen the recap to every session that had touched the repo (with the CLI that is actually the default; one session is the narrowing). It found one claim the developer session had made that never landed in the repo. The developer session could not explain it. The PM session could not explain it. It asked me whether I remembered what happened.
And then it said the thing the whole tool is built on, unprompted: the best it could have done otherwise was ask the developer session over MCP — and take its word for it. The lane writes its own summary. It does not write the git history.
And then it did something close to apologizing: it pointed out that because it is not set up to run this tool by default, it could not have caught the miss on its own. Whether it should be (a cross-session recap at every coordination point) is now an open question I have not resolved. An auditor that invites itself is a different product than an auditor you invite, and I want to get that boundary right.
The deterministic CLI underneath is the real deal
The plugin is how I read one session. The CLI is how I sweep everything.
git receipts --project ~/Developer/Projects walks a folder, discovers every git repo under it that has Claude Code sessions, and rolls up verdicts per repo: that is my whole portfolio, every agent session across every project, in one pass. The scope flags compose like git’s own: --this-session narrows to one live session, --full-history opens the when (your commits beyond the session window), --all-authors opens the who, --me claims additional identities as yours. --filter red-amber climbs to the unanswered findings; --exit-code maps the verdict onto 0/1/2 so a script or a CI job can act on it.
And it is deterministic in the way that word is supposed to mean. Same input, same numbers, every run. It renders to console, HTML, and JSON, and the three outputs are required to agree to the digit. There is a QA harness in the repo (qa/, point it at your own repos) that verifies exactly that, plus exit codes, self-contained HTML, and no home-path leaks. It has caught real bugs before every release; nothing ships while any number disagrees. Behind it, 100+ unit and end-to-end tests, heavy on the adversarial cases reconciliation has to survive: amended commits, relocated files, hostile session input.
The console is not the only face. Every view also renders as a self-contained HTML page (theme-aware, no external assets, works offline) and as JSON:
git receipts recap --format html --compact > recap.html # the story, as a page to keep or share
git receipts audit --format html --compact > audit.html # the verdict
git receipts export > receipt.json # the data, complete
In Claude Code the same abilities are one command each: /gitreceipts:HTMLReport-Compact for a page small enough to read in the chat pane, /gitreceipts:HTMLReport-Full for the complete record in your browser, /gitreceipts:JSONExport for the data. The compact page clips long output but every cap says how much it hid and points at the JSON, which stays complete. The report never quietly omits.
This is the division of labor the whole product rests on: Claude is the best interface this evidence could ask for, and the evidence is the part Claude cannot improvise.
The numbers, as they stand
Across my corpus at 0.1.5: 950 agent commits, 1,684 of 1,714 claims landed (98%), 5 broken promises (771 green, 130 grey, 44 amber, 5 red). The tool’s own development, audited by itself: 190 commits, 354 of 354 claims landed, 0 broken promises. One limit stated plainly: my corpus is single-contributor, so the identity fix is evidenced by fixtures and simulated-identity runs, not by a live multi-author repo. That is in RECEIPTS.md too.
Private by construction
Everything runs locally. The binary reads your session logs and your repo; nothing leaves your machine, there is no server, and there never will be one in the read path. A secrets scanner is always on, and redaction flags exist for anything you choose to share. Your receipts are a mirror, not a badge.
Try it
Claude Code plugin: /plugin marketplace add cloudcraft-ai/claude-plugins, then /plugin install gitreceipts@cloudcraft.
CLI without the plugin: brew install cloudcraft-ai/tap/gitreceipts or cargo install gitreceipts, then git receipts in any repo you have used Claude Code on.
Source, receipts, known limitations, and the issue tracker: github.com/jagmeetchawla/gitreceipts. MIT.
If you run multiple sessions on one repo and try this on a lane you have only known through its own reports, I would genuinely like to hear what you find.
Subscribe
Occasional notes on building things — mostly software, sometimes with AI. No spam.
No spam. Unsubscribe anytime. Powered by Buttondown.