defract › blog

the last mile: why AI coding agents stop at PR open

2026-07-30 6 min

When an AI coding agent opens a pull request, there is a small moment of relief: the code is written, committed, pushed, the PR is open, and the task feels finished. Most tools treat it that way — the agent's job is scoping and implementation; everything after "PR opened" is your problem. That model made sense when agents were narrow tools. It breaks down as soon as you're running more than one agent in parallel, or trusting agents with tasks that take longer than a session.

PR open is not PR merged. And the gap between those two states carries more work than it looks like from the outside.

what the last mile actually costs

Consider what has to go right after an agent opens a PR:

  • CI runs. It may fail — not because the code is wrong, but because of a flaky test, a transient service dependency, a test that was already broken before the PR opened. Someone has to decide whether to rerun, fix, or skip.
  • The branch may fall behind main while the PR sits open. If branch protection requires an up-to-date branch before merging, this blocks the merge until a rebase happens.
  • A rebase that produces a conflict requires someone to resolve it. If the conflict was caused by another parallel agent's commit landing first, the order matters — resolving it wrong can silently revert the other agent's work.
  • Branch protection rules may require reviewer approvals before merge. The agent can't assign reviewers or nudge them; the task is now blocked on a human loop it can't close.
  • If the task is reopened (because the first attempt failed or was rolled back), the agent creates a new PR. Now you have two open PRs against the same base. Which one is canonical? Which one carries the review comments?

Each of these is a small piece of work. Together, across several parallel tasks, they add up to a context-switching overhead that follows you through the merge queue. Every PR that's stuck is a task you have to keep loaded in memory — "did that CI rerun pass?", "did the rebase go cleanly?", "which of these two PRs is the one I want?"

This is the last-mile tax on agent-assisted development: the time and attention that accumulates between "code written" and "code merged."

the three mechanics that close the gap

The problem decomposes into three distinct failure modes, each requiring a different solution.

state tracking: done vs. awaiting merge

An agent that marks a task "done" when it opens a PR is losing state. Done should mean the code is in main — not "the agent's work is complete." When the task is done but the PR isn't merged, the task should exist in a separate state that surfaces it as still requiring attention, without treating it as active implementation work.

This sounds obvious, but getting it right requires the orchestration layer to distinguish between the agent's completion event and the merge event, and to hold the task in the intermediate state until the branch is actually merged. Most lightweight workflows conflate these two events because they don't have a merge-state listener — you'd have to poll the GitHub API to know when the merge happened. Without that listener, you get a binary: either the agent's job is open, or it's "done."

blocker detection: knowing what's actually in the way

When a merge is blocked, the blocker matters. "CI failed" and "branch protection rejected the push" and "requires review" are three different problems requiring three different responses. Conflating them into "merge blocked" gives you no path forward.

Rebase loops are a specific case worth naming. When CI fails on a merge commit, some workflows retry the push without rebasing first — they keep pushing against a protected branch, getting rejected each time. The retry loop is not useful; the task is blocked on the rebase, and the rebase can't happen automatically if it would produce a conflict. Detecting this specific failure — rejected push, not a general CI failure — and stopping the retry loop is the correct behavior. The right next step is surfacing the reason, not spinning.

idempotent reopen: one PR per task

When a task is reopened after a failed or incomplete merge attempt, the agent should find and reuse the existing PR rather than opening a new one. This is an idempotency requirement: the same logical task should always have at most one open PR, regardless of how many times it's been opened, paused, and restarted.

Without this, you accumulate PR debt: orphaned open PRs that nobody owns, review comments split across duplicates, and a merge queue that doesn't reflect what's actually outstanding. At two or three agents working in parallel, this is a minor annoyance. At ten tasks in flight over a sprint, it's a management problem on its own.

where the agent workflow ends

For parallel agent workflows, these mechanics aren't optional hygiene — they're load-bearing. When multiple agents are submitting PRs against the same codebase, the merge queue is shared state. A stuck PR blocks downstream agents that depend on the same files; a duplicate PR creates a merge conflict when both are applied; a rebase loop on one task delays everything merging behind it.

The worktree workflow that lets agents work in parallel without file conflicts only holds as long as merges happen in an order that doesn't produce silent regressions. Getting implementation agents to open PRs is the easier part; closing those PRs cleanly, in order, with full context, is where the workflow has to hold end to end.

The logical endpoint of a gated agent lifecycle isn't "PR open" — it's "branch merged." Everything between those two states is part of the release process, and if the orchestration layer doesn't own it, you do.

what this looks like in practice

defract v1.3.0 extends the lifecycle to cover this segment. A new Pull requests view shows the live merge queue — every open PR with its branch, check state, and merge status. Tasks move into an "awaiting merge" state after the agent's work completes, and stay there until the branch actually lands in main. Common merge blockers clear automatically where they can: rebase-loop recovery, detection of branch-protection-rejected pushes (with the reason surfaced, not a silent retry), and PR deduplication on task reopen.

If you're running Claude Code tasks in parallel and the last mile is where your coordination overhead lives, defract treats that segment as part of the workflow rather than the part you handle manually after the agent stops.

defract is in open beta

a structured lifecycle for your parallel Claude Code agents — from scope through merge. free, no caps, no signup.