LuKul Concierge
One defect, three layers
Three independent checks misread correct refusals as the claims they denied. Patching phrasings never converged; fixing the matcher did.
A concierge that answers questions about a real business has to be able to say no. It has to decline to give out a home address, decline to promise a result, decline to confirm a booking it never checked. Those refusals are the most safety-critical sentences the system produces, and for one sprint they were the sentences it kept throwing away.
Three separate components were rejecting them, and each looked like its own bug. The sprint closed with one sentence that explained all three:
One defect appeared in three independent layers: a phrase inside a denial read as the claim it denies.
The same mistake, three times
The runtime validator rejected a correct refusal because the refusal named the thing it was refusing. To decline to share a home address, the reply had to say the words home address — and a scanner looking for private information found them and threw the reply out. The refusal was correct precisely because it named the boundary, and naming the boundary is what made it look like a breach.
The scoring oracle marked a coaching answer as a conflict because it carried a medical disclaimer. The disclaimer is the responsible thing to include. Its presence was read as the problem it exists to prevent.
The safety scanner read a promise that results are not guaranteed as a guarantee, and hard stopped an entire run. That one cost 25 journeys that were never measured — not failed, never run. The system's own safety layer was the thing that took the safety-critical evidence away.
Why patching it never converged
There was a fourth variant, and it is the one that explains why fixing this felt endless. The matcher required contiguous tokens, so a pattern written for a phrase with an article in it did not match the same phrase without one. Every alias that was patched revealed another phrasing, and the work looked like an infinite queue of near-misses.
It was not a queue. It was one defect in the matcher, wearing a different phrase each time. The queue only ended when matching allowed bounded gaps — that is, when the fix moved from the aliases to the thing that read them.
The part that makes it a finding rather than an anecdote
Correcting a scorer is dangerous work. A scorer you are allowed to edit until the numbers look right is not a scorer. So every correction had to be proved against something that could still fail:
Every oracle correction was proved by replaying stored transcripts with no provider calls, against a control that could fail
A second model was held as a control. If a correction to the oracle had made that control's score improve, the correction was measuring the oracle's generosity rather than the system's behaviour. It stayed where it was, with the same two rows failing, because those two replies really do not contain the fact being scored. The corrections were real.
That is mutation testing pointed at a scorer instead of at code, and it is the only reason the numbers from that sprint are worth quoting.