Commit Graph

2 Commits

Author SHA1 Message Date
dd6e6d5571 crdt_sync 0.5.0: the shared config and the cutover seam
Everything an app needs to move from GitHub to Firebase without each one
reinventing the wiring.

Python:
- _config.py: FirebaseConfig.load() + firebase_client_for(app) +
  mirror_client_for(app, github). One config file, read in one place.
  Credential caches stay per-app (~/.config/<app>/firebase_auth.json) --
  one file written by several processes is a corruption waiting to happen.
- tool/preflight_firebase.py: validates the config before any data moves.
  Catches a wrong-region databaseUrl (which answers 404 with a correctUrl
  body rather than an error, so it would otherwise pass every check and
  break mid-migration) and confirms the uid in the ID token matches the uid
  the rules pin -- sign_in() returns None and discards localId, so the uid
  is decoded from the JWT.
- tool/interop_seed.py: the missing Python half of the cross-language
  check. interop_check.dart asserts it can see a record written by Python,
  but nothing ever wrote one, so it could only ever fail.

Dart:
- firebase_config.dart: the same config shape, kept free of dart:io so it
  works on web and Android, where the file does not exist at all and
  credentials belong in flutter_secure_storage.
- PersistedSyncStateStore: the revision cache, persisted. Without it a
  phone forgets every peer revision on each cold start and re-downloads
  everything -- the exact traffic the revision scheme exists to avoid, and
  what the free-tier headroom depends on.

Verified live against the real database: preflight passes all five checks,
the Dart smoke test passes end to end, and Python <-> Dart interop is OK.

Python 226 tests at 100% branch coverage; Dart 218 tests, both new files
at 100% line coverage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJ9sxEw3ymzXNjL7SHNpJc
2026-08-04 22:59:48 +02:00
cfc4c295e4 crdt_sync: add the one-shot GitHub -> Firebase data migration
Reads through the GitHub API, not a local clone: the checkout on this
machine was five days stale while devices kept syncing, so seeding from it
would have silently rolled back everything written in between. Blobs are
fetched by sha rather than by path, so a concurrent push cannot swap the
content out from under the tree listing.

Writes through the real FirebaseSyncClient so the migration exercises the
same key escaping the apps will, then reads every blob back and compares
bytes before reporting success -- a migration that claimed success without
checking would be indistinguishable from one that dropped a file.

Retries connection resets and 5xx: GitHub drops keep-alive connections
routinely, and a half-completed migration is worse than one that never
started.

Three things are deliberately left behind, and stay in the GitHub repo
(archived, not deleted):
  - todo-sync/changesets/  pre-migration sqlite_crdt format, unread since
    2026-07-17
  - diet-guard-sync/devices/desktop/  a replica that stopped syncing on
    2026-07-22; verified to hold 0 records, 0 tombstones and 0 fields newer
    than both `pc` and `phone`. Carrying it over would cost a full
    re-download every tick forever, since a device that publishes no
    revision can never be skipped
  - READMEs and .gitkeep, which are not sync data

Run against the live database: 16 files, 956,272 bytes, all verified
byte-for-byte, and independently re-checked by listing the namespaces and
parsing records back out.
2026-08-03 18:31:18 +02:00