The agent gate
Verification only protects a change if somebody runs it. When an AI agent writes the code, the agent decides whether to run it — and the agent is exactly who benefits from skipping it. The agent gate hands that decision to the harness instead: it registers verification as a Claude Code Stop hook, so the agent cannot end a turn while the release gate is blocked.
Install
npm install --save-dev --save-exact marucheck@0.4.0
npx --no-install maru init
npx --no-install maru hook installThat merges one entry into .claude/settings.json and leaves every other setting and hook untouched. Running it twice is a no-op, and maru hook uninstall removes only the MaruCheck entry.
What the agent receives
When the agent tries to finish, Claude Code runs maru hook run. It verifies the working tree and, if the gate is blocked, refuses the stop and returns the verdict as the reason:
MaruCheck gate: BLOCKED.
Risk critical (91). 2 requirement(s) failed, 0 inconclusive.
Blocking findings:
- usage-quota#QUOTA-001: QUOTA-001 verification failed
expected: Free plan users may perform at most 10 generations per calendar month.
actual: Received: 1000
reproduce: maru verify --diff
Change the code so the approved contract holds. Do not edit or re-approve the
contract to make this pass; if the contract itself is wrong, stop and tell the
human to run maru drift propose.The closing instruction is deliberate. The cheapest way to make a failing contract pass is to edit the contract, so the gate says out loud that this is not an available move. Approval stays a human action with a recorded owner and version hash.
Commands
maru hook install- Register the Stop hook in .claude/settings.json. Idempotent.
maru hook uninstall- Remove the MaruCheck Stop hook and leave every other hook in place.
maru hook run- The hook entrypoint. Reads the payload on stdin and exits 2 to block a turn, with the reason on stderr.
Cost and scope
The gate runs maru verify --diff at the end of a turn, so it costs whatever the selected tests cost — and verification selects only the tests linked to the changed requirements, not the whole suite. Everything still runs locally: the hook makes no network request, and no source code leaves the machine.