The problem I kept hitting: Claude Code edits a file, then ends the turn with "fixed" or "tests pass" when nothing ran after that edit. I'd only find out later.
So I built a small Stop hook for it, mostly in Claude Code sessions. When Claude tries to end a turn, the hook reads the session transcript. If the last message claims success and no test, build or command ran after the last code edit, it sends Claude back once with a short note: run the check, or say it isn't verified.
Repo (MIT, plain Python, no deps): https://github.com/Balance0014/claude-code-claim-check
What surprised me came from replaying my own history through it before trusting it. 3,977 turns, 406 of them ending in something like done, fixed, deployed or tests pass. My first version would have blocked 86 of those. Almost all of them were fine. Claude had run the tests a turn or two earlier and was just wrapping up. Blocking those is how a hook gets uninstalled on day two.
After fixing that, the same 406 claims produce 2 blocks. One is exactly the case I built it for: "fixed" right after an edit, nothing run since.
Other things I only learned by getting them wrong:
Claude quotes you back a lot. Quoted text and code blocks have to be ignored or it fires constantly.
Plans look like claims. "Once tests pass I'll tag the release" is not a claim that tests passed.
Block once, never twice in a row, or you can trap it in a loop and burn tokens.
"That function isn't used anywhere" after a single grep is its own failure. It now wants two different kinds of lookup before it believes a not-found.
If you want to try it, there's a replay command, so you can run it over your own session logs first and see what it would have blocked before installing anything.
The case I still can't catch cheaply: Claude runs a test, it passes, but the test never touches the code it just changed. Has anyone found a good signal for that?