commit 2290830d2885276eab49a8f745083f5d8310b77d
parent 7ef94157432de98fd4447b95221ba269af479121
Author: Michael Camilleri <[email protected]>
Date: Sat, 30 May 2026 21:35:46 +0900
Give MovesUpdater's flush context a merge policy
The grid writer flushed buffered edits on a background context created
with the default NSErrorMergePolicy. Each flush links MovesEntity and
CellEntity to their GameEntity and bumps game.updatedAt, while the sync
engine writes the same rows from its own context. An optimistic-lock
conflict on any of them (Core Data 133020) would fail the save and lose
the whole flush — the grid-side analogue of the move-journal drop just
fixed, though its fresh-per-flush context makes the window much
narrower.
This commit sets the context to mergeByPropertyObjectTrump, matching the
rest of the sync layer (RecordApplier, SyncEngine, the publishers,
GameStore). Beyond resolving the conflict, in-memory-wins keeps the grid
fail-safe to the local typist on a same-cell race with an inbound remote
edit, which converges on the next merge — unlike the journal, which owns
no GameEntity field and so could take storeTrump.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Diffstat:
1 file changed, 8 insertions(+), 0 deletions(-)
diff --git a/Crossmate/Sync/MovesUpdater.swift b/Crossmate/Sync/MovesUpdater.swift
@@ -147,6 +147,14 @@ actor MovesUpdater {
writerAuthorID: String
) -> Set<UUID>? {
let context = persistence.container.newBackgroundContext()
+ // This flush links MovesEntity/CellEntity to their GameEntity and bumps
+ // game.updatedAt, while the sync engine writes the same rows from its own
+ // context. Without an explicit policy the default NSErrorMergePolicy would
+ // fail the save on an optimistic-lock conflict (Core Data 133020) and lose
+ // the whole flush — the grid-side analogue of the journal-drop bug. Match
+ // the rest of the sync layer with mergeByPropertyObjectTrump, which also
+ // keeps the grid fail-safe to the local typist on a same-cell race.
+ context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
return context.performAndWait {
var byGame: [UUID: [(Key, Pending)]] = [:]
for (key, pending) in snapshot {