# The merge train that landed 3 of 9: when automation outside the loop backfires

**Topic:** delivery engineering · **Published:** October 2026 · **Reading time:** 6 min

In October 2026, an AI agent team on a regulated fintech platform produced approved code changes faster than they could merge. I built a merge train to land them in batches. Four days after it became the only way to merge, I removed it. This article explains why it looked right, how it failed, and what replaced it.

## The problem the train had to solve

Each code change needed an independent reviewer agent's approval of its exact version, then a clean merge with the main branch, then a staging build. Builder agents worked in parallel, so the main branch moved all the time. A change that was approved in the morning often no longer merged cleanly by noon. It went back for a rebase, then back to review. One change was refused three times this way.

On one afternoon, 20 tickets were waiting in review. Nine had already merged and waited for a staging check. Seven more sat in four approved changes that kept going back for a rebase.

## The design

The idea was the standard one for a busy repository:

1. Collect every approved change from every agent lane.
2. Run one preflight (type checks and affected tests) on all of them merged together.
3. If the preflight passes, land them all in order.
4. If it fails, bisect: split the batch, find the change that broke it, drop it, and land the rest.
5. Run one staging build after each train.

One agent, the lead, owned every merge. Other lanes reported approved changes to the lead and stopped running their own merges. On paper, this cut the number of preflights and staging builds and ended the rebase loop.

## What happened

The preflight on the merged batch took about 72 minutes on our test machine. That number decided everything else, because each bisect round cost another 72 minutes.

On the first train-only day, three trains ran. Each one failed on a defect in a single change:

- one change failed a work-ownership check,
- one had a formatting error in a single changelog row,
- one had a wrong option in its ticket key check.

None of these was a real product bug. Each one stopped the whole train. For close to two hours nothing merged, while about 16 approved changes waited. A later train failed on one four-line comment.

A train also failed after its preflight passed. Two changes were stacked: the second one was built on top of the first. The train ran the second change first, so the first one's merge came out empty, and the train landed nothing.

Four days later, 171 of 175 open changes had an approval, and only 3 of the 9 trains had landed.

## Why it failed

A batch gate multiplies one change's defect across every change in the batch. When each change has a small chance of a small problem, a batch of twenty almost always has one. The bisect finds it, but each round costs a full preflight.

There was a second cause. The train moved a decision out of the loop without making it smaller. Before, each lane merged its own change, and a defect cost one change. With the train, one defect cost every lane's work for an hour or more.

I fixed parts of it on the way: a per-change entry check at approval time, a hygiene check for comments and ticket keys, and automatic ordering of stacked changes. Each fix helped. The batch still landed only 3 of 9 trains.

## What replaced it

We went back to per-change merges, inside the test schedule we already had:

1. **Each change merges on its own** after an exact-version approval and a clean merge with main. The oldest approval goes first.
2. **A staging build follows each merge**, and a two-hour deploy gate runs the broad checks once on the current main branch. A red result is bisected there, after merge, where it blocks nobody's approval.
3. **The full test suite runs nightly.** A red result becomes a ticket in the next batch, not a hold on approved work.

The train also taught us which cheap checks matter most: formatting, ticket keys, comment hygiene and a clean merge. They belong at the moment a change gets its approval, not in a shared batch.

## The general lesson

When you take a human out of a step, the blast radius of that step's failure should get smaller, not larger. A gate that one agent owns and that all work passes through is a single point of failure, even when each part of it works.

## What I would tell a team running agents

1. **Measure the gate time before you batch.** A 72-minute preflight with bisect does not fit a team that approves several changes per hour.
2. **Run cheap checks per change, at approval.** Formatting and naming errors should never reach a shared batch.
3. **Prefer a defect that costs one change.** Check after merge on a schedule, and fix forward.
4. **Set a removal rule when you build it.** I removed the train because the numbers said so: 3 of 9 trains landed. Decide in advance which number would make you remove it.

More on how the rest of the team runs is in [In the loop, near the loop, out of the loop](/writing/near-the-loop/).

Email [hello@jeffkent.dev](mailto:hello@jeffkent.dev) to talk about delivery for an agent team.
