Most teams I've seen converge on a single path to production: feature branch → main, gated on tests. Some add a staging branch in between. Both work; the trade-offs are about how much pre-prod testing you want to gate on.
We have two paths to main. The slow path goes feature → staging → main, with full Playwright matrix on staging. The fast path goes feature → main directly, for docs/*, chore/*, content/* branches that don't touch runtime code. Both paths are auto-merged on green CI; humans don't manually merge.
This is great. Until it isn't.
The "isn't" arrived three months in, when both an engineering agent (working on the slow path) and a content agent (working on the fast path) edited the same package.json on the same afternoon. The fast path landed in main first. The slow path's auto-PR went DIRTY against main. Sentinel (our auto-rebase agent) tried to rebase it; the rebase had a real conflict; Sentinel gave up and left it for a human; we noticed three hours later when CI was suspiciously quiet.
Two paths to main + no automatic rendezvous = DIRTY auto-PRs on the same file. This piece is the workflow we shipped to fix it, plus the one cultural rule that keeps it from regressing.
The shape of the problem
When you have two paths to main, both depositing changes asynchronously, you have a synchronization problem. The two paths see different versions of main as they each progress through their pipeline:
- Fast path opens at
main@abc, lands atmain@def(now main isdef). - Slow path opens at
main@abc, runs through staging, prepares to land atmain@def. - Slow path's promotion PR was constructed against
main@abc. It's now stale.
If the changes don't conflict, the slow path's promotion PR auto-merges fine — Git is smart enough to fast-forward or do a clean three-way merge. But if the changes conflict (both edited the same file), the auto-merge fails. Now you have a stuck PR.
In a small team, this is rare. In a team running multiple agents on both paths, this becomes routine. Agents don't pause on each other; they don't even know about each other. The conflict rate scales linearly with the number of active branches.
The workflow we shipped
The fix has two parts: an auto-backmerge from main → staging on every main push, and a cultural rule that the slow path always sees a fresh staging.
Part 1: Auto-backmerge
Every time main moves (i.e., the fast path lands a change), GitHub Actions opens a PR from main → staging. The PR contains exactly the diff between staging and main — the changes the fast path landed that staging hasn't seen yet.
The auto-backmerge PR is itself auto-merged on green CI. So the sequence is:
- Fast path lands feature-x in main.
- Workflow opens auto-backmerge PR
main → stagingcontaining feature-x. - CI runs on the PR.
- Auto-merge merges PR. Staging now has feature-x.
- Slow path's feature-y PR (which is being prepared) sees the new staging with feature-x in it. The slow path's promotion to main will be a clean fast-forward because main and staging are in sync.
The implementation is straightforward — .github/workflows/sentinel-auto-backmerge.yml triggers on push: branches: [main], runs gh pr create --base staging --head main --title "Backmerge main → staging", and lets the standard auto-merge worflow handle the rest.
The trick is making it robust:
- Skip if there's nothing to backmerge. If staging is already at main, don't open a PR.
- Skip if a backmerge PR is already open. Don't open a duplicate.
- Don't trigger on the backmerge merge. The merge into staging shouldn't trigger another backmerge attempt; recursion stops.
Part 2: Cultural rule — rebase before push
The workflow handles the automatic synchronization. The cultural rule covers the human (or agent) who's about to push:
Always
git pull --rebase origin stagingbefore pushing your feature branch.
This is one line and it eliminates 90% of stale-base conflicts. If your feature branch is ahead of an old main / staging, your PR opens behind the line and immediately needs Sentinel to rebase it. If you've rebased before pushing, the PR opens at the head and goes through cleanly.
The cultural rule isn't enforced by tooling; it's enforced by the team noticing when someone's PR opens BEHIND. If you skip the rebase three times in a week, someone says something. The norm holds because the cost of skipping (Sentinel doing your rebase, sometimes failing, sometimes leaving CI red) is visible.
What this fixed
A few patterns disappeared:
Stuck DIRTY PRs. The slow path's promotion PR is no longer stale by default — staging has been kept current via auto-backmerge. The promotion is a fast-forward in the common case.
Conflicting auto-PRs from agents. When two agents both push to the same file on different paths, the auto-backmerge brings the fast-path's changes into staging before the slow-path's promotion. The conflict is resolved at the point where it's expected (during the backmerge, not during the promotion), and the conflict resolution is automatic-or-human-flagged, not a silent stick.
Sentinel's rebase load dropped. Sentinel was rebasing 60-70% of stuck PRs. After auto-backmerge + the cultural rule, Sentinel's rebase load is closer to 10-15%. The remaining 10-15% is the genuine "two agents wrote the same file" cases that no automation can solve cleanly.
What this does not fix
Auto-backmerge handles the common case. It does not handle:
Genuine conflict between fast-path and slow-path changes. If the fast path edits line 47 of package.json to "1.2.4" and the slow path edits the same line to "1.3.0", auto-backmerge opens a PR with the conflict, and a human has to resolve. We can't auto-resolve semantic conflicts.
Large rebases. When a feature branch is way behind, even auto-backmerge can't rescue it. The branch has to be rebased by hand or merged with conflict resolution. The cultural rule (rebase before push) prevents this from happening in steady state.
Race conditions in the workflow itself. In rare cases, two main pushes happen within the same minute, and the auto-backmerge workflow runs into itself. We've seen this once. The recovery is manual: trigger a fresh backmerge.
The cultural lesson
The thing that took the longest to internalize was that two paths to main is a deliberate trade-off, not a defect we should remove. The fast path is fast for a reason — it lets docs and content updates ship in 4 minutes instead of 20. Removing the fast path to "simplify" the synchronization problem would lose the speed.
The right move was to keep the two paths and build the synchronization. Auto-backmerge is the substrate-level fix; the cultural rule is the friction reducer. Together, they make the trade-off livable.
What this composes with
Auto-backmerge is one part of a small set of agentic-CI patterns we've shipped:
- Sentinel auto-rebase — when a PR goes BEHIND for any reason, Sentinel rebases it on the next main push.
- Stale-PR sweeper — PRs older than 4 hours get labeled
stale; older than 28 hours get auto-closed. - prod-pr-guard —
staging → mainpromotion PRs are guarded; the head ref must bestagingand CI must be green.
Each of these is a small mechanical safeguard. Together they mean the agent fleet can run continuously without the team having to babysit CI. We covered the broader pattern of agent-aware CI in The smallest useful MCP tool and adjacent posts.
Cross-links
- AI-agent-first primitives — substrate-level fixes generalize this pattern.
- The smallest useful MCP tool — how we think about the surface of agent-callable functions.
FAQ
Why have two paths to main?
The slow path (feature → staging → main) gates on full Playwright + Lighthouse matrix and takes 15-20 min. The fast path (feature → main directly) is for docs/*, chore/*, content/* branches that don't touch runtime code. The fast path lets non-code updates ship in 4 minutes. Removing it would lose ~80% of our agent-shipping speed.
What's a backmerge?
A merge from a faster-moving branch (main, in our case) back into a slower one (staging). Backmerges keep the slower branch in sync with changes that landed via the fast path, so the slow path's promotion PRs stay non-stale.
What's the auto-backmerge workflow?
A GitHub Actions workflow that runs on every push to main. It checks if staging is behind, opens a main → staging PR if so, and lets the standard auto-merge flow land it. The workflow is at .github/workflows/sentinel-auto-backmerge.yml.
What if the backmerge has a conflict?
The auto-merge fails. The PR sits open with a conflict marker. A human (or Sentinel) resolves. This happens for genuine code conflicts that can't be auto-resolved — we can't make those go away, but we can make them visible at the right step (during backmerge, not during promotion).
What's the cultural rule about rebasing?
git pull --rebase origin staging before pushing a feature branch. This is one line and prevents 90% of "PR opens BEHIND" failures. It isn't enforced by tooling; the team enforces it by noticing when it's skipped.
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "Backmerges or bust",
"description": "Two paths to main, no automatic rendezvous, agents on both sides editing the same files. The DIRTY auto-PRs that ate a sprint, and the workflow + cultural rule that stopped them.",
"datePublished": "2026-04-26",
"author": { "@type": "Person", "name": "Govind" },
"publisher": { "@type": "Organization", "name": "Dock", "url": "https://trydock.ai" },
"image": "https://trydock.ai/blog-mockups/style-d-dreamscape/backmerges-or-bust.webp",
"mainEntityOfPage": "https://trydock.ai/blog/backmerges-or-bust"
}