Autonomy-First Merge-Back Conflict Handling
Try to resolve the merge conflict without a human, and ask only when that fails.
Every Gantry run ends by merging its finished work back into the main branch, and sometimes that merge hits a conflict — two lines of work that disagree about the same code. Gantry already had a resolver agent that could untangle such conflicts inside a throwaway copy of the tree without touching the real main branch. But whether that resolver even got a chance depended on how the run was being watched. A run with an attached console stopped the moment a conflict appeared and asked the operator what to do; only an unattended, headless run went straight to the autonomous resolver. The same conflict got two different treatments for no reason other than whether a person happened to be looking.
This job removed that split. Now every run, watched or not, tries the autonomous resolver first: combine main with the run's work in a scratch copy, let the agent clear the conflict, re-run the test gate against the result, and complete the merge only if it comes out clean. A person is asked for help only after autonomy has genuinely failed — and when asked, they get two plain choices: let the agent try again with their guidance, or stop the run. Whatever happens, if the run gives up, the main branch is put back exactly as it was, never left half-merged.
Build
Three sprints separated the last-resort human choice surface, the engine change that makes every run try autonomy first, and the wiring that keeps human help as a fallback — so the risky engine rewrite landed against tests already in place.
The build was cut so that the piece a person sees and the piece that decides when they see it were built and proven separately. The first sprint added only the interactive choice surface — the labelled "let the assistant try" and "stop the run" actions and the dispatch that answers them — while explicitly leaving the engine's decision about when a conflict reaches that surface untouched. That let the front-end contract be rendered and snapshot-tested on its own, before any engine path could reach it, so the second sprint could change engine behaviour against a surface that already existed and already had coverage.
The second sprint was the load-bearing one, and the run's history says so: it was the only sprint whose gate went red. This is the piece that had to reroute every run — interactive and headless alike — through the resolver, adjudicate leftover residue, re-check the integration against the real gate, and preserve the invariant that main is restored on any give-up. That is the change with the most distinct paths to keep straight at once, and the gate caught something on the first attempt; a fix brought it green. The other two sprints passed their gates without a repair. The failure landing exactly on the sprint that rewired the core conflict path, and nowhere else, is the decomposition locating its own hard part.
The third sprint could then be small: it wired the "let the assistant try" choice into the recovery directive workflow Gantry already had, with the one new constraint that this human-guided path is now reachable only after the autonomous attempt has failed. Nothing forced a re-plan, no sprint was retried from scratch, and the boundaries the plan drew held from the first sprint to the last.
Feature
Gantry's merge-back now runs one autonomy-first conflict path for every run, exposing a human decision only when the resolver truly fails — and the engine and interface pieces that job built are still the live path at today's HEAD.
Before this job, an attached run and a headless run disagreed about what a merge-back conflict meant. An attached run treated the conflict as a full stop and immediately prompted the operator; a headless run reached for the autonomous resolver. The capability to fix conflicts without a human existed but was gated behind how the run happened to be observed, and that inconsistency is what the job set out to remove.
The design in the tree today is a single conflict-resolution path in the merge-back engine. It integrates main inside the disposable worktree, runs the resolver agent, adjudicates any residue the agent leaves, re-grounds the result against the real gate, and only then lands the merge. A shared give-up routine handles every failure by aborting the in-progress main merge and restoring main to its snapshotted tip. The interactive fallback is now a genuine last resort: it is offered only after the autonomous attempt has already failed, and choosing to stop leaves main at its live tip with the partial integration preserved for manual repair.
Standing in the worktree at today's HEAD, that path is still the one that runs. The engine's conflict-resolution routine and its shared fallback-or-give-up decision remain the entry point for merge-back conflicts, carrying documentation that still describes autonomy-first as the rule and the console as the last-resort arm. The interactive choice surface persists too, though its wording has since shifted — the "stop the run" action is unchanged, while the assistant option now reads as asking the Gantry Agent rather than a generic assistant. The handover plan that specified all of this has been moved into the archived design records, its work having landed. Much of the code the job introduced has been reworked by later commits, but the shape it established — one autonomous-first path, a human choice only on real failure, main always restored — is intact.