Docs

Install the plugin

Two commands in a Claude Code session.

/plugin marketplace add Grade-Inc/corund-plugin
/plugin install corund

It needs Python 3.9+ (the system python3 on macOS is enough, and no third-party packages), git, and a JS test runner on PATH. Jest and Vitest are what this version was built and measured against. The plugin is MIT-licensed, runs entirely on your machine, and uploads nothing.

What the hook checks

The new and changed tests on this change — nothing else. It reads your working tree against HEAD, takes the test files you touched, and runs only those. It does not run your suite, and it does not look at tests you did not change.

On stop, Corund builds a candidate commit from the dirty tree, reverts the non-test half against HEAD, and runs those tests on both trees. If a test passes with the change and passes without it, the stop is refused with two lines:

corund: these tests pass with your non-test changes reverted
— they do not test what you wrote.
Write a test that fails against the old code, then finish.

The plugin also denies git commit --no-verify and HUSKY=0: commit hooks cannot be skipped from inside the session.

Every invocation appends to ~/.corund-hook.log — what was detected, the verdict, and what was returned. A hook that did nothing and a hook that never ran are otherwise indistinguishable.

When it stays quiet

Silence is the normal case. Corund allows the stop, and prints nothing, when:

  • no test file changed against HEAD — the common case, and it costs nothing
  • only test files changed — there is no non-test diff to revert
  • the change touches no source file — reverting a README, a config file or a lockfile cannot make a test fail, so a test passing without it proves nothing about it
  • the tests do fail on the reverted tree — the change is covered
  • it could not measure: a missing runner, a timeout, a crash, a tree mid-rebase

That last one is deliberate. A check that blocks on its own blindness is an accusation without proof, and this tool exists to refuse exactly that.

Configuration

Environment variables, all optional:

  • CORUND_TEST_COMMAND — default npx jest
  • CORUND_RUNNER — default auto
  • CORUND_TEST_GLOBS — which paths count as tests
  • CORUND_TIMEOUT_MINUTES — default 5

Limits

Stated rather than footnoted.

It proves a test ran and fails on revert. It does not verify the test asserts the correct value. A change whose code and test are wrong in agreement is not decided.

It catches a new test that does not exercise new code. It does not catch a weakened test on an existing function.

On JavaScript, the block is green-on-revert only. A test that fails on the reverted tree by erroring — TypeError: discount is not a function, a failed import — is not a witness. Corund reports that as unproven and allows the stop. It does not treat a crash as proof that your test works.

On Jest, a failed assertion carries no frame, so Corund cannot confirm the red came from the test's own body rather than a helper or a setup file. The receipt says so when it applies.

Also a GitHub Action

The same red-on-revert engine runs on pull requests in your own CI, where it covers Python as well. It is a separate, open-source thing and it is not what this page is about — the plugin above needs none of it. If you want it on the PR too, the Action's own repository has the workflow snippet and its inputs.

Scope

Corund catches agents that take shortcuts to green. It does not defend against an attacker who already controls your test infrastructure -- that's a different problem, and the receipt names where that line is.

Security

The plugin runs locally and talks to nothing. It reads your working tree, makes a temporary index and a throwaway commit object, runs your test command in a temporary worktree, and deletes it. HEAD, your index and your stash are never touched, and no code, diff or test output leaves your machine.

The Action runs inside your own CI with the permissions your workflow grants it: checks: write, pull-requests: write, contents: read. Customer code is not retained past run scope — the diff, the reverted tree and the full test output exist only for the duration of the check run. The receipt itself is kept, and a finding can quote the single line that triggered it, up to 160 characters.

Sources

Every figure this site cites is pinned to a named external report, with report, publisher, date, and URL, in one place: /dossier#sources. A figure that cannot be pinned is dropped, not softened.