Files
chess/doc/todo.md
T

14 KiB

Replay Implementation Tasks

This plan covers the remaining work needed for first-class replay in the web and CLI clients, durable player-game browsing, and a curated archive of famous games. The persistence/API foundation completed by the database audit is listed first so later work does not duplicate or bypass it.

Foundation Available Now

  • Store terminal result and end_time_utc on each game.
  • Persist white_claimed_by and black_claimed_by, including claims made on the first valid move after game creation.
  • Commit each move, first-move claim, and move-caused terminal result in one SQLite transaction.
  • Rewind moves and clear a stale terminal result/end time in one transaction.
  • Return ordered UCI moves with fenAfterMove through GET /api/v1/games/{gameId}/history.
  • Return bounded pages through authenticated GET /api/v1/users/me/games?limit=&offset=.
  • Provide matching Go client DTOs and methods (GetGameHistory, GetMyGames) without prematurely adding CLI presentation.
  • Evict terminal games from memory after a configurable TTL while retaining durable rows and moves.
  • Add an async-write barrier and a single SQLite read snapshot for immediate, internally consistent replay reads.
  • Configure browser API origin through /config, with /chess fallback for the static deployment at lixen.com/projects/chess/.
  • Add debug-level persistence, cleanup, engine queue, and lifecycle logging.

Decisions Required Before Replay UI Work

Decision Current behavior Decision needed
History visibility Public to anyone with a game UUID, like live game reads Keep public, make games private by default, or add per-game visibility
Live mutation authorization Configure, undo, computer-trigger, and unload remain UUID-based; claimed slots protect human moves only Define owner/opponent/spectator permissions before replay and archive UI expose more game discovery
Database retention Indefinite; only terminal in-memory state is evicted Retention by account type, archive status, age, or explicit deletion
Delete semantics DELETE /games/{id} unloads memory but retains history Name it “close/unload,” or add a separate authorized durable delete
Durability guarantee Gameplay continues after a write failure; health becomes degraded Keep best-effort, acknowledge writes, retry with an outbox, or fail gameplay closed
Result model white_wins, black_wins, draw, stalemate Keep compatibility or split outcome (1-0, 0-1, 1/2-1/2) from termination reason
Archived-game owner Not created Protected system/demo user, separate archive owner table, or ownerless source records
Replay notation UCI plus authoritative FEN after each move Add SAN and canonical PGN at import/write time or derive them on read

Record these choices in an ADR before changing the v1 response contract. Until privacy is decided, do not add searchable public player-game indexes or expose usernames in public history.

Phase 1 — Complete the Durable Game Model

Results and termination

  • Represent outcome separately from termination reason. Candidate fields: outcome, termination, and optional result_detail.
  • Detect and persist all supported draw paths, not only stalemate: insufficient material, repetition, fifty/seventy-five-move rule, and agreed draw if that interaction is added.
  • Define behavior for resignation, timeout, abandonment, engine failure, and administrative termination.
  • Add constraints covering valid combinations: an end time requires a terminal outcome; an ongoing game has neither.
  • Decide whether undoing a finished rated/player game is allowed. If yes, preserve an audit event rather than silently rewriting official history.

Stable participant metadata

  • Snapshot display names at game start/end so replay remains readable after a user rename or temporary-account deletion.
  • Separate historical participant identity from mutable controller config. Changing a human slot to a computer must never remove the user's game link.
  • Decide whether anonymous players receive a durable pseudonym, remain unnamed, or are excluded from archive browsing.
  • Add optional clocks/time-control metadata before timeout results are supported.

Notation and integrity

  • Add SAN per ply and canonical PGN, or add a deterministic backend converter from the stored initial FEN/UCI line.
  • Validate that move_number, player_color, FEN side-to-move, and the previous position form one legal continuous line.
  • Add a stored content hash for import idempotency and corruption checks.
  • Add a repair/audit CLI command that reports broken game rows without mutating them; make repair an explicit separate operation.
  • Define a schema-migration policy beyond v2, including forward-version rejection, backup instructions, and rollback limitations.

Phase 2 — Replay and Library APIs

Player games

  • Add filters to the authenticated list: status, result, color, opponent type, and date range.
  • Replace offset pagination with a stable (start_time_utc, game_id) cursor before the table grows large; retain v1 offset parameters during migration.
  • Return a compact display label/opponent summary so clients do not recreate association logic.
  • Define whether an authenticated user may list a game merely created for their random player ID versus one explicitly claimed by them.
  • Add authorization tests for expired/deleted sessions and attempts to list another user's games.

Replay payload

  • Version the history payload before adding annotations, evaluations, comments, variations, clocks, or PGN tags.
  • Include a canonical final FEN and normalized outcome/termination fields.
  • Decide whether long games return one payload or paged/chunked moves.
  • Add ETag/If-None-Match for immutable finished histories.
  • Add a downloadable PGN response with correct Content-Type and filename.
  • Return an explicit “ongoing/incomplete” marker when history is requested before a terminal result.

Live-game restoration

  • Decide whether a server restart should make unfinished games playable or replay-only.
  • If play must resume, load the last persisted FEN, next turn, player config, claims, and move list into memory at startup.
  • Mark games interrupted in pending state as recoverable stuck or ongoing; never re-submit an engine task blindly.
  • Define reconciliation when the service previously entered degraded mode and memory contains moves absent from SQLite.

Phase 3 — Curated Famous-Game Archive

Schema and ownership

  • Add a game origin such as player, curated, or imported.
  • Add searchable archive metadata: title, event, site, event date, round, white/black display names, Elo values, ECO/opening, source URL, source license, attribution text, and import timestamp.
  • Add publication state, featured flag, and explicit featured rank/order.
  • Create a protected demo/system identity only if ownership remains tied to users. It must not consume temporary-user capacity, expire, authenticate, or be evicted/deleted through normal user tools.
  • Prefer a separate protected archive owner over credentials embedded in seed scripts.
  • Add only indexes backed by actual archive queries; confirm each with EXPLAIN QUERY PLAN and a representative data volume.

Import pipeline

  • Add chess-server db archive import for one PGN or a directory.
  • Parse PGN tags, comments, NAGs, and variations deliberately; document which are preserved and which are discarded in the first version.
  • Validate every main-line move from its initial position and generate the authoritative FEN sequence before opening the transaction.
  • Import a game and all moves in one transaction.
  • Make repeated imports idempotent by source key/content hash.
  • Add dry-run, structured error output, per-file summary, and all-or-nothing versus continue-on-error modes.
  • Preserve source attribution and verify redistribution rights for every bundled collection.
  • Seed a small, reviewed fixture set in tests; keep large archives outside the executable and repository unless licensing and binary size are accepted.

Archive API

  • Add a public, bounded curated list endpoint with stable sorting.
  • Add exact filters required by the UI (featured, player name, event, year, ECO); do not expose an unconstrained database query API.
  • Reuse the same history representation for player and curated games.
  • Cache immutable curated list/history responses and invalidate only on archive administration.

Phase 4 — CLI Replay Experience

  • Add games/games mine to call GetMyGames, show pagination, result, colors, opponent/controller, date, and move count.
  • Add games featured after the curated endpoint exists.
  • Add replay <gameId> and allow selection from a prior list result.
  • Render the initial FEN before ply 1; never assume the standard start.
  • Add next/previous/start/end navigation, move-number jump, and optional autoplay speed.
  • Display UCI initially and SAN once the backend contract supplies it.
  • Clearly separate replay state from live session state: replay commands must not poll, move, undo, configure, or delete the live game.
  • Add pgn save <path> after the PGN endpoint is defined.
  • Cover empty lists, ongoing histories, custom FEN, malformed/incomplete history, expired auth, server restart, and deleted live-memory state.

Phase 5 — Web Replay Experience

  • Add “My games” for authenticated users and a separate “Classic games” collection available without login.
  • Build accessible loading, empty, pagination, and error states.
  • Add a replay route/deep link, for example ?replay=<gameId>, that works beneath /projects/chess/ and does not assume the API shares that path.
  • Initialize from initialFen; step by assigning the stored fenAfterMove, not by replaying moves through a browser chess engine.
  • Add previous/next/start/end buttons, move-list selection, keyboard controls, autoplay speed, pause, and current-ply announcement.
  • Disable move, computer-trigger, undo, and player-configuration actions in replay mode.
  • Stop live long-polling when replay mode begins and restore it only when a live game is explicitly reopened.
  • Show result, termination, players, date/event, source attribution, and custom-start notice.
  • Make browser back/forward restore list filters and replay ply.
  • Test both embedded /config and the deployed /chess fallback, including CORS and reverse-proxy headers.
  • Add responsive and accessibility checks for board orientation, focus, screen-reader labels, reduced motion, and high contrast.

Phase 6 — Durability, Operations, and Scale

  • Choose and implement the durability contract from the decision table. For acknowledged persistence, return success only after a writer receipt or use a durable outbox with retries and ordering.
  • Expose counters/metrics for queue depth, enqueue rejection, write latency, failed transaction, barrier latency, replay read latency, and terminal-memory eviction.
  • Add request/game correlation fields to logs without logging JWTs, passwords, or full private payloads.
  • Configure a stable production JWT signing key (prefer a secret file or deployment secret) so persisted sessions can survive a server restart; document rotation and invalidation procedures.
  • Add a bounded degraded-mode recovery procedure; current behavior requires operator intervention/restart and cannot reconstruct missing writes.
  • Benchmark list and history queries with realistic user/archive sizes and verify query plans in CI.
  • Set WAL checkpoint and database backup procedures; test online backup and restore with active reads/writes.
  • Define database retention separately for anonymous, temporary-user, permanent-user, and curated games.
  • Add authorized durable deletion/anonymization if required by the privacy policy, with archive records protected from accidental cascades.

Required Test Matrix

  • Upgrade a production-shaped legacy database to every new schema version and reopen it with foreign keys enabled on multiple pooled connections.
  • Read history immediately after create, move, terminal move, slot claim, player reconfiguration, and undo—without sleeps.
  • Run concurrent legal moves from one position; exactly one may commit and the loser must receive GAME_CONFLICT.
  • Submit duplicate computer triggers; only one engine task may run.
  • Fill the write queue/fault SQLite and assert degraded health, visible logging, and documented client behavior.
  • Shut down with queued writes and prove all accepted writes drain.
  • Restart after a finished game and replay the exact FEN sequence/result.
  • Evict a terminal game from memory and replay it from SQLite.
  • Change a claimed human slot to computer and verify “My games” association remains.
  • Verify registration duplicate/session failures roll back account creation and capacity eviction.
  • Exercise public/private history rules for anonymous, owner, opponent, and unrelated authenticated clients.
  • Validate imported PGNs with promotions, castling, en passant, custom FEN, comments, and every supported result.
  • Run Go unit/race tests, HTTP integration scripts, JavaScript syntax/tests, and browser end-to-end replay navigation in CI.

Replay Definition of Done

  • A finished player game survives restart, appears once in its owner's list, and replays deterministically from the stored initial FEN to the stored final FEN in both clients.
  • A curated game is imported idempotently with source attribution, appears in a stable public collection, and uses the same replay path as a player game.
  • Undo, player reconfiguration, terminal eviction, and concurrent requests cannot produce a stale result, missing claim, duplicate ply, or mixed history snapshot.
  • Privacy, retention, durable deletion, and degraded-write behavior are documented and enforced consistently by API, storage, web, and CLI layers.
  • Query plans and benchmarks show no redundant indexes or unbounded list scans at the agreed deployment size.