fix(core): save files store the deal via upstream serializers (schema v6) #171

Merged
funman300 merged 1 commits from fix/save-schema-v6-session-recording into master 2026-07-10 18:12:28 +00:00
Owner

Follow-up to #170: GameState's save serde had the same latent flaw replays had. Schema v5 persisted seed + saved_moves and re-dealt the board from the seed on load, so any RNG or upstream upgrade that shifts the seed→deal mapping would invalidate every in-progress save. The failure was graceful (validation error → fresh game) but data-losing.

Change

  • v6 persists the SessionRecording introduced in #170 (upstream card_game session serde: config + dealt board + instruction list). seed remains presentation metadata only.
  • v4/v5 files still load through the legacy seed path (they remain valid as long as the mapping they were written under holds) and are rewritten as v6 on the next save — a natural migration.
  • v6 loads replay the instruction history with per-instruction validation, so tampered or corrupt files surface an error instead of a silently wrong board.
  • saved_moves() removed (dead once serialisation reads recording()).

Tests

  • v6 mid-game round-trip via the storage layer (byte-identical re-save)
  • corrupting the seed field of a v6 save does not change the loaded board
  • v5 legacy fixture loads with correct move count and stats
  • v6 without a recording payload is rejected with a named error
  • recording with an invalid instruction history is rejected

Gate: cargo test --workspace green (27 binaries), clippy --all-targets -- -D warnings clean, rustfmt applied.

🤖 Generated with Claude Code

Follow-up to #170: `GameState`'s save serde had the same latent flaw replays had. Schema v5 persisted `seed + saved_moves` and re-dealt the board from the seed on load, so any RNG or upstream upgrade that shifts the seed→deal mapping would invalidate every in-progress save. The failure was graceful (validation error → fresh game) but data-losing. ## Change - **v6** persists the `SessionRecording` introduced in #170 (upstream `card_game` session serde: config + dealt board + instruction list). `seed` remains presentation metadata only. - **v4/v5 files still load** through the legacy seed path (they remain valid as long as the mapping they were written under holds) and are rewritten as v6 on the next save — a natural migration. - v6 loads replay the instruction history with per-instruction validation, so tampered or corrupt files surface an error instead of a silently wrong board. - `saved_moves()` removed (dead once serialisation reads `recording()`). ## Tests - v6 mid-game round-trip via the storage layer (byte-identical re-save) - corrupting the `seed` field of a v6 save does not change the loaded board - v5 legacy fixture loads with correct move count and stats - v6 without a recording payload is rejected with a named error - recording with an invalid instruction history is rejected Gate: `cargo test --workspace` green (27 binaries), `clippy --all-targets -- -D warnings` clean, rustfmt applied. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
funman300 added 1 commit 2026-07-10 17:21:18 +00:00
GameState's save serde had the same latent flaw PR #170 removed from
replays: v5 persisted seed + saved_moves and re-dealt the board from the
seed on load, so any RNG or upstream upgrade that shifts the seed->deal
mapping would invalidate every in-progress save (graceful error, but the
player loses their game). v6 persists the existing SessionRecording
payload (upstream card_game session serde: config + dealt board +
instructions) instead; seed stays as presentation metadata only.

- v4/v5 files still load through the legacy seed path and are rewritten
  as v6 on the next save; v6 files load from the recording and replay
  with per-instruction validation, so tampered or corrupt files surface
  an error rather than a silently wrong board
- saved_moves() removed (dead once serialisation reads recording())
- tests: v6 round-trip via storage, seed-corruption immunity, v5 legacy
  load, missing-recording rejection, invalid-history rejection

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
funman300 merged commit 3fbee9ce30 into master 2026-07-10 18:12:28 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: funman300/Ferrous-Solitaire#171