# 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.
