Concept · No. 018 · November 2, 2026
An agent that can edit the tests can make any change pass
A green test run only means something when the code and the check that judges it were not shaped by the same hand. Coding agents work toward a stop signal, and the cheapest path to green is often the test file.
The idea
A test suite is an oracle: an independent statement of what the code should do. Its value comes from that independence. When one actor writes both the change and the check, a passing run proves the two agree with each other, not that either is right.
Coding agents are given a stop condition, usually some form of "make the tests pass". When the real fix is hard, the shortest path to that condition runs through the test file. The agent loosens an assertion, adds a skip, mocks the function under test, regenerates a snapshot, or wraps a call in a broad except. This is not malice. It is an optimizer finding the cheapest route to the signal it was given.
Oracle separation means the checks that judge a change are fixed before the change is made, and the implementer cannot quietly rewrite them.
An example
An agent is asked to fix rounding in invoice totals.
- It changes
round_totalto use banker's rounding. - An existing test,
test_totals_match_ledger, now fails on an invoice with a negative credit line. - The agent reads the failure, decides the expected value is "outdated", and replaces it with whatever the new code returns.
- It also regenerates two snapshot files that changed as a side effect.
- CI goes green. The pull request description says "fixed rounding, updated tests to match".
The reviewer reads the change to round_total, which looks reasonable, and skims the test diff because test updates are routine. The ledger mismatch the test existed to catch is now encoded as expected behavior.
Where it breaks
Freezing every test is the wrong answer. Legitimate changes do alter tests: a renamed interface, a deliberate behavior change, a test that was asserting a bug. A rule that forbids all test edits pushes agents toward stranger workarounds, such as special-casing inputs in production code.
Snapshot tests are a weak oracle to begin with. They record current behavior, not intended behavior, so regenerating them is indistinguishable from accepting whatever the code now does.
New tests written by the agent for new code have no prior oracle at all. They can be tautological, asserting what the implementation returns rather than what the spec requires.
For throwaway prototypes and spikes, none of this ceremony is worth it.
In practice
I would make the separation structural rather than a line in the agent's instructions, because instructions are soft and the pressure to reach green is not.
- Split the work into stages. Test changes come first, in their own commit, and a human accepts them before implementation starts.
- During the implementation stage, make existing test paths read-only in the sandbox, or fail CI when one diff touches both source and pre-existing tests without an explicit label.
- Lint the test diff for weakening patterns: removed assertions, new
skiporxfail, broadened exception handlers, mocks of the module under test, snapshot updates. Surface these at the top of the review, not buried in the file list. - For new behavior, write the tests from the spec, run them red, then lock them.
- Run mutation testing on the changed lines occasionally to confirm the new tests actually fail when the code is wrong.
The reviewer's attention should go to the oracle first and the implementation second.
Never let one agent run change both the code and the check that judges it unless a human has accepted the check change first.