Shared Sync Algorithm Design
⚠️ Status: superseded (2026-06). The mobile consolidation removed the Room/offline layer, the
core-datalibrary, and the per-feature Hilt modules. Post-consolidation, all clients are online-first. This document is retained as a design reference; any future sync re-implementation should build on these goals but adapt to the consolidatedshared/structure.
Background
The Android app previously had a Retrofit-based SyncApi (GET/POST /sync) with SyncRemoteDataSource, SyncRepositoryImpl, and a SyncLocalDataSource using Room DAOs for offline queueing. The sync was Android-only.
Removed in migration (2026-06):
SyncApi.kt(Retrofit)SyncRemoteDataSource.ktSyncRepositoryImpl.kt- Related Hilt providers in
AppModule.kt
Created (still present, unwired):
shared/.../services/sync/SyncService.kt— shared Ktor stub
Current state: The SyncService stub exists but is not wired into any feature. All data operations are remote-only via direct HTTP calls.
Design Goals (for future reference)
- Multiplatform: Sync works on Android, Desktop, iOS, Web
- Conflict Resolution: Last-write-wins or server-authoritative
- Offline Queue: Write operations queued locally when offline, replayed when online
- Incremental: Only sync changes since last timestamp
- Shared Logic: Sync orchestration lives in
shared/module, not platform-specific
Proposed Architecture (for future reference)
┌───────────────────┐ ┌────────────────────┐
│ ViewModel / UseCase │ │ SyncRepository │
│ (triggers sync) │────>│ (orchestrator) │
└───────────────────┘ └─────────┬──────────┘
│
┌───────────────┼───────────────┐
│ │ │
┌───────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ SyncService │ │ LocalQueue │ │ Conflict │
│ (Ktor HTTP) │ │ (DataStore) │ │ Resolver │
└───────┬──────┘ └─────────────┘ └─────────────┘
│
┌───────▼──────┐
│ Server: /sync │
└──────────────┘Server Sync Endpoints (Existing, unchanged)
GET /sync?lastSyncTimestamp=<long>— pull changes since timestampPOST /sync— push local changes, receive merged result
Sync Data Model (existing, to be wired)
@Serializable
data class SyncPayload<T>(
val created: List<T>,
val updated: List<T>,
val deleted: List<String>, // IDs
val lastTimestamp: Long,
)
@Serializable
data class SyncResponse(
val tasks: SyncPayload<TaskDto>,
val habits: SyncPayload<HabitDto>,
val workoutDays: SyncPayload<WorkoutDayDto>,
val lastTimestamp: Long,
)Implementation Priority (if revived)
- Phase 1:
SyncServicewithgetSyncData()/saveSyncData()— already done (stub) - Phase 2: Local change queue using
DataStore<Preferences>(shared module) - Phase 3: Sync orchestrator in
shared/— pull + push + merge - Phase 4: Automatic sync triggers (network available, periodic, manual)
- Phase 5: Desktop + iOS integration
Current Limitations
- Server sync endpoints exist; client implementation is the gap
- No offline queue exists post-consolidation
lastSyncTimestampwould need to be stored per-user inDataStore- The offline/sync question is tracked as tech-debt TD-08