refactor(sync): share one CloudKit engine and error model between Mac and mobile - #1996
Merged
Conversation
…y can run in parallel
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the red
main: themacOS App Testsjob fails to build the test target withambiguous use of 'connection'atTableProTests/Core/Sync/SyncRecordMapperConnectionTests.swift:119(run 30484954184). This removes the cause rather than qualifying the reference.Cause
The macOS app and the shared package each declared their own version of six same-named types:
CloudKitSyncEnginePullResultSyncError.accountUnavailable,.unknown.noAccount,.tokenExpiredSyncStatus.disabled(DisableReason).error(String)SyncRecordTypeSyncMetadataStoragestatic shared, non-isolated@MainActorSyncCoordinatorimports the package, but every collision silently resolved to the app-local declaration, because a same-module declaration outranks an imported one with no diagnostic. That is how the two engines drifted into carrying different bugs, and why #1990 applied the identical 99-line diff to both files by hand. In a test file that imports both, the same collision becomes an outright ambiguity error.Change
All six types now live once, in
TableProSyncTransport. The app-local copies are deleted, so the shadowing is impossible rather than merely fixed. Net -128 lines across 53 files.SyncErrorkeeps the app's 9 cases and richer localized strings, plus.tokenExpired, and now mapsCKError.changeTokenExpired..accountUnavailableinstead of trapping. macOS Debug builds genuinely lack that entitlement.SyncRecordTypeowns the record-naming contract, removing four separate copies of it (two mappers plus three hardcoded prefixes inIOSSyncCoordinator).Three findings worth review attention
Favorite_is a proper prefix of bothFavoriteFolder_andFavoriteTable_, so matching in declaration order resolvesFavoriteFolder_abcto.favoritewith idFolder_abc. That would have silently corrupted favorite-folder and favorite-table sync.SyncRecordTypeTestscovers those three names explicitly.SecTaskCopyValueForEntitlementis macOS-only, which is the real reason the shared engine never carried the entitlement probe. It is now#if os(macOS)-guarded; iOS returnstrue, since its Debug and Release configurations share one entitlements file that always declaresicloud-services.CKError.changeTokenExpiredtoSyncError.tokenExpiredinsideperformPull, butSyncCoordinatorcaught the rawCKError. Left alone, that catch stops matching and incremental sync stalls forever.SyncCoordinator.isTokenExpired(_:)closes it, andSyncCoordinatorTokenExpiryTestsasserts a rawCKError(.changeTokenExpired)does not match, which is the guard against reverting it.Compatibility
No migration and no forced re-sync. Verified directly:
com.TablePro.sync.dirty.<type>and friends).NSKeyedArchiver,requiringSecureCoding: true, same key).ConnectionSyncFieldgating and the production CloudKit schema are untouched, andProductionSchemaParityTestspasses unmodified.Verification
SyncRecordType,SyncError,SyncStatus, andSyncMetadataStorage, which had no direct unit test on either side.main); sync suites pass.swift testruns them deterministically instead of self-skipping whenever the app test host happens to be entitled.Two pre-existing problems were verified as not caused by this change: an iOS simulator link failure against FreeTDS/hiredis/libssh2 reproduces identically on unmodified
main, and a set of unrelated test failures (etcd, Oracle formatting, plugin capability) reproduces identically atdaf79f178, which predates #1990. Both look toolchain-local.The second commit fixes a pre-existing race, kept separate for review:
ConnectionStorageSyncDeleteTestssharedtemporaryDirectory/tablepro-testswith 11 other test files and one of its cases deleted that directory outright, so it failed under parallel execution. It guards the documented sync delete ordering invariant, so its flakiness mattered.Not in scope
SyncRecordMapperstays duplicated. The two copies map genuinely different models (TablePro's richDatabaseConnectionversusTableProModels' leaner one) andConnectionSyncFieldalready reconciles them on the wire. Unifying those models is a larger separate gap.