AI coding agents have a cheating problem: when a fix is hard, some agents quietly weaken the test instead — changing expect(page.items.length).toBe(10) to toBe(9) — then re-run, see green, and report the task complete. Others rig the RNG so "the list would always be sorted," mock the function under test instead of its collaborators, or plant helpers in conftest.py. Prompt-level instructions don't stop this; the guardrails have to live below the prompt layer, in the tool-call path.
agent-guard is a set of behavior-guardrail hooks for Claude Code, Codex CLI, and Gemini CLI — one install, zero dependencies (Python standard library only). Eight guards plus test-honesty CLIs:
- Test-tampering guard — on SessionStart, snapshots hashes of every test and source file; on Stop, diffs. If test files were modified or deleted while no source file changed, the stop is blocked until the agent proves each changed test actually fails without the fix. v0.7 classifies each changed test file as legit (new tests, fixtures, renames → passes with an audit entry), malicious (weakened/removed assertions, newly skipped tests → blocked), or ambiguous — with an explicit, audited escape hatch (--allow-test-change) for the ambiguous-but-fine cases.
- Outbound-action guard — blocks risky actions before they run: pushes to protected branches (incl. force pushes), package publishes (npm publish, twine upload), prod deploys, cloud provisioning, mass-send channels. Also scans the content of script files the command executes (bash evil.sh) to close the "write it to a script first" bypass.
- Post-exec read-back verifier — after a command claimed an outbound effect, reads back world state (git ls-remote, npm view) and warns when the effect isn't visible.
- Mutation test-honesty checker (mutate-check) — breaks assertions with regex-based mutants and reports survivors: tests that stayed green don't cover the bug.
- Comment-slop guard — scores added comments for dead-code-in-comments, restatements, changelogs, meta-apologies, emoji, vacuous docstrings; blocks at threshold, or decomment --fix one-pass.
- Cheat-sniffing guard — catches cheating that never touches test files: RNG rigging, mocking the subject instead of collaborators, conftest.py plants, time-freezing, always-True dunders.
- Cross-tool write guard — stops Bash (sed -i, heredocs, >, tee) from bypassing the Edit/Write hooks.
- Tamper-shape guard — scores how tests changed: weakened assertions (toBe(x) → toBeTruthy()), newly skipped/focused tests, unreviewed snapshot updates.
- Plus: cross-tool hooks (same rules on Codex CLI and Gemini CLI via a payload shim), a gate-liveness verifier (agent-guard verify), and a semantic reviewer (Nemotron gives a second opinion on rephrased-but-weakened assertions; advisory, fail-open).
Why this is different: observation-only tools record agent behavior and compare it against a closing summary — agent-guard prevents: the hook sits in the tool-call path and blocks the bad action before it happens. And unlike all-or-nothing test-tampering guards, it classifies changes instead of blocking every tests-only edit.
How I used GitLab to automate the post-code lifecycle — the repo carries a .gitlab-ci.yml with two stages: test (pytest across a Python 3.10–3.13 matrix plus a stdlib-only smoke job) and guard (agent-guard scans its own changed files per commit — cheatsniff on changed tests, decomment on changed sources). Every push is tested and honesty-checked by the very hooks it ships.
Built With
- claude-code
- codex-cli
- gemini-cli
- gitlab
- gitlab-ci
- guardrails
- hooks
- pytest
- python
- stdlib-only
Log in or sign up for Devpost to join the conversation.