Full-state JSON snapshots let a scene be rolled back to an earlier
action. All game state lives in game_id-scoped tables, so a checkpoint
is just a serialized dump of the gameplay rows and rollback restores
it -- sidestepping deterministic replay (the deck shuffle is
materialized into state and captured verbatim).
- Checkpoint table + GameEvent.checkpoint_id (tri-state) +
Game.rollback_timeline_version (models.py, Alembic migration).
- crud_rollback.py: serialize/apply/capture/seal-purge/rollback.
- Capture is driven by the broadcast middleware: snapshot per action
while in the `scene` phase, seal+purge otherwise. Confined to the
current scene; older scenes' checkpoints are purged at scene end.
- POST /game/{gid}/player/{pid}/rollback (routes_rollback.py),
server-enforced for Admins and Deep players.
- EventLog.svelte: per-event rollback buttons + timeline reconciliation.
- Remove the orphaned /scene/rollback per-card-play undo (dead code
since d7f8483, never wired up; superseded by full-state rollback).
Phase 1 truncates the future immediately; the greyed/undoable redo is
deferred to Phase 2.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>