# Sync invariants GameTime remains offline-first. The current sync surface is preparatory only: there is no server protocol, remote auth, or network adapter yet. ## Local mutation log Every local mutation of a syncable aggregate must update the row metadata and append a `change_log` entry in the same local transaction. Required row metadata: - `id` is stable and generated locally. - `updatedAt` is the logical mutation timestamp. - `syncState` is `dirty` for local writes and `deleted` for soft deletes. - `localRevision` increases when an existing row changes. - `originDeviceId` identifies the device that produced the row mutation. - `futureOwnerProfileId` is nullable until user profiles exist. `change_log.operation` currently supports `insert`, `update`, `softDelete`, and `restore`. Exercise archive/unarchive is represented as `update` because the row is not deleted and the current schema has no `archive` operation. ## Future conflict policy Conflict resolution is not implemented yet. The intended first server strategy is last-write-wins per entity using `updatedAt`, with `localRevision` and `originDeviceId` retained for diagnostics and later upgrade. Vector clocks are not implemented and are not assumed by the current model. ## Future profiles The profile boundary will attach to `futureOwnerProfileId`. Until profile support is added, rows keep it nullable and sync code must not infer ownership from local device IDs.