Don't ask your agent to run the tests.
Force it.

Verification for coding agents. Krites runs your checks itself and blocks the stop until they pass.

$claude plugin marketplace add krites-dev/krites
$claude plugin install krites@krites -y
Requires Node 20 or laterClaude Code · free · Apache-2.0README

What the agent tries.

What happens
Says the tests pass, never ran them.RUNKrites runs them itself and reads the exit codes.
Hook times out, agent proceeds.HELDAn internal deadline blocks first, below Claude Code's own timeout.
Edit tool writes outside the repo.DENIEDDenied on the resolved real path, symlinks included.
Second stop attempt skips the gate.RERUNThe gate runs on every stop.
exit 2 leaks hook paths to the model.SCRUBBEDJSON block, relative paths only.

Telemetry is opt-in and asked until you answer: a ping of a random install UUID, plugin version, OS, and timestamp, nothing else. Decline it and nothing is sent. This build ships with no endpoint, so nothing is asked yet.

What the gate proves

Which commands ran, with which exit codes, on which diff, at which commit. Signed.

Krites runs the checks itself. The edit tools are denied on protected paths; a protected path changed any other way refutes the next stop. The receipt is signed. It does not prove your tests are good; it proves they ran.

How it works

Three steps · none of them trust the agent
01

Protect

Name the paths the agent may not touch in krites.toml. The edit tools are denied on them, on krites.toml itself, and on anything outside the repo, before the write lands.

fig. 1 · protected paths, edit tools denied
02

Gate

When the agent tries to stop, Krites runs your check commands in order. All exit 0 and the stop goes through. The first failure blocks it, up to three times per chain by default, and hands the agent the command and its last 60 lines.

fig. 2 · the stop is blocked until exit 0
03

Receipt

/krites:receipt writes the last run as signed JSON and Markdown under receipts/: commands, exit codes, durations, diff stat, config hash. Anyone can verify it offline with one dependency-free file.

receipts/2026-09-21T15-49-43Z-6d50a7a6.jsonkrites.receipt/0.1
{
  "checks": [
    {
      "command": "cargo fmt --check",
      "duration_ms": 162,
      "exit_code": 0,
      "tail": ""
    },
    {
      "command": "cargo test",
      "duration_ms": 536,
      "exit_code": 0,
      "tail": "   Compiling krites-fixture v0.1.0 (.)\n    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.30s\n     Running unittests src\\lib.rs (target\\debug\\deps\\krites_fixture-b47e9f1eb22c7040.exe)\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n     Running tests\\total.rs (target\\debug\\deps\\total-ab1a7d6bba047f68.exe)\n\nrunning 1 test\ntest total_sums_the_values ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n   Doc-tests krites_fixture\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n"
    }
  ],
  "commands": [
    [
      "cargo",
      "fmt",
      "--check"
    ],
    [
      "cargo",
      "test"
    ]
  ],
  "config_hash": "a886c46fc87596954156251643cf52d997ed033690c4cfb25c46612b634d15e5",
  "created_at": "2026-09-21T15:49:43Z",
  "diff": {
    "deletions": 1,
    "files": [
      "src/lib.rs"
    ],
    "files_changed": 1,
    "insertions": 1
  },
  "fingerprint": "d10e3ed5362a034fabb932db8dd710d3fab41648d67ada74bedc37d497387754",
  "plugin_version": "0.1.0",
  "reason": null,
  "repo": {
    "dirty": true,
    "head": "6d50a7a6911a3718a704e0243acb3e0f5ae7a122",
    "root_hash": "d50d438f2b5f23e378230279f0c65b317d554b8e43f365334e1a5a89460566af"
  },
  "schema": "krites.receipt/0.1",
  "session_id": "v0-sample",
  "signature": {
    "alg": "ed25519",
    "public_key": "S8B0by11nSvB+iMlF9ElY2jp7eOuRP8Afkz4CM7kiz8=",
    "sig": "Fu9mEWmxCWcGuSTUB5YrpDEJ9Wsmy7CxSqbq2HlPZofOYvW1/EyVYS68LBEdycRIzLa2t746lRM4CUYD48KCCg=="
  },
  "verdict": "passed"
}
$ node verify-receipt.js --key 6b11164d362ea9b5 receipts/2026-09-21T15-49-43Z-6d50a7a6.json
valid: signed by 6b11164d362ea9b5c7711d8f5465d76c7d6da068a55f9b4253bd697171978bfa, verdict passed
fig. 3 · evidence from a machine you trust

Every gate leaves a receipt.

Commands, exit codes, durations, and the diff stat, exported with /krites:receipt as signed JSON and Markdown. Attach one to the pull request and stop answering "did you run the tests?"

14:02:11npm testexit 08.4s+61 −4 · 2 files
14:02:19npx tsc --noEmitexit 03.9s
14:02:23npx eslint . --max-warnings 0exit 02.2s
13:48:02npm testexit 17.1s+58 −4 · 2 files · refuted
14:02:11npm testexit 08.4s+61 −4 · 2 files
14:02:19npx tsc --noEmitexit 03.9s
14:02:23npx eslint . --max-warnings 0exit 02.2s
13:48:02npm testexit 17.1s+58 −4 · 2 files · refuted
11:20:44cargo test --lockedexit 041.2s+212 −37 · 6 files
11:19:58cargo test --lockedexit 10139.8srefuted · stop blocked
11:21:26cargo clippy -- -D warningsexit 012.0s
11:21:39cargo fmt --checkexit 00.3s
11:20:44cargo test --lockedexit 041.2s+212 −37 · 6 files
11:19:58cargo test --lockedexit 10139.8srefuted · stop blocked
11:21:26cargo clippy -- -D warningsexit 012.0s
11:21:39cargo fmt --checkexit 00.3s
09:05:17pytest -qexit 014.6s+19 −2 · 1 file
09:05:32ruff check .exit 00.4s
09:03:50pytest -qexit 113.9srefuted · stop blocked
09:05:33mypy srcexit 06.8s
09:05:17pytest -qexit 014.6s+19 −2 · 1 file
09:05:32ruff check .exit 00.4s
09:03:50pytest -qexit 113.9srefuted · stop blocked
09:05:33mypy srcexit 06.8s
commandsVerbatimThe exact command Krites ran, its exit code, and how long it took. Nothing summarized by the agent.
diffThe diff, at a commitFiles changed, insertions, deletions, HEAD, and the hash of the krites.toml that governed the run.
signatureTamper-evidentSigned with a per-install ed25519 key, so a receipt on a PR is evidence, not a screenshot.
localYoursWritten to receipts/ in the repo. No account, no upload, no model call.

The plugin is free. Pro has 100 founding seats.

Per seat · no usage fees
Free$0 · Apache-2.0

The Claude Code plugin: protected paths, the stop gate, signed receipts, and the verifier. That's all that exists today, and it's all free.

Install the plugin
Founding seat100 seats
$99 / year, lockedPro planned at $240/yr

Pro is in development: the switcher, cross-agent adapters, and the observatory. Your year starts the day Pro ships, not today.

Claim a founding seat · $99Full refund any time before Pro ships, and without asking if it hasn't shipped by the date you pick at checkout.

Krites never holds a vendor credential and never meters a token. License keys verify offline; if you stop paying, the version you have keeps working.

Tell me about the last time your agent lied about tests.

Book 20 minutes