mirror of
https://github.com/kuhyx/todo-app.git
synced 2026-11-10 21:07:02 +01:00
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019KwUCPESvE5CjCtmUFz8xQ
2.2 KiB
2.2 KiB
Migration: sqlite_crdt → shared crdt_sync
todo's note storage and sync were moved off sqlite_crdt onto the shared
crdt_sync library
(pinned tag crdt_sync_dart-v0.3.0), so every companion app now syncs through
one library.
What changed
- Storage:
NoteRepositoryis backed bycrdt_sync'sLogStore(aLog = Map<String, Record>persisted as JSON). EachNoteis stored as aRecord(per-field last-writer-wins + a sticky tombstone), so the on-disk log is the sync wire format — no separate adapter. Filtering/sorting run in Dart over the in-memory log. - Sync:
SyncServicepushes each device'sLogtotodo-sync/notes/<nodeId>.jsoninkuhyx/syncs(new dir; the oldchangesets/format is incompatible, so it's a clean cutover). It usescrdt_sync'sGitHubClient+logToJson/logFromJson. - Client: todo's local
lib/sync/github_client.dartwas deleted; it now usescrdt_sync'sGitHubClient(which gainedcanAccessRepo()+deleteFile()in v0.3.0).putFileTextself-resolves the file sha, so the old SHA-tracking is gone.
The one-time on-device migration
NoteRepository.open() migrates a pre-existing sqlite_crdt DB into the log on
first launch, guarded by a persisted crdt.migratedFromSqlite flag. Verified
on-device: a real 27-note backlog (+5 tombstones) migrated intact.
sqlite_crdt is retained as a dependency only to read that legacy DB during
migration — the old todo.db is left untouched as a safety net. Remove the dep
in a later cleanup once every device is known-migrated.
Surprises / non-obvious decisions
- Seed HLCs from each note's
updatedAt, not "now". Two devices migrate independently; stamping migration-time "now" would let per-field LWW resolve a pre-cutover edit arbitrarily. Seeding fromupdatedAtkeeps last-real-edit-wins across the cutover. - Reuse the legacy
sqlite_crdtnode id (persisted in prefs) so a device keeps itsdevices/<id>identity and devices still converge. watchNotes/watchCountseed inonListen, not lazily in anasync*body — otherwise a write betweenlisten()and the first generator tick swallows the initial snapshot.