fix(ui): suppress auto-open conflict resolver during MCP-driven merges

review_task/continue_merge on a planning parent always leaves conflicts in
the tree, and the UI auto-opened the resolver on every PlanningMergeConflict
broadcast regardless of who started the merge -- so a running Claude session
resolving a unit-merge conflict could race a human editing the same shared
checkout in a resolver window neither of them asked for.

PlanningMergeOrchestrator.StartAsync now takes an externallyDriven flag (set
by ExternalMcpService's MCP-driven review_task path, left false for the UI's
ApproveReview) that rides along on the PlanningMergeConflict broadcast. The
UI only auto-opens the resolver when it's false; otherwise it shows a
persistent banner with a manual "Open resolver" button, cleared on
PlanningMergeAborted/PlanningCompleted. A new GetActiveExternalConflictsAsync
query (checked against GitService.IsMidMergeAsync rather than the in-memory
flag alone) lets the UI resync the banner on reconnect instead of trusting a
one-shot broadcast that isn't replayed after a restart.

The childless single-task conflict path was checked and needed no change --
it only broadcasts the generic TaskUpdated, never PlanningMergeConflict.
This commit is contained in:
mika kuns
2026-08-06 12:04:52 +02:00
parent 8247a749a0
commit 5d1d2d89d0
15 changed files with 379 additions and 15 deletions
+22
View File
@@ -258,5 +258,27 @@
</StackPanel>
</Border>
<!-- External merge popup card — bottom-left corner, while an MCP session owns an
unresolved unit-merge conflict. Does not auto-dismiss; stays until the merge ends. -->
<Border Grid.Row="0" Grid.RowSpan="3"
VerticalAlignment="Bottom" HorizontalAlignment="Left"
Margin="16,0,0,30"
ZIndex="100"
IsVisible="{Binding IsExternalMergeBannerVisible}"
Background="{DynamicResource DeepBrush}"
BorderBrush="{DynamicResource PeatBrush}"
BorderThickness="1"
CornerRadius="6"
Padding="14,10">
<StackPanel Spacing="8" Width="300">
<TextBlock Classes="body" Text="{loc:Tr shell.externalMerge.banner}" TextWrapping="Wrap"/>
<StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Right">
<Button Classes="btn"
Content="{loc:Tr shell.externalMerge.open}"
Command="{Binding OpenExternalMergeConflictCommand}"/>
</StackPanel>
</StackPanel>
</Border>
</Grid>
</Window>