[Claude-drafted]
ProjectData.ClientId — the identity FW Lite uses to author new Harmony commits — lives inside the project's SQLite file. Copying the DB (second machine, restored backup, duplicated folder) therefore copies the authority to write as that client, which violates Harmony's single-writer-per-ClientId invariant and permanently forks sync (see sillsdev/harmony#79).
The goal is to move ClientId authority out of the copyable DB so that a copied/restored DB naturally gets a fresh identity. Minting extra ClientIds is cheap (one SyncState entry — same cost as a re-download today), so designs should fail toward new IDs, never toward reusing one.
One possible shape (illustrative, not a settled design):
- A random
dbInstanceId in ProjectData, written once at create/download.
- An app-local record
{dbInstanceId → clientId, lastOwnWriteWatermark} outside the project folder.
- On open: no record for this
dbInstanceId → this install never wrote this DB → fresh ClientId. Record exists but the DB's newest own commit is older than the watermark → DB was rolled back/replaced → fresh ClientId.
Whatever the mechanism, it needs careful per-platform thought: "app-local storage" and backup/restore semantics differ across Windows, Android, iOS, and web (e.g. Android auto-backup and iCloud restores may clone the app-local record along with the DB), which changes what each platform's variant actually protects against. Cases no local scheme can catch (full-device restore) fall back to the harmony-side tripwire.
fw-headless and seeded downloads: fw-headless maintains its own CRDT DB per project and must follow the same rule — its ClientId can't live in that DB either. Beyond correctness, that unlocks a fast path for initial project downloads: instead of pulling the full commit history and replaying it from scratch (today's GetChanges from an empty SyncState), the server could stream a ready-made DB (snapshots and projected tables included) and the client would then do a normal sync, playing back only the commits newer than the seed — almost always FAR fewer. Streaming a DB is only safe once the DB carries no writer identity.
Related hygiene, optional: LocalResource paths and ProjectData.Role/LastUserName/LastUserId are also install/user-local state living in the copyable DB; they could move to the same app-local record if ProjectData is being touched anyway.
[Claude-drafted]
ProjectData.ClientId— the identity FW Lite uses to author new Harmony commits — lives inside the project's SQLite file. Copying the DB (second machine, restored backup, duplicated folder) therefore copies the authority to write as that client, which violates Harmony's single-writer-per-ClientId invariant and permanently forks sync (see sillsdev/harmony#79).The goal is to move ClientId authority out of the copyable DB so that a copied/restored DB naturally gets a fresh identity. Minting extra ClientIds is cheap (one
SyncStateentry — same cost as a re-download today), so designs should fail toward new IDs, never toward reusing one.One possible shape (illustrative, not a settled design):
dbInstanceIdinProjectData, written once at create/download.{dbInstanceId → clientId, lastOwnWriteWatermark}outside the project folder.dbInstanceId→ this install never wrote this DB → fresh ClientId. Record exists but the DB's newest own commit is older than the watermark → DB was rolled back/replaced → fresh ClientId.Whatever the mechanism, it needs careful per-platform thought: "app-local storage" and backup/restore semantics differ across Windows, Android, iOS, and web (e.g. Android auto-backup and iCloud restores may clone the app-local record along with the DB), which changes what each platform's variant actually protects against. Cases no local scheme can catch (full-device restore) fall back to the harmony-side tripwire.
fw-headless and seeded downloads: fw-headless maintains its own CRDT DB per project and must follow the same rule — its ClientId can't live in that DB either. Beyond correctness, that unlocks a fast path for initial project downloads: instead of pulling the full commit history and replaying it from scratch (today's
GetChangesfrom an emptySyncState), the server could stream a ready-made DB (snapshots and projected tables included) and the client would then do a normal sync, playing back only the commits newer than the seed — almost always FAR fewer. Streaming a DB is only safe once the DB carries no writer identity.Related hygiene, optional:
LocalResourcepaths andProjectData.Role/LastUserName/LastUserIdare also install/user-local state living in the copyable DB; they could move to the same app-local record ifProjectDatais being touched anyway.