A stop hook for coding agents

Slopbouncer sends a coding agent back when it weakens a test.

A coding agent's job is to make the checks pass. One way is to weaken the check. When an agent is about to finish and has weakened an existing test, Slopbouncer sends it back once: put the test back and make the code pass it, or leave the change and tell the person which behaviour changed.

It reads code and never runs it, so it takes a fraction of a second.

View on GitHub Status: in development. Reads Ruby tests first.

Setting up your agent

$ gem install slopbouncer
$ slopbouncer install claude-code

It needs Ruby 3.2 or later and git. Slopbouncer is most use when the agent that made the change hears about it. The agent knows why it changed a test, and it can put the test back. One command sets an agent up:

Claude Code is the one it has been run under so far. The Cursor, Gemini CLI and Codex hooks are written to those agents' published hook contracts and have not yet been run inside the agents themselves.

What the agent is told

Slopbouncer: this change weakens 2 existing tests.

test/checkout_test.rb: Checkout lets a guest pay: assertion_removed: assert_equal 'paid', order.status
test/refund_test.rb: Refund refunds part of an order: expected_value_changed: 100 → 90

For each one, do one of two things before you finish:
1. Put the test back as it was, and make the code pass it.
2. Leave the change, and tell the person which behaviour changed and why the test had to change with it.

What it flags

What it leaves alone

Running it yourself and in a pipeline

An agent set on a passing build can still get round a hook, so keep running the plain command where a person will see it. In a pipeline, or before a push, it is the backstop to the hook.

$ slopbouncer main..HEAD

test/basket_test.rb
  Basket applies a voucher
    skip_added  marked to be skipped

test/checkout_test.rb
  Checkout lets a guest pay
    assertion_removed  assert_equal "paid", order.status

test/refund_test.rb
  Refund refunds part of an order
    expected_value_changed  100 → 90

3 tests got weaker in 3 files

Its limits

Slopbouncer catches tests that are visibly weaker. A test that looks thorough and checks nothing needs mutation testing, which is slower and runs the code. Use both: Slopbouncer on every change, mutation testing behind it.

It flags changes for a person to review. It does not block a merge unless you ask it to.