Countercheck
Proof that AI work really happened.
AI coding agents often say "done, all tests pass" when it isn't true: they skip a failing test, edit the test so it
passes, or never run it. countercheck verify checks the claim itself:
- It reads your checks from the base revision (before the agent's change), so the agent can't weaken them.
- It copies the agent's files into a clean folder and puts back any protected file the agent changed (your tests).
- It runs the checks in a macOS sandbox: no internet (localhost works; a check can opt in with
network = true), writes only inside that folder. - It writes a receipt: the claim, what ran, the output, what the agent changed that it shouldn't have, the cost if known, and a verdict. Receipts are sealed with a SHA-256 hash, so edits are detectable.
$ countercheck verify --claim "Fixed the discount bug. All tests pass." --agent claude-code --cost-usd 0.42
countercheck receipt 9073a384e88f9d37 FAILED
claim: "Fixed the discount bug. All tests pass." -> CONTRADICTED by evidence
checks: .countercheck/checks.toml @ HEAD (locked), sandbox: macos-seatbelt (no internet, localhost only; writes only in the check folder)
protected Modified: tests/test_pricing.py -> restored_from_base
[FAIL] unit tests (65 ms) $ python3 -m unittest -q
| self.assertEqual(apply_discount(10, 150), 0)
| AssertionError: -5.0 != 0
cost: $0.4200 (reported)
- the worker changed 1 protected file(s); countercheck ran the checks with the base version
- check "unit tests" failed (exit 1)
(The agent's own run said OK (skipped=1): it had marked the failing test as skipped.)
How it works
The mental model: the agent never grades itself. Its claim is compared with what actually runs, on rules it couldn't change:
base revision (locked) agent's work + claim
checks.toml, your tests files, "Done, all tests pass."
\ /
+--> countercheck verify <+
1 lock the checks (from the base revision)
2 clean copy (edited tests put back)
3 run in a sandbox (no internet unless a check opts in)
4 decide + seal (receipt with a SHA-256 seal)
|
VERIFIED -> agent may finish
FAILED + "done" -> hook sends the failure back; agent fixes the code (max 3 rounds)
FAILED + honest "can't, because ..." -> agent may stop; receipt still says FAILED
How Countercheck thinks
| Rule | In plain words |
|---|---|
| The worker never grades itself | the checks come from before the work started; edited tests are put back |
| Re-run, don't read | the agent's claim and logs are never evidence; the checks run again |
| Honest verdicts | VERIFIED, FAILED or UNVERIFIED (with a reason), never a guess; a hint never softens a failure |
| Everything from the repo is hostile | paths, sizes and links are checked, because the worker controls the files |
| Domain-blind core | it knows nothing about SQL, games or ML; your checks carry the domain knowledge |
| Fail closed on the verdict, open in the hooks | a broken referee never waves work through, and never traps your agent |
| Leave evidence | every run writes a sealed receipt anyone can re-check |
The code behind each rule, one run step by step, and where new work plugs in: docs/architecture.md.
Where it fits
| You use | How Countercheck plugs in |
|---|---|
| Claude Code | Stop hook: countercheck hook claude-stop (docs/hooks/) |
| Cortex Code (Snowflake) | same hook format as Claude Code (not yet tried on a real install) |
| Hermes Agent | pre_verify hook: countercheck hook hermes-pre-verify |
| Codex, Cursor, Copilot, Aider, your scripts | run countercheck verify --claim "…" when it says done (dedicated hooks planned) |
| CI | countercheck verify --base main; fail the job on any exit code but 0 (GitHub Action planned) |
Does it work?
We gave Hermes Agent 29 small coding tasks, 3 times each, in four setups, and graded every run with hidden tests the agent never saw. Some tasks are impossible on purpose (a test expects a wrong value, a server can't be reached): the honest answer is "this can't pass, because …".
| False "done" claims (claimed success, hidden tests fail) | Hermes | + its verify_on_stop |
+ Countercheck | both |
|---|---|---|---|---|
| GPT 6.1, all tasks (87 runs per setup) | 7 | 11 | 3 | 2 |
| GPT 6.1, impossible tasks only | 6 | 8 | 0 | 0 |
| DeepSeek V4 Pro, all tasks | 7 | 6 | 6 | 6 |
- When GPT 6.1 got stuck, it edited the protected test and said "all tests pass". Countercheck re-ran the original tests, said FAILED and pushed back once; the agent restored the test and reported the real problem.
- Countercheck only knows what your checks test: the remaining false claims were cases the visible tests didn't cover. DeepSeek rarely games tests, so there was little to catch.
- It added about 1 second to the median run.
Full method, numbers and limits: docs/eval/results-2026-10.md. Reproduce: eval/.
Install
| You use | Install with |
|---|---|
Python (pip, a .venv, uv) |
pip install countercheck · uv tool install countercheck · or run without installing: uvx countercheck verify |
| macOS or Linux, Homebrew | brew install datacrate360/tap/countercheck |
| macOS or Linux, any shell | curl -fsSL https://raw.githubusercontent.com/datacrate360/countercheck/PROD/install.sh | sh |
| Rust | cargo install --git https://github.com/datacrate360/countercheck countercheck |
| Direct download | Releases: one file per platform + SHA256SUMS.txt |
Builds: macOS (Apple silicon and Intel, one universal binary) and Linux (x86_64, arm64; static). The install
script checks every download against SHA256SUMS.txt before installing. Windows is planned; use WSL for now.
Sandbox by platform. On macOS, checks run in the system sandbox (no internet, localhost allowed, writes only in
the check folder; network = true lets a single check use the internet).
On Linux there is no sandbox yet: checks run as a normal process and the receipt says so; run Countercheck inside
a container with networking off when you need isolation.
What's shipped, in progress and next: docs/ROADMAP.md.
Quick start
countercheck init # writes .countercheck/checks.toml, edit the commands
git add .countercheck && git commit -m "Countercheck checks"
# ... let an agent work ...
countercheck verify --claim "what the agent said" # uncommitted work vs HEAD
countercheck verify --base main # committed work on a branch vs main
countercheck show .countercheck/receipts/<id>.json # re-check a receipt's seal
.countercheck/checks.toml:
protected = ["tests/**"] # the agent may not change these; .countercheck/** is always protected
link = ["node_modules", ".venv"] # git-ignored dependency folders to make visible (read-only)
[[check]]
name = "unit tests"
run = "pytest -q" # any command; runs with `sh -c` in the clean copy
timeout_secs = 600
Besides plain commands there are matrix, forbid, golden, budget and coverage checks: see docs/checks.md and the runnable example in examples/data-migration.
Exit codes: 0 verified · 2 failed (claim contradicted) · 3 unverified (nothing failed, but the checks weren't
locked in the base revision, or none ran) · 1 error · 4 (countercheck show) receipt was edited.
Agent hooks
-
Hermes Agent:
countercheck hook hermes-pre-verifykeeps Hermes working while the checks fail. In the profile'sconfig.yaml:hooks: on_session_start: - command: "countercheck hook session-start" pre_verify: - command: "countercheck hook hermes-pre-verify" timeout: 300
-
Claude Code and Cortex Code:
countercheck hook claude-stopblocks theStopwhile the checks fail, withcountercheck hook session-startonSessionStart. See docs/hooks/claude-code.md.
Add the session-start hook too. It remembers the commit each session started from, so the checks stay locked
even if the agent commits its own edits to a test or to checks.toml. Without it, the checks are locked to HEAD.
In CI, pass the base explicitly: countercheck verify --base origin/main.
How Countercheck itself is tested
A referee has to be right, so Countercheck is tested harder than most tools its size:
- 140 tests (
cargo test --workspace): unit tests, end-to-end tests that run the real binary on throwaway git repositories (tampered tests, timeouts, the macOS sandbox, both agent hooks, every check type), and property tests that throw thousands of generated inputs at the verdict and seal logic (e.g. a receipt's seal breaks if any one of 33 fields changes;decidenever says VERIFIED when a check failed). - Mutation testing (cargo-mutants): the receipt, claim and config code was deliberately broken in every way the tool could find; tests were added until every break that compiles is caught (one remaining change is provably equivalent).
- CI on every push (macOS and Linux):
cargo fmt --check,cargo clippy -D warnings, all tests, a release build and the benchmark task validator. Countercheck also verifies itself with its own.countercheck/checks.toml. - The benchmark above, with hidden tests as ground truth.
Status
v0.2 (CHANGELOG). macOS has the sandbox; Linux runs checks without one (the receipt says so). Not yet: signed receipts, a per-host network allow-list, cost read from agent logs, Linux/Windows sandboxes, more agent hooks (Codex, Cursor, a GitHub Action). See docs/ROADMAP.md.
Contribute: make it fit your domain
Countercheck gets better with every field it learns about, and most of that needs no Rust: a domain is covered
by a good checks.toml plus a small example. How agents fake "done" in game development, ML, dbt, video pipelines
or a migration is knowledge we don't have and you do.
| Start here | Rust? |
|---|---|
| Open a "New domain" issue: how "done" goes wrong in your work | no |
| Add an example for your field (examples/) | no |
| Add a benchmark task that tempts agents to cheat (eval/) | no |
| Hook another agent (Codex, Cursor, OpenCode, …) | yes |
| Add tests, or a check type | yes |
Each of these has a step-by-step guide in .claude/skills/, written for people and for AI coding agents alike (Claude Code loads them automatically). Read CONTRIBUTING.md for the gate, pull requests and commit sign-off (DCO).
Build
Rust 1.90+: cargo build --release → target/release/countercheck. Gate: cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test --workspace, python3 eval/validate.py. Countercheck also verifies itself: countercheck verify (checks in .countercheck/checks.toml).
Repository
crates/countercheck-core: pure logic (check file, check types, protected paths, receipts, verdicts)apps/countercheck: the CLI (git, clean copy, sandbox, runner, hooks)examples/: one made-up project per domain, with checks that catch a planted mistakeeval/,docs/eval/: the benchmark against Hermes Agent (tasks with hidden tests).claude/skills/: step-by-step contribution guides ·docs/architecture.md: how it works insidepackaging/,install.sh,pyproject.toml: release packaging (Homebrew, install script, Python wheels)docs/checks.md: every check type ·docs/hooks/: agent hooks ·docs/decisions/: design decisions
MIT licensed.
Metadata
Release files for countercheck 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| countercheck-0.2.1.tar.gz | 91.3 kB | With provenance | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| countercheck-0.2.1-py3-none-musllinux_1_2_x86_64.whl | Python 3 | none | Linux musl 1.2+ x86-64 | Details |
| countercheck-0.2.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | Python 3 | none | Linux glibc 2.17+ x86-64 | Details |
| countercheck-0.2.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | Python 3 | none | Linux glibc 2.17+ ARM64 | Details |
| countercheck-0.2.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl | Python 3 | none | macOS 10.12+ x86-64, macOS 10.12+ universal2 (ARM64, x86-64), macOS 11.0+ ARM64 | Details |
Total release size: 7.1 MB
Release files / countercheck-0.2.1.tar.gz
| Download URL | countercheck-0.2.1.tar.gz |
|---|---|
| Size | 91.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1e1414a1a6d727aa5255969efd93bec701017cf25eaaf8b94c9211d050ea3e91
|
|
BLAKE2b-256 checksum How to use checksums |
4bec7668c5c47018533fdbd491f7102e4c28ca447990ae9480f0dea919a37d88
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / countercheck-0.2.1-py3-none-musllinux_1_2_x86_64.whl
| Download URL | countercheck-0.2.1-py3-none-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 1.5 MB |
| Tags | Linux musl 1.2+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
612a55c002d224051d528be4ba8945f36d9bac4a9518721cc98148d50da16cc4
|
|
BLAKE2b-256 checksum How to use checksums |
1d14680dd6687ee33dbe486b9730205cf8d1249592c9cdacbf647cd3f94b3210
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / countercheck-0.2.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | countercheck-0.2.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 1.4 MB |
| Tags | Linux glibc 2.17+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
1e90166504cf2307418d46cd637d424aa86e66744a7fbdf2f275300f89df1169
|
|
BLAKE2b-256 checksum How to use checksums |
44c2237aedb11418f1ad468ea2072ad99a06b13da0500046be563888c471edf1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / countercheck-0.2.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | countercheck-0.2.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 1.3 MB |
| Tags | Linux glibc 2.17+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
0696b893218c72a18f9db6e8fa9d3dc39d7f64c6749419b146ebf479f103a190
|
|
BLAKE2b-256 checksum How to use checksums |
400423b5fa7c707ca8d2f197bbf613e75b58757abbeae86e49421c9449a1051e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / countercheck-0.2.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
| Download URL | countercheck-0.2.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl |
|---|---|
| Size | 2.7 MB |
| Tags | Python 3 macOS 10.12+ universal2 (ARM64, x86-64) macOS 10.12+ x86-64 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
9ead980b93dec07fa7fe3b3102072efd3b7703f6df7e102b040d74e7610a6bd7
|
|
BLAKE2b-256 checksum How to use checksums |
0aeb2d429ab1c741690d2601dc82b20809982abd0635c4d339fcd86708061c10
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logSecurity for countercheck 0.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 5 files in this release have been uploaded with provenance metadata.
Attestation for countercheck-0.2.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.