Commit graph

1 commit

Author SHA1 Message Date
ac1c983ddd feat(schema): fields the client needs to sync watch state / watchlist / prefs
The #9 schema sketched these collections before the client existed. Reconciling
two devices turned up three gaps:

- `meta` (json) on watch_state/watchlist. Local rows carry display fields —
  episode, title, cover art, MAL id, completed/dismissed — with nowhere to live
  server-side, so a freshly-signed-in device pulled resume points it couldn't
  render.
- A client-owned clock. `updatedAt`/`addedAt` were autodate, i.e. stamped on
  server receipt, so a device that edited offline and pushed a day later beat a
  device that edited afterwards and synced at once. LWW needs the time the user
  acted. The server autodate lives on as `updated` and is now the pull cursor —
  that one must be server-side, or a skewed client clock would write rows behind
  another device's cursor and stay invisible to it.
- `deletedAt` tombstones on watchlist. Hard deletes are an absence, and an
  absence isn't pullable — the next device to push just resurrects the title.

The type change means dropping and re-adding the fields, which drops their column
data, so the migration snapshots the old timestamps and writes them back. Live is
believed empty, but blanking `updated` would leave rows invisible to every future
pull, which is too quiet a failure to risk on an assumption.

Verified against a local docker compose on both paths — a fresh install, and an
upgrade over the init-only schema with rows already in it (timestamps preserved).
scripts/verify.py grows the field-shape assertions plus behavioural checks: the
client clock round-trips unrestamped, duplicate (profile,itemId) is rejected, and
delete/re-add reuses the row instead of duplicating. 36/36 pass.

Refs richiexec/myanime-app#11
2026-07-17 16:20:38 +02:00