0327GitHub
Canny
Evidence ledger that challenges unsupported "done" claims from coding agents.
qkal/CannyREADME ↗
# Canny A warden for AI coding agents. It hooks into Claude Code and Codex CLI, keeps a ledger of what the agent actually did, and will not let it finish on a claim. [](https://github.com/qkal/canny/actions/workflows/ci.yml) [](LICENSE) [](#install-by-pasting-a-prompt) [](#claude-code-and-codex-differ-in-four-places) [](package.json) [](https://typesafe.ai) [](https://github.com/qkal/canny/commits/main) > Done. Skipped tests — one-liner, no branch to break. That is Claude Code, verbatim, during this project's first live run. It had been asked to add a function, it wrote the file with a shell heredoc, ran nothing, and finished. No error. No warning. Nothing in `CLAUDE.md` could have stopped it, because a rules file only asks the model to remember, and nothing checks that it did. Canny is the hook that noticed. On the next run of the same prompt, the agent's "done" was refused with this message: > Canny: math.js changed, but no check has passed since the last edit. The last command was `cat > math.js <<'EOF' …` (exit 0). Run the project's checks and fix what fails before finishing. A test, build, lint, or type-check command counts. If no check applies to this change, say so explicitly and stop again. Claude ran `npm test`. It passed. The next "done" went through. The whole exchange is in the [session ledger](#what-a-guarded-session-looks-like) below. ## The one rule **Facts go to code. Judgments go to Jev. Only facts can block.** A fact is something the ledger can prove: a file changed, a command ran, it exited 1, the same command failed with the same output three times, the text about to be written contains an AWS key. Code decides those, offline, with no API key. A judgment is something code cannot decide: does this message claim the work is done, does this d