diff --git a/@packages/imessage/Sources/IMessageSync/SyncManager.swift b/@packages/imessage/Sources/IMessageSync/SyncManager.swift index eac7dc5..8809415 100644 --- a/@packages/imessage/Sources/IMessageSync/SyncManager.swift +++ b/@packages/imessage/Sources/IMessageSync/SyncManager.swift @@ -239,8 +239,9 @@ final class SyncManager: BaseSyncManager { log.info("Batch sent: \(sent) messages (running total: \(totalSynced))") } catch { totalFailed += window.count - log.warning("Batch failed (\(window.count) convs): \(error.localizedDescription)") - activityLog.warning("Batch failed, continuing...") + let detail = (error as NSError).userInfo.description + log.warning("Batch failed (\(window.count) convs, \(batchMsgs) msgs): \(error.localizedDescription) | \(detail)") + activityLog.warning("Batch failed (\(window.count) convs): \(error.localizedDescription)") } window.removeAll() windowMessages = 0 @@ -249,6 +250,10 @@ final class SyncManager: BaseSyncManager { for conversation in conversations { convIndex += 1 + if convIndex % 50 == 0 { + log.info("initial-sync progress: \(convIndex)/\(conversations.count) iterated, synced=\(totalSynced) failed=\(totalFailed)") + activityLog.info("Progress: \(convIndex)/\(conversations.count) (synced \(totalSynced) msgs)") + } do { let messages = try imessageReader.getMessages(conversationId: conversation.id, since: nil) if messages.isEmpty { continue } @@ -330,11 +335,23 @@ final class SyncManager: BaseSyncManager { log.info("Initial sync complete - \(totalSynced) messages synced, \(totalFailed) conversations failed") - if let newDate = latestMessageDate { + // Only advance the watermark when every batch was accepted. A partial + // initial sync that advances the watermark would mask the missing + // conversations forever — the regular incremental path filters by + // `date > lastSync` and would never retry them. Leaving the watermark + // unset means the next cycle re-enters performInitialSync (gated on + // server-side message count == 0) only after the operator truncates; + // for partial-failure mid-run we want the user to clear and retry, + // or for the loop to retry on the next syncNow. + if totalFailed == 0, let newDate = latestMessageDate { setLastSync(newDate) + activityLog.success("Initial sync complete: \(totalSynced) messages synced") + } else if totalFailed > 0 { + log.warning("Initial sync partial — watermark NOT advanced (\(totalFailed) convs failed). Re-run will retry.") + activityLog.warning("Initial sync partial: \(totalSynced) synced, \(totalFailed) failed — will retry") + } else { + activityLog.success("Initial sync complete: \(totalSynced) messages synced") } - - activityLog.success("Initial sync complete: \(totalSynced) messages synced") await fetchStats() } catch {