Skip to content

v0.3.0

Latest

Choose a tag to compare

@qkal qkal released this 22 Sep 23:24
f2c5e53

Canny 0.3.0 stops costing the agent a turn. It tells the agent at session start what a finished session needs, and fixes the one habit that got nearly every session blocked, so a guarded run takes no longer than an unguarded one. It also closes two ways a failing test could pass the done-gate, and lets a project keep its code from being sent to Jev.

Install or update as before: git -C ~/.canny/src pull --ff-only, or the prompt in the README. There is nothing to build.

Before you update

  • Run canny init again in each guarded project (or canny init --global) to add the new SessionStart hook. Without it the rest of this release still works, but the agent learns the done-gate rule from its first blocked Stop instead of up front.
  • ! npm test and true # npm test no longer count as a passing check. The first exits 0 when the tests fail, and the second runs none.
  • "allow": ["rules"] and ignore paths now keep edits from being sent to Jev, and both wait for canny trust, like every field that loosens the guard.

Faster sessions

  • A SessionStart hook tells the agent what the done-gate asks for and names the project's test command, read from package.json, a justfile, a Makefile, Cargo.toml, or go.mod.
  • A Bash check piped into tail (npm test 2>&1 | tail -20) is rewritten to run behind set -o pipefail, so it counts when it passes and reports its failure when it fails. On Claude Code the rewritten command still goes through your permission rules; Codex takes a rewrite only with an "allow" and asks for approval separately.
  • Measured with bench/, Opus 5, 25 pairs: no blocked Stops where every earlier guarded run had one, 25 of 25 passes with and without Canny, the same turns and cost, and the guarded arm 1.6 ± 3.4 s faster, down from 4.5 s slower. No measurable overhead; also no measured change in outcome, since both arms pass every task.

The done-gate

  • A negated check and a check named only in a comment no longer count, and a # after an escaped space no longer hides a pipe into tail.
  • Under pipefail, the last set -o or set +o pipefail in a command decides, and a check pattern in .canny.json such as ^npm test$ is matched against the command that feeds the pipe.
  • A shell command that rewrites a test file goes through the test-removal check, a ; inside quotes no longer splits a statement past the secret check, and a leading cd that goes nowhere keeps a git-ignored .env writable.

Privacy

  • The Jev rule check can be turned off with "allow": ["rules"] and skips the paths in ignore, so part or all of a project's code can stay off TypeSafe's API without unsetting the key.
  • ignore patterns match a path however the agent spells it (./x, a/../x), for the done-gate as well.

Development

  • bench/run.mjs gives five tasks to a headless Claude Code or Codex with and without Canny, judges them by hidden checks, and records turns, cost, and model time per run.
  • The test suite strips git's environment, so committing from a linked worktree no longer marks the main checkout bare. CI runs the tests on Node 22, 24, and 26 plus macOS, with CodeQL.

Full changelog: https://github.com/qkal/canny/blob/v0.3.0/CHANGELOG.md · v0.2.0...v0.3.0