A fix is not applied until it is attacked where it was never made
We fixed a defect, wrote down how to stop it coming back, and it came back one phase later in the file next door. Then we wrote down a better rule, and it came back again.
The defect had a name by then: validate one read, compare another. A function reads a value once to check it, reads it a second time to use it, and the two reads can disagree. The check passes on one version and the code acts on a different one. It was closed in one module of our evolve engine during one phase, and left open in its twin, a module doing the same job on a different record, until an attacker walked three hops through an accessor to reach it in the next phase (retro log, 2026-08-04).
That was the second time in two days. The first had produced a written prevention: grep for the pattern, not the file. It did not take.
Advice fails at the moment it is needed
The written rule was correct. Nobody disputed it. It failed for the same reason every rule of the form “remember to also check” fails: it asks the person fixing a bug to widen their attention at the exact moment their attention is narrowest. You fix the file in front of you, you run its tests, they go green, and the twin is in a file you have no reason to open.
Development driven by tests has this blind spot built in. The test you write for a fix proves the fix, in the place you made it. It says nothing about the four other places that share the shape, because you did not write tests for those. You did not know they were there.
Move the control into the mechanism
The retro’s conclusion was that advisory had now failed twice, so the control had to stop depending on anyone remembering it (retro log, 2026-08-04).
A lane now keeps a running list of defects it has already fixed. One line each, in a fixed format: the defect, where it was fixed, and the pattern to check elsewhere (an example list). The fresh adversarial agent that attacks each change gets that list in its prompt, with one instruction: check each line in every other file you are shown, not only the file it was fixed in.
The rule we wrote alongside it is the one we actually use: a fix is not applied until it has been attacked somewhere it was never made (build rules).
Why the attacker and not the author
The attacker is the right holder for this list because it has no stake in the fix being finished. The author of a fix wants to close the ticket. The attacker is rewarded for finding something, and a list of known shapes is a list of cheap places to look.
It also turns the list into an asset that grows. Each attack pass appends the holes it found. A defect fixed in one phase becomes a probe for every later phase, and the probes accumulate faster than any one person’s memory of what went wrong last month.
What it does not fix
This catches the twins of defects we already know about. It does nothing for a defect class nobody has met yet, and a list that only grows will eventually be long enough that an attacker skims it. We have not hit that limit. When we do, the list needs grouping by shape rather than by date, and that is a problem we would rather have than the one it replaced.
What we would tell you to check
Pick the last bug you fixed and write its shape in one line, without the file name. Then search the codebase for the shape. If you find a twin, the fix was a patch. If the search is something only you would think to run, put the line where your next reviewer will be made to read it.