Fix MR-923: refresh restored coordinator on merge Err path

branch_merge_impl swaps the coordinator for the merge target, runs the
merge body, then restores the original coordinator. A concurrent /change
on the same target during this window publishes against the swapped
coord, advancing on-disk manifest state that the restored coord doesn't
see.

The post-restore refresh was previously gated on merge_result.is_ok(),
so the clean-409 path (merge body's post_queue_snapshot drift check
returning a recoverable conflict) left the restored coord's cached
snapshot stale relative to disk. The next sequential /change seeded its
publisher expected_versions from that stale cache and 409'd with
ExpectedVersionMismatch — a non-retryable conflict surfaced to a caller
with no concurrent writer of their own.

Refresh on both Ok and Err paths so cached state cannot diverge from
the manifest across the swap-restore window.

Add a focused regression test
(concurrent_merge_clean_409_does_not_poison_next_change_on_target) that
loops the cell-d scenario until the clean-409 branch fires and asserts
the follow-up sentinel succeeds in that branch specifically.

Co-Authored-By: Ragnor Comerford <ragnor.comerford@gmail.com>
This commit is contained in:
Devin AI 2026-05-11 20:31:18 +00:00
parent 19e9292ec0
commit e91d5615c6
2 changed files with 99 additions and 1 deletions

View file

@ -1109,7 +1109,22 @@ impl Omnigraph {
.await;
self.restore_coordinator(previous).await;
if merge_result.is_ok() && previous_branch == target_branch {
// Refresh the restored coordinator on both Ok and Err paths.
// During the swap window above, `self.coordinator` was a freshly
// opened coord for the merge target; any concurrent writer on
// that target (e.g. a `/change` on `main` racing a
// `merge into=main`) publishes against the swapped coord and
// never touches the original. Without this refresh, the
// restored coord's cached manifest snapshot would diverge from
// disk and seed a stale `expected_versions` into the next op's
// publisher CAS fence — a non-retryable
// `ExpectedVersionMismatch` for a user with no concurrent
// writer of their own. Pinned by
// `concurrent_merge_clean_409_does_not_poison_next_change_on_target`
// in `crates/omnigraph-server/tests/server.rs` and by the
// `[d:merge×change:into-target]` cell of
// `concurrent_branch_ops_morphological_matrix`.
if previous_branch == target_branch {
self.refresh().await?;
}