Skip to content
MrJev

Canny

Stop hook for Claude Code and Codex CLI that refuses a "done" while no check has passed since the last edit. Jev can only relax that refusal and never cause one, and it still blocks with no API key at all.

View on GitHub →

Hands-on review

A warden that refuses an agent's 'done' when the ledger holds no passing check. The model can relax that refusal, and it can never cause one.

Good for

  • A block that works with no API key at all, and says so in `canny status`
  • A gate you can prove: the decision is a pure function, exported
  • Refusing `npm test || true` and `npm test | tail` as evidence of a check

Watch out for

  • A model certain your message is not a 'done' claim will let the stop through
  • Only file edits count as changes; a heredoc through Bash did not register
  • GitHub detects no licence, though `package.json` says MIT

Tested Sep 22, 2026 at a266600711c2 · node:24 in Docker, offline with no API key; its 217 tests, its stop gate walked as a truth table, and seven ways of faking a passing check

How we reviewed this: we ran its CI sequence, then drove the real hook in node:24 with no API key and no network — a scratch repository, a file written, a stop refused, a check run, a stop allowed — walked its stop gate as a truth table through the exported pure function, and tried seven ways of faking a passing check. We made no Jev calls, and neither did it: canny status reported jev 0 calls.

What it is

A hook for Claude Code and Codex CLI. The README opens with the transcript that caused it:

Done. Skipped tests — one-liner, no branch to break.

That is the agent, verbatim, on this project’s first live run — it wrote a file with a heredoc, ran nothing, and finished. The rule it broke was in CLAUDE.md, “because a rules file only asks the model to remember, and nothing checks that it did”.

The one rule, and it holds

Facts go to code. Judgments go to Jev. Only facts can block.

decideStop is a pure exported function, which is what made this checkable rather than believable. We called it directly across the matrix:

situation verdict
code changed, no passing check, no Jev at all block
a passing check in the ledger allow
no code files touched allow
Jev is certain the message is not a “done” claim (0.01) allow
Jev is certain it is a done claim (0.99) block
Jev is unsure (0.50) block
a second stop with nothing since the block warn
the same, in strict mode block

Jev moves exactly one cell, in exactly one direction. It cannot cause a block, an unsure answer blocks, and no key blocks. Compare Jevmind, reviewed the same day, where a confident model answer allows the command the code’s own rules have just flagged.

End to end, with TYPESAFE_API_KEY unset and the container offline:

Write math.js                     → {}
Stop "Done. Skipped tests…"       → {"decision":"block","reason":"Canny: math.js changed, but no check
                                     has passed since the last edit. …"}
Bash npm test (exit 0)            → {}
Stop "Done."                      → {}            (allowed)

canny status
  verified  yes: `npm test` passed after the last edit
  stops     block, allow
  jev       0 calls, 0 cached, 0 failed

That last line is the part to notice. The tool reports how many times it asked the model, so “the deterministic rule stood alone” is something you can read off rather than infer.

It knows the ways you would cheat

The block message warns that a pipe into tail, a || true, or a trailing ; echo hides the exit status. That is not only advice — we tried each one after an edit, against a project whose test actually fails:

what the agent ran did “done” go through?
npm test reported exit 0 allow
npm test || true block
npm test | tail -1 block
npm test; echo ok block
echo ok block
cat package.json block
npm run build allow

An agent that wants to get past this has to actually run something whose own exit status is the result. That is the difference between a check and a ritual, and it is the best implementation of it we have seen.

One gap worth knowing

Changes are recognised from file-editing tools. Our first attempt used the README’s own heredoc — cat > math.js <<'EOF' through Bash — and it did not register as a code change, so the subsequent stop was allowed. Rewriting it as a Write tool call produced the block immediately.

This matters because the heredoc is exactly what the founding transcript describes the agent doing. We have not filed it, because we cannot tell from outside whether Claude Code reports that shape in a way Canny could catch, and the README’s own reproduction says the next run was refused. It is worth confirming on your own setup: edit a file through a shell redirect, then say you are done, and see whether Canny notices.

Engineering

MIT by package.json, though GitHub detects no licence — a detection failure rather than a missing grant, but licence scanners will flag it.

217 tests pass across 12 files. CI runs format, type-check, lint, build, and then git diff --exit-code -- dist — it fails if the committed build output does not match a fresh one. We reproduced that: the committed dist matches. For a tool distributed as a hook that runs on every agent turn, checking the artifact against the source is the right paranoia, and almost nobody does it.

Zero runtime dependencies. One test is a benchmark whose assertion is the shape of the thing: “every task fails untouched and passes with its reference fix; only the task’s own check decides”.

canny replay reproduced the session byte-for-byte across two runs.

Verdict

The clearest statement in this category of where a decision model belongs, and the only one we could verify as a truth table because the author made the gate a pure function and exported it.

Install it if you run coding agents unattended. Read decideStop either way — it is nine lines, and every project in this directory that routes a model into a safety decision should have to explain why it did not do it this way.

For the same instinct applied to shell commands, see Jevmind; for an agent loop built on the same split, JevLoop.

See how it compares with other tools in Best Jev tools, tested hands-on.

Review updated Sep 22, 2026. Numbers quoted from the project are its author's own; we don't publish our own measurements of Jev.

More in Guardrails & Safety

Abide

★ 211▲ 79

coldteadotai/abide

Hooks into Claude Code, Codex, and OpenCode, and asks Jev one question per rule whether each edit breaks your AGENTS.md or CLAUDE.md rules.

TypeScriptReviewed

Jevmind

★ 166▲ 1

dealerdefi/Jevmind

Nine skills over one brain: a shell-command gate, a diff triage, a router and more, each decision appended to a hash-chained ledger that names any record edited afterwards. Runs offline on local reflexes or against Jev.

PythonReviewed

pi-warden

★ 138▲ 85

DevMortimer/pi-warden

Guardrails for the Pi coding agent. Jev judges every write and edit against the rules in pi-warden.md.

TypeScriptReviewed

Get new Jev projects every week

New Jev releases, pricing changes, and the best new projects, once a week. No spam; unsubscribe anytime.

Powered by Buttondown. See our privacy policy.