Skip to content

fix(sync): refuse a push built on a stale record and merge it again - #3773

Merged
datlechin merged 9 commits into
mainfrom
fix/sync-stale-base-overwrite
Oct 22, 2026
Merged

datlechin merged 9 commits into
mainfrom
fix/sync-stale-base-overwrite

Conversation

@datlechin

@datlechin datlechin commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Summary

  • iCloud saves are conditional (.ifServerRecordUnchanged), so a push built on an older copy of a record is refused.
  • Every record is built on the cached server copy and carries only the fields this device changed. A refused save is merged onto the server's copy and sent again; a refusal without that copy fetches it; a record deleted elsewhere goes up again whole, as before.
  • Types built fresh with every field (tag, SSH and credential profiles, settings, favorites, table folders, saved queries on iOS) no longer revert fields another device changed.
  • The Mac's SSH route is judged against the newer copy by the dial rules: clearing its own route keeps a route another Mac published since. The pull merge uses the same edit.
  • A value iOS and the Mac only spell differently is not part of an edit. The iOS mapper and the Mac settings encoder write sorted JSON.
  • Settings merge option by option: objects key by key, lists of items with an id item by item. Slash commands two Macs added under one name both stay, the later one as name-2. Shortcuts, AI settings and themes go up whole, since their options depend on each other.
  • The record cache holds only what the local copy holds. A merged save keeps the pushed record as base until the local copy takes it, so an edit back to an earlier value still goes up. A category this Mac does not sync is not cached. iOS keeps the base of a copy with a pending edit, applies merged saves right away (a failed write holds the run as an unsaved pull) and settles legacy SSL modes from the pulled record.
  • A Mac pull leaves a record with a real pending edit alone; its push merges instead of the pull replacing the edit.
  • PushOutcome.merge lets a later save clear an earlier failure.

Test plan

  • New: SyncRecordEditTests (package), SyncConflictRebaseTests (macOS), and IOSSyncCoordinatorTests cases for a dirty connection, a saved query and an undo during a merged save, one per case above.
  • Changed on purpose: SyncedWriteFailureTests.unsavedSavedQueryHoldsOnlyTheToken now expects an unsaved saved query to stay out of the cache.
  • verify.sh: package TableProCore (3668), macOS sync suites (292), iOS sync suites (139), lint.

Needs the maintainer

  • A record with no cached base that iCloud already holds (a second Mac turning sync on: settings, slash commands, preset tags) still goes up whole over the iCloud copy. Recommendation: the iCloud copy wins there, as the theme seed already does.
  • Shortcuts and AI settings both changed on two Macs keep this Mac's whole category. Merging them per option needs rules for a chord two actions claim and an active provider the other Mac removed. Recommendation: the newer assignment keeps the chord, as the Keyboard settings do, and a missing active provider falls back to the first.

Follows #3769.

@datlechin
datlechin enabled auto-merge (squash) October 22, 2026 04:08
@datlechin
datlechin merged commit 6c7ca0b into main Oct 22, 2026
8 checks passed
@datlechin
datlechin deleted the fix/sync-stale-base-overwrite branch October 22, 2026 07:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant