# Why Coding Agents Write Narrow Fixes (And Why Logs Won't Save Them)

> This post is about why coding agents write narrow patches that only fix one crash trace, and how replaying 56 variants catches those patches in milliseconds.

Koustav Chowdhury · Founder · October 16, 2026 · Engineering

Before I introduce variant verification, let me tell you who I am and why we built this. I have worked in financial industry for about half-a-decade, and when I recently build HFT infrastructure from scratch, we never merged a bug fix without replaying recorded traffic variations against it first. While building that, I found certain patterns that are helpful for me, and also helpful for agents, to debug faster, to reproduce the issue exactly, and to verify the fixes.

[Variant verification](https://antithesis.com/blog/2026/time-traveling-agents/)[†](https://antithesis.com/blog/2025/findability/) is about replaying the event variations which govern transitions in your state machine. What is a narrow fix, you may ask? A narrow fix is a software's incomplete patch which determines whether only the recorded crash passes while sibling events still fail.

Why does a variant check help to catch narrow fixes faster? Whenever we are stuck reviewing a bug fix pull request by looking at unit tests that the agent wrote itself, a simple variant check lays out in bare bones the events that took place, which caused the software to fail on sibling inputs. Let's say you have a bank account. Your bank account is a summation of all the credits and debits of your money. Now imagine one day you go up to your bank, and it says that you have 10 dollars. When you checked last night, you had 100k. So you will demand the transaction ledger for this, right? You are not ready to accept a green unit test when a deposit patch still leaves transfers broken.

```json
{"id": "evt-008", "type": "deposit", "account": "bob", "amount": null}
```

```go
if event.Type == "deposit" && event.Amount == nil {
    env.Emit("ledger.rejections", Rejection{Event: event.ID, Reason: "missing amount"})
    return nil
}
```

![Why single-trace reproduction lets coding agents ship narrow fixes. The top lane shows that without variant verification, an Incident at seq 31 (deposit: amount=null) leads the agent to write a narrow guard (if type=="deposit" && nil), which merges and crashes in production tomorrow on transfer: amount=null. The bottom lane shows kavach mcp running 56 incident perturbations and 48 neighbour regressions in 5.9 ms, rejecting the narrow patch with variant_failed(6)@22 (deposit to transfer), forcing the agent to guard all events until verdict: fixed (47/47 variants pass).](https://www.thekavachlabs.com/blog/why-coding-agents-write-narrow-fixes/d-agent-verdict-loop.diagram.svg "Single-trace reproduction rewards narrow patches; variant perturbation rejects them")

A variant verifier is kind of like that. It has a transactional ledger which keeps account of all the state changes, all the event variations that lead up to the same crash. In a way, it makes the generality of a bug fix auditable.

Now, since I have worked in the finance industry, I see its applications everywhere. It helps to catch narrow patches, to actually see what variations break the fix, and so on.

```callout
invariant | What counts as a verified fix in Kavach (SPEC.md §6.1)
A candidate build only receives the verdict `fixed` when:
1. The recorded failure is gone and all declared invariants hold after every step.
2. Every step *before* the failing input produces the exact outputs production saw (no collateral behaviour changes).
3. The build passes at least 10 **reproducing incident variants**—perturbed copies of the journal on which the old build still fails the same way.
4. The build matches the old build's outputs across up to 48 **neighbour regression variants**—mutations of earlier valid inputs that resemble the failing one.
```

```interactive
/interactive/replay-lab.html?scene=variants
Interactive fix verification across the ledger crash variants: toggle between the narrow deposit-only patch and the general patch.
```

## Why deterministic variants matter

Starting from a snapshot, if you keep replaying the variations that are in the journal, your fix should hold on every step. That's the ultimate goal for [Kavach](https://github.com/kavachlabs/kavach/blob/main/AGENTS.md)[†](https://github.com/kavachlabs/kavach/blob/main/BENCHMARKS.md). What makes it challenging are certain shortcuts sprinkled through agent patches. Shortcuts come from hardcoding an event ID. They can come from guarding only one branch. They can come from the fact that the agent is rejecting valid inputs too.

Having the ability to replay 56 variations deterministically in milliseconds is one of the key guarantees of our verifier, and that is what makes the agent loop so reliable.

```callout
spike-pit | Why incident variants alone aren't enough: the "drop everything" cheat
What if an agent fixes a crash by rejecting *every* deposit—valid or invalid? Every incident variant will stop crashing! That is why `kavach diff` also generates up to 48 **regression variants** (`--regressions 48`): it takes earlier inputs in the journal that succeeded on the old build and resemble the failing input, perturbs them, and verifies that `--new` produces the exact same outputs as `--old`. If an over-broad fix breaks valid traffic, `kavach diff` rejects it with `regressed(K)@N`.
```

## Challenges of verifying agent bug fixes

These challenges all come down to how agents optimize for a passing test. Verifying an agent's patch has several such traps.

**Guarding only one branch.** Can a single replay catch a narrow patch? Catch every missing guard? How does it know what happens on a transfer? Which other branches share the same bug? If these variations aren't tested, the same bug will crash production the next day on a different event type. As a company, Kavach Labs is going to tackle these challenges so that narrow fixes get rejected automatically.

```markdown
# Ten Planted Bugs Benchmark (bench/bugs.go) — go test ./bench -run TestBenchmark -v

| # | Planted Bug | Tempting Narrow Fix | Verdict |
| - | - | - | - |
| 1 | Nil pointer on missing email | Guard signup only, leave profile_update unguarded | variant_failed |
| 2 | Slice index on empty items | Guard checkout only, leave quote unguarded | variant_failed |
| 3 | Division by zero on metric window | Guard only the recorded metric name | variant_failed |
| 4 | Write to uninitialized nil map | Init map only when step 0 is init | variant_failed |
| 5 | Type assertion panic on string qty | Validate qty type but forget price | variant_failed |
| 6 | Group booking exceeds capacity | Check capacity on reserve but not extend | variant_failed |
| 7 | Update delivered before create | Buffer only for the recorded customer ID | variant_failed |
| 8 | Redelivered charge applied twice | Deduplicate only back-to-back duplicates | variant_failed |
| 9 | Leap-day array index on day 366 | Special-case the exact recorded timestamp | variant_failed |
| 10 | Modulo zero on empty lottery pool | Refill pool only at the recorded draw count | variant_failed |
```

**Variations that don't reproduce the bug.** Software often behaves differently when you change a field value. Say you design a check that replaces a missing amount with 5,000 dollars. How do you score that test without running it on the old build first? With 5,000 dollars the old build works fine, so on replay you are testing a happy path and not the crash.

**Patches that break valid traffic.** One system requirement for a good fix is that earlier valid events should produce the exact same outputs as before. Most careful human patches follow this principle, but quite a few chunks of agent code still don't. How do you catch those? These are the uncomfortable parts of the system that we want to tackle.

```bash
claude mcp add kavach -- kavach mcp
npx skills add kavachlabs/kavach
```

We're building Kavach Labs to tackle this problem head-on. We love deterministic verification as an architectural concept, and we want agentic software to be written and deployed so that it's easy to capture a bug, replay it, fix the code, and deploy the fix with more confidence.

## References

1. [Your agent can now ship reliable distributed systems](https://antithesis.com/blog/2026/time-traveling-agents/) (Article, antithesis.com)
2. [Did you get lucky or unlucky?](https://antithesis.com/blog/2025/findability/) (Article, antithesis.com)
3. [Kavach Agent Loop & Verdict Specification (AGENTS.md)](https://github.com/kavachlabs/kavach/blob/main/AGENTS.md) (Docs, github.com)
4. [Kavach Ten Planted Bugs Benchmark](https://github.com/kavachlabs/kavach/blob/main/BENCHMARKS.md) (Code, github.com)

Source: https://www.thekavachlabs.com/blog/why-coding-agents-write-narrow-fixes
