Quick answer
When an agent resolves a merge conflict, do not trust a cleanly completed merge. The default behavior of most coding agents is to make the conflict disappear, not to decide which side is correct. A common failure mode is that the agent keeps both blocks of code, deletes the conflict markers, and reports that the merge is done. The file compiles, the markers are gone, and you now have duplicated or contradictory logic that only fails at runtime. Treat every auto-resolved conflict as unreviewed code and inspect the actual diff before you push.
What "keeping both sides" looks like
Here is a conflict an agent was asked to resolve. Two branches changed the same pricing helper: one introduced a discount cap, the other changed the rounding rule.
A careful human merges the two intents: apply the cap, then round to cents. An agent that has been told simply to "resolve the conflict" often produces this:
The markers are gone, so the merge is technically resolved. In non-strict mode JavaScript will not even complain about the duplicate declaration; the second function simply shadows the first and the discount cap silently never runs. In a language that rejects duplicate symbols you instead get a build failure, which is arguably luckier because it cannot be ignored.
Why agents default to this
An agent is trained to reduce the signal you gave it. You gave it a conflict, and the measurable goal is "no conflict markers remain." It does not have the product context to know that the cap and the rounding are two halves of the same business rule. Keeping both blocks is the lowest-risk edit from its point of view because it deletes nothing, and deletion is what usually gets an agent into trouble. The result is a green merge that quietly changes behavior.
| Resolution strategy | What happens | When it is safe |
|---|---|---|
| Keep both blocks | Duplicate or shadowed logic; one intent silently wins | Almost never, unless the two blocks are genuinely additive |
| Take HEAD or incoming wholesale | One branch's work is silently discarded | Only when you can confirm the other change is obsolete |
| Manually merge intents | Both rules composed in the correct order | This is the default for business logic |
| Rebuild the function from the spec | Single implementation that satisfies both constraints | When the conflict exposes a deeper design clash |
A review routine that catches it
You do not need to read every line of the merge. You need to look at the conflicted regions specifically, because those are the only places the agent made a judgment call.
- List the files that had conflicts rather than the full diff:
git diff --name-only --diff-filter=Ubefore the merge completes, or review the merge commit withgit show <merge> --cc. - For each conflicted hunk, state out loud what each branch intended. If you cannot name both intents, you cannot judge the resolution.
- Search for duplicated declarations in the resolved file: repeated function names, duplicate object keys, or two blocks that set the same variable.
- Run the code path that the conflict touched, not just the test suite. A shadowed function often passes unit tests that were written against the old single implementation.
If you want the agent to do the right thing in the first place, give it the rule instead of the generic instruction. "Resolve this conflict by composing both changes: apply the cap first, then round to two decimals. Do not keep two functions with the same name" produces a dramatically better result than "fix the merge."
When keeping both is actually correct
Not every conflict is a choice between alternatives. Sometimes the two branches added genuinely independent things to the same region, such as two new entries in a list or two separate import lines. Keeping both is correct there. The test is simple: ask whether the two blocks operate on the same state. If they both define or mutate the same symbol, they are competing and need a real decision. If they touch unrelated values, they are additive and the union is legitimate.
FAQ
Why did the build pass if the merge was wrong?
Because removing conflict markers is all it takes to make a file parse. Duplicate declarations in loose language modes may be legal, and runtime behavior like function shadowing is not something a type checker always flags. A passing build tells you the code is syntactically valid, not that the merge preserved both branches' intent.
Should I just tell the agent to always rebuild conflicted functions?
Rebuilding is good for business logic but risky when one side contains subtle work the agent might omit. A safer instruction is to name both intents and require them to appear exactly once in the result, then verify against the original hunks. Reserve a full rebuild for conflicts that reveal a genuine design problem.
How do I review a large merge without reading everything?
Focus on the conflicted hunks; non-conflicting changes were already merged deterministically and need only normal review. Use git show <merge> --cc to see only the combined diff for conflicted regions, and check each one for duplicate symbols and the correct ordering of operations.
Does a merge commit instead of a rebase make this safer?
The commit style does not change the underlying problem, which is the agent's judgment, not the graph shape. A merge commit does make the conflict resolution easier to inspect in one place, which is a practical advantage when you know an agent performed the resolution.
This is part of the The AI Coding Agent Guide — start there for the full picture of how agents fail after the code appears to work.
Top comments (0)