diff --git a/CHANGELOG.md b/CHANGELOG.md index da44252..ec56407 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,99 @@ project follows [Semantic Versioning](https://semver.org/). _Nothing yet._ +## [0.14.0] — 2026-05-02 + +Two threads land in v0.14.0: the second half of the post-v0.12.0 UX +candidate list (theme thumbnails, daily-challenge calendar, Time Attack +auto-save, per-mode bests, time-bonus multiplier) plus a **major new +feature** — the replay pipeline (record → upload → web viewer). Three +Quat-reported bugs from a smoke-test round shipped alongside. + +### Added + +- **Theme-picker thumbnails** in Settings → Cosmetic. Each theme chip + renders a small Ace-of-Spades + back preview pair via the existing + `rasterize_svg` path. Cached per theme in a new + `ThemeThumbnailCache`. Themes that lack a preview SVG fall back to + a transparent placeholder rather than crashing. +- **14-day daily-challenge calendar** in the Profile modal. Horizontal + row of dots showing the trailing two weeks; today's dot is ringed + in `ACCENT_PRIMARY`, completed days fill `STATE_SUCCESS`, missed + days fill `BG_ELEVATED`. Caption above the row reads "Current + streak: N · Longest: M". +- **Time Attack session auto-save** to `/time_attack_session.json`, + atomic .tmp + rename. 30-second auto-save while a session is active, + plus on `AppExit`. Sessions whose 10-minute window expired in real + time while the app was closed are discarded on load. Classic, Zen, + and Challenge already auto-saved correctly via `game_state.json` — + Time Attack was the only mode missing session-level persistence. +- **Per-mode best-score and fastest-win readouts** in the Stats screen. + `StatsSnapshot` gains six `#[serde(default)]` fields (Classic / Zen + / Challenge × best_score + fastest_win_seconds). Stats screen renders + a "Per-mode bests" section between the primary cell grid and + progression. Lifetime totals continue to roll all modes together. +- **Time-bonus multiplier slider** in Settings → Gameplay (0.0–2.0, + 0.1 steps, default 1.0, "Off" label at zero). Cosmetic only — + multiplies the time-bonus shown in the win modal but does NOT + affect achievement unlock thresholds (those still use the raw + unmultiplied score). +- **Win-replay recording + storage.** Every move during a successful + game appends to a `RecordingReplay` resource; on `GameWonEvent` + the recording freezes into a `Replay` (seed + draw_mode + mode + + score + time + ordered move list) and persists to + `/latest_replay.json` atomically. Single-slot — overwrites + on every win. +- **"Watch replay" button** in the Stats overlay. Shows the latest + win's caption and surfaces a button that loads the replay (button + fires an `InfoToastEvent` describing the replay; full in-engine + playback is deferred to a future build). +- **Replay upload + fetch endpoints** on the server. `POST /api/replays` + accepts a `Replay` JSON; `GET /api/replays/:id` returns it. JWT-gated + with the existing auth middleware. Engine uploads winning replays + automatically when the player has cloud sync configured. +- **`solitaire_wasm` crate** — new workspace member compiling + replay-relevant `solitaire_core` types to WebAssembly so a + browser can re-execute a replay client-side. No-std-friendly + surface; `wasm-bindgen` glue. +- **Web replay viewer** served from the Solitaire server. + `GET /replays/:id` returns HTML + CSS + the wasm bundle that + fetches the replay JSON, rasterises a deal from the seed, and + animates the recorded moves. +- **Card flight animations on the web side** so the browser viewer + reads as a real game replay rather than a static dump. + +### Fixed + +- **Multi-card lift validation.** `solitaire_core::rules::is_valid_tableau_sequence` + rejects a moved stack whose adjacent cards don't form a descending + alternating-colour run. Previously a player could lift any + multi-card selection and drop it as long as the bottom landed + legally. Wired into `move_cards`'s tableau-destination branch. +- **Softlock detection.** `has_legal_moves` rewritten to walk every + potential move source (every stock card, every waste card, the + face-up top of every tableau column) and check it against every + foundation and every tableau. Previously the heuristic + early-returned `true` whenever stock had cards — players got + stuck in unwinnable end-states with no end-game screen. + `GameOverScreen` now actually fires for true softlocks. Quat's + exact reproduction case is pinned by a new test. +- **Deal-tween information leak.** New-game now snaps every card + sprite to the stock pile position before writing + `StateChangedEvent`, so all 52 cards animate from a single point + during the deal. Previously the sprites started from their + previous-game positions, briefly revealing the prior deal. + +### Documentation + +- `SESSION_HANDOFF.md` refreshed for the Quat smoke-test round + including investigation findings on solver decisions and + dependency duplicates. + +### Stats + +- 1134 passing tests (was 1053 at v0.13.0 close). +- Zero clippy warnings under `--workspace --all-targets -- -D warnings`. + ## [0.13.0] — 2026-05-02 Third UX iteration round on top of v0.12.0. Six handoff candidates @@ -312,7 +405,8 @@ with no PNG artwork yet. CREDITS.md, persistent window geometry, mode-launcher Home repurpose, client-side sync round-trip integration tests. -[Unreleased]: https://github.com/funman300/Rusty_Solitaire/compare/v0.13.0...HEAD +[Unreleased]: https://github.com/funman300/Rusty_Solitaire/compare/v0.14.0...HEAD +[0.14.0]: https://github.com/funman300/Rusty_Solitaire/compare/v0.13.0...v0.14.0 [0.13.0]: https://github.com/funman300/Rusty_Solitaire/compare/v0.12.0...v0.13.0 [0.12.0]: https://github.com/funman300/Rusty_Solitaire/compare/v0.11.0...v0.12.0 [0.11.0]: https://github.com/funman300/Rusty_Solitaire/compare/v0.10.0...v0.11.0 diff --git a/SESSION_HANDOFF.md b/SESSION_HANDOFF.md index 5b9798e..d1d4d83 100644 --- a/SESSION_HANDOFF.md +++ b/SESSION_HANDOFF.md @@ -1,20 +1,24 @@ -# Solitaire Quest — UX Overhaul Session Handoff +# Solitaire Quest — Session Handoff -**Last updated:** 2026-05-02 (session 8, post-Quat smoke test) — Quat playtested and reported four bugs + two investigation tasks. Three of the four bugs are fixed and pushed; the fourth was downstream of #2 and is now resolved without new code. Replay-feature WIP is back in the working tree. +**Last updated:** 2026-05-02 (session 9, post-v0.14.0 release prep) — v0.14.0 cut. The Quat bug fixes, the rest of the v0.13.0 candidate list, and the entire replay → upload → web-viewer pipeline are all bundled in this release. Direction now opens for the next round. ## Status at pause -- **HEAD on origin:** `2716472`. Three bug-fix commits pushed (`f1aeb24`, `3eabc14`, `2716472`) on top of the v0.13.0-era HEAD `0001432`. -- **Working tree:** WIP for the replay feature is restored (`solitaire_data/{lib,settings,stats}.rs`, `solitaire_data/src/replay.rs` new, `solitaire_engine/{game_plugin,lib,settings_plugin,stats_plugin,win_summary_plugin}.rs`, `solitaire_sync/{merge,stats}.rs`). Plus untracked `CARD_PLAN.md` (intentional). -- **Build:** `cargo clippy --workspace --all-targets -- -D warnings` clean — bug fixes + WIP coexist. -- **Tests:** **1126 passed / 0 failed** across the workspace (+ Quat's softlock case as a new regression test). -- **Tags on origin:** `v0.9.0`, `v0.10.0`, `v0.11.0`, `v0.12.0`. v0.13.0 still pending — its content shipped but the tag was never pushed; consider rolling its scope into v0.14.0 along with the bug fixes + replay feature. +- **HEAD on origin:** v0.14.0's tag commit (CHANGELOG + handoff refresh). +- **Working tree:** clean apart from untracked `CARD_PLAN.md` (intentional). +- **Build:** `cargo clippy --workspace --all-targets -- -D warnings` clean. +- **Tests:** **1134 passed / 0 failed** across the workspace. +- **Tags on origin:** `v0.9.0`, `v0.10.0`, `v0.11.0`, `v0.12.0`, `v0.13.0`, `v0.14.0`. ## Where we are -Post-v0.12.0 the handoff listed six "next-round candidates" — every one shipped plus two code-review fixes. v0.13.0 was prepared but not pushed. Then session 8 ran a smoke-test pass with Quat that surfaced four real bugs and two investigation tasks; three of the four bugs landed today, the fourth was downstream of #2 and is now resolved. +v0.14.0 is the largest release since the card-theme system. Three threads land together: -Direction is open. +1. **The remaining v0.13.0-era UX candidates** — theme thumbnails, daily-challenge calendar, Time Attack auto-save, per-mode bests, time-bonus multiplier slider. +2. **Quat smoke-test bug fixes** — multi-card move validation, softlock detection, deal-tween information leak. +3. **The replay pipeline** — record on win, persist to disk, upload to server, view in browser via a new `solitaire_wasm` crate. The biggest single feature since the card-theme system. + +The card-flight web animations and replay E2E test coverage close out the pipeline. ### Design direction (unchanged) @@ -26,61 +30,64 @@ Direction is open. `github.com/funman300/Rusty_Solitaire` is the canonical repo. Always push there. -## Session 7 round 3 (shipped 2026-05-02 late-late) — v0.13.0 +## Session 8 + 9 (shipped 2026-05-02) — v0.14.0 + +### v0.13.0-era UX candidates (had landed but missed v0.13.0's tag) | Area | Commit | What landed | |---|---|---| -| Font fix | `17f9b51` | Code-review fix: bundle FiraMono via `include_bytes!()` in both `font_plugin` and `svg_loader`; drop `load_system_fonts`, drop the lenient resolver, drop the CSS-generic fallbacks. New `bundled_font_resolver` always returns the single bundled face. Parse failure aborts with a clear error. | -| sccache removal | `13dd44b` | Code-review fix: deleted `.cargo/config.toml` and the `.cargo` directory. Plain `cargo build` works without per-project setup. | -| Wave 1 bundle | `ddc8f27` | **Tooltip-delay slider** in Settings → Gameplay (0.0–1.5 s, 0.1 s steps, "Instant" label at zero). **Win-streak fire animation** at thresholds [3, 5, 10] via new `WinStreakMilestoneEvent`. **Score-breakdown reveal on win modal** with per-row stagger (Base / Time bonus / No-undo / Multiplier / Total), respects `AnimSpeed::Instant`. | -| Card-back theming | `7ed4f2c` | The active theme's `back.svg` now actually drives the face-down sprite. Legacy `back_N.png` picker remains as a fallback for themes without a back; Settings caption surfaces when the override is in effect. | -| Drag-with-keyboard | `a0fc0d2` | Tab → Enter → arrows → Enter completes a move without a mouse. New `KeyboardDragState` resource; mutual exclusion with mouse drag via `KEYBOARD_DRAG_TOUCH_ID` sentinel. Help + onboarding hotkey lists updated. | -| Right-click radial | `b37f0cb` | Hold RMB on a face-up card → ring of icons at the cursor, one per legal destination; release over an icon → `MoveRequestEvent`. New `RadialMenuPlugin`. Help controls reference gains a "Mouse" section. | +| Theme thumbnails | `ba527de` | Each Settings → Cosmetic theme chip renders an Ace + back preview pair via `rasterize_svg`. Cached per theme. Missing-SVG themes show a transparent placeholder rather than crashing. | +| Daily-challenge calendar | `1a10476` | 14-dot horizontal calendar in the Profile modal. Today is ringed, completed days fill `STATE_SUCCESS`, missed days fill `BG_ELEVATED`. Caption: "Current streak: N · Longest: M". `PlayerProgress` gains `daily_challenge_history` (capped at 365) and `daily_challenge_longest_streak`. | +| Time Attack auto-save | `0001432` | New sibling `time_attack_session.json` next to `game_state.json`. Atomic .tmp + rename. 30 s auto-save while active + on `AppExit`. Sessions whose 10-min window expired in real time while the app was closed are discarded on load. | +| Per-mode bests | `3984231` | StatsSnapshot gains six `#[serde(default)]` fields (Classic / Zen / Challenge × best_score + fastest_win_seconds). Stats screen renders a "Per-mode bests" section. Lifetime totals continue to roll all modes together. | +| Time-bonus slider | `89c51ab` | Settings → Gameplay slider 0.0–2.0, default 1.0, "Off" at zero. Multiplies the time-bonus shown in the win modal. Cosmetic only — does NOT affect achievement unlock thresholds. | -## Session 8 (shipped 2026-05-02 post-Quat) - -Quat playtested current `master` and reported 4 bugs + 2 investigation items. Bug #3 turned out to be downstream of bug #2 — the `GameOverScreen` and `WinSummaryOverlay` modals already exist with new-game buttons; the softlock screen just never spawned because the old `has_legal_moves` returned `true` whenever stock had cards. With #2 fixed, the existing screen will fire for Quat's case. Smoke-test verification on the player side is the last step. +### Quat smoke-test bug fixes | Area | Commit | What landed | |---|---|---| -| Move validation (#1) | `f1aeb24` | `solitaire_core::rules::is_valid_tableau_sequence(&[Card]) -> bool` checks every adjacent pair in a moved stack descends one rank with alternating colour. Wired into `move_cards`'s tableau-destination branch. Closes the bug where a player could lift an arbitrary multi-card selection and drop it as long as the bottom landed legally. One focused test (single-card / valid-run / same-colour / rank-gap). | -| Deal-tween leak (#4) | `3eabc14` | `handle_new_game` now snaps every existing card sprite to the stock pile's position before writing `StateChangedEvent`. The downstream slide tween in `card_plugin` reads the stock position as its source, so all 52 cards animate from a single point — reads as "dealing from the deck" with no information leak. Gated on `Option>` for headless tests. | -| Softlock detection (#2) | `2716472` | `has_legal_moves` rewritten: replaces the early-return-on-non-empty-stock heuristic with a single pass over every card that could ever become a move source (every stock card, every waste card, the face-up top of every tableau column). Each is checked against every foundation and every tableau. Returns `true` only if some card anywhere can land somewhere — otherwise the player is genuinely stuck no matter how many recycles. Fresh-game test renamed; new test reproduces Quat's exact case (foundation 0 at 10, stock holds Hearts 2–5, no legal landing). | -| End-game screen (#3) | — | Resolved as downstream of #2. Verified `GameOverScreen` (game_plugin.rs:636) shows "No more moves available" + final score + Undo + New Game buttons; `WinSummaryOverlay` (win_summary_plugin.rs) shows the breakdown + Play Again. Both pre-existed; the softlock path just wasn't being reached. | +| Move validation (#1) | `f1aeb24` | `solitaire_core::rules::is_valid_tableau_sequence(&[Card]) -> bool` checks every adjacent pair in a moved stack descends one rank with alternating colour. Wired into `move_cards`. Closes the bug where any multi-card lift could be dropped as long as the bottom landed legally. | +| Deal-tween leak (#4) | `3eabc14` | New-game snaps every card sprite to the stock pile position before writing `StateChangedEvent`, so all 52 cards animate from a single deck point during the deal. Previously sprites started from previous-game positions, briefly revealing the prior deal. | +| Softlock detection (#2) | `2716472` | `has_legal_moves` rewritten: walks every potential move source (every stock card, every waste card, the face-up top of every tableau column) against every foundation and every tableau. Previous heuristic returned `true` whenever stock had cards, hiding genuine softlocks. `GameOverScreen` now actually fires for true softlocks. | +| End-game screen (#3) | — | Resolved as downstream of #2. The pre-existing `GameOverScreen` and `WinSummaryOverlay` already cover the close-out paths; the softlock screen just never spawned because the old `has_legal_moves` lied. | -### Investigation findings +### Replay pipeline (the major feature) -**Solver / unwinnable-deals decision (Quat report):** still open — needs your call. Three options Quat outlined: (a) accept some deals are unwinnable, (b) run a solver at deal-time and only ship winnable layouts, (c) offer a "winnable deals only" mode. (b) is the modern-Solitaire standard but adds a dependency or hand-rolled solver (~500–1500 LOC). (c) is the lightest middle ground — keep classic deals available, add a Settings toggle. Recommendation: defer until other UX work settles; doesn't block any release. +| Area | Commit | What landed | +|---|---|---| +| Replay storage | `42535f5` | `solitaire_data::replay::Replay` (seed + draw_mode + mode + score + time + recorded date + ordered move list) and atomic save/load helpers under `/latest_replay.json`. Schema v1; `load` returns None for any other version. | +| Engine recording | `57d1c58` | `RecordingReplay` resource + `ReplayPath` settings. Every successful `MoveRequestEvent` / `DrawRequestEvent` appends to recording; `GameWonEvent` freezes the recording into a `Replay` and persists. Undo intentionally not recorded. New game clears the recording. | +| Stats button | `d9f36bf` | Stats overlay surfaces a "Latest win:" caption + "Watch replay" button. Loads from disk via `LatestReplayResource`. (Full in-engine playback deferred — button currently fires an `InfoToastEvent` describing the replay.) | +| Server upload + fetch | `93182fa` | `POST /api/replays` accepts a `Replay` JSON; `GET /api/replays/:id` returns it. JWT-gated. SQL migration for the new `replays` table. | +| Engine sync | `23c9704` | Engine uploads winning replays automatically when the player has cloud sync configured. Re-uses the existing JWT/refresh-token flow. | +| WASM crate | `5bed43e` | New workspace member `solitaire_wasm` compiles replay-relevant `solitaire_core` types to WebAssembly so a browser can re-execute a replay client-side. `wasm-bindgen` glue. | +| Web viewer | `07b8ecd` | `GET /replays/:id` returns HTML + CSS + the wasm bundle. Browser fetches the replay JSON, rasterises a deal from the seed, and animates the recorded moves. | +| E2E coverage | `3081505` | Server tests covering the full upload → fetch round-trip via `axum::test`. | +| Web flight anim | `1fcd032` | Card-flight tweens on the web side so the browser viewer reads as a real game replay rather than a static dump. | -**Dependency duplicates (Quat report — 1014 deps):** the biggest single bloat is the audio stack split. Bevy's default features pull `bevy_audio → rodio → cpal 0.15 + alsa 0.9 + symphonia ⨯N codecs`, while the project actually uses **kira** for sound (`cpal 0.17 + alsa 0.10`). Disabling Bevy's default `bevy_audio` feature would eliminate 20+ transitive crates including the rodio + symphonia chain. Other duplicates are minor (bitflags 1.x via `png` is build-tooling only; multiple hashbrown majors are common in the Bevy/wgpu ecosystem and not actionable). Recommendation: a one-line `default-features = false` swap on the workspace `bevy =` line, then re-enable explicitly the features the engine uses (`render`, `bevy_winit`, `2d`, `bevy_window`, `png`, `bevy_text`, `bevy_ui`, `bevy_log`, `bevy_asset`, `default_font`, `bevy_state`, `webgpu`/`webgl2` if targeting wasm). Worth ~50 fewer crates compiled. Defer until the active feature work settles so churn doesn't conflict. +## Open punch list -## Open punch list — release prep +### Release prep +1. **Smoke-test on the alex machine** after pulling — confirm Quat's three bug fixes hold up in real gameplay, and try the new replay button + web viewer end-to-end. +2. **Desktop packaging** per `ARCHITECTURE.md §17`. The Arch PKGBUILD exists in `/home/manage/solitaire-quest-pkgbuild/` (separate repo). Pending: app icon, macOS `.icns` + notarisation cert, Windows `.ico` + Authenticode cert, AppImage recipe. -1. **Push** the unpushed feature commits to origin (5 still unpushed: `b37f0cb`, `a0fc0d2`, `7ed4f2c`, `ddc8f27`, `13dd44b`/`17f9b51` — those last two were on the v0.13.0 round, never pushed; verify with `git log --oneline origin/master..HEAD` after committing replay). -2. **Roll v0.13.0 + replay + bug fixes into v0.14.0** rather than tagging two close releases. The bug fixes alone aren't a feature release; bundle them with the replay feature when it lands. -3. **Desktop packaging** per `ARCHITECTURE.md §17`. The Arch PKGBUILD exists in `/home/manage/solitaire-quest-pkgbuild/` (separate repo). Pending: app icon, macOS `.icns` + notarisation cert, Windows `.ico` + Authenticode cert, AppImage recipe. -4. **Smoke-test the bug fixes** on the alex machine after pulling: confirm (a) tableau-to-tableau invalid-stack moves are now rejected, (b) the new-game deal animates from a single deck position with no per-card origin leak, (c) softlock with unplayable stock now spawns the GameOverScreen. +### UX iteration (next-round candidates) -## Open punch list — UX iteration (next-round candidates) - -Several v0.13.0-era candidates have already shipped to master since the v0.13.0 doc commit: **daily-challenge calendar** (`1a10476`), **card-art thumbnails in the theme picker** (`ba527de`), **auto-save in Time Attack** (`0001432`). Replay is **WIP in the working tree** — `solitaire_data/src/replay.rs` plus modifications across stats/settings/win-summary plugins. Not yet committed. - -Fresh candidates not yet started: - -- **Per-mode high-score readout** in the Stats screen. Currently lifetime stats roll all modes together. -- **Auto-save Zen mode** alongside Time Attack so close-mid-session resumes work in both. -- **Configurable scoring weights** — Settings → Gameplay slider for time-bonus magnitude. Cosmetic but power-user appealing. -- **Solver-at-deal toggle** (Quat's investigation #1, deferred): per the recommendation in the session-8 findings, add a Settings toggle "Winnable deals only" rather than baking solver-only into all deals. Lightest middle ground. -- **Disable Bevy's default audio feature** (Quat's investigation #2, deferred) to drop ~50 transitive crates. One-line workspace edit then re-enable engine features explicitly. Defer until active feature work settles. +- **Solver-at-deal toggle** (Quat investigation #1, still deferred): add a Settings → Gameplay toggle "Winnable deals only" rather than baking solver-only into every deal. Lightest middle ground. +- **Disable Bevy's default audio feature** (Quat investigation #2, still deferred): one-line `default-features = false` swap on the workspace `bevy =` line, re-enable explicitly the features the engine uses (`render`, `bevy_winit`, `2d`, `bevy_window`, `png`, `bevy_text`, `bevy_ui`, `bevy_log`, `bevy_asset`, `default_font`, `bevy_state`). Drops ~50 transitive crates including the rodio + symphonia stack the project doesn't use (kira handles audio). +- **In-engine replay playback** — promote the "Watch replay" button from a stub toast to a real playback overlay that re-runs the recorded moves with `CardAnimation` tweens. The wasm crate already proves the playback math; the in-engine version reuses the same execute logic against the live game state. +- **Per-replay history** — currently single-slot at `latest_replay.json`. A "best replay per mode" bucket or a recent-N rolling list would let players revisit notable wins. +- **Solver-driven hint system** — extend the existing hint toggle so a deal-time solver provides higher-quality hints (currently a heuristic). Requires the solver from the toggle work above. +- **Achievement: "won via replay path"** — track when a player wins a deal whose previously-saved replay also won the same deal. Mostly fun; trivial scope. ## Card-theme system (CARD_PLAN.md, fully shipped) -Seven phases landed across `b8fb3fb` → `924a1e2` in v0.11.0; v0.13.0's `7ed4f2c` finally consumes the per-theme `back.svg`. End-to-end: +Seven phases landed across `b8fb3fb` → `924a1e2` in v0.11.0; v0.13.0's `7ed4f2c` consumes the per-theme `back.svg`; v0.14.0's `ba527de` adds preview thumbnails. End-to-end: - **Bundled default theme** ships in the binary via `embedded://` — 52 hayeah/playing-cards-assets SVGs + a midnight-purple `back.svg`. - **User themes** under `themes://`. Drop a directory containing `theme.ron` + 53 SVGs. - **Importer** at `solitaire_engine::theme::import_theme(zip)` validates archives and atomically unpacks. -- **Picker UI** in Settings → Cosmetic; the active theme's `back` overrides the legacy `back_N.png` picker when present. +- **Picker UI** in Settings → Cosmetic; thumbnails + the active theme's `back` override the legacy `back_N.png` picker when present. ## Resume prompt @@ -88,22 +95,17 @@ Seven phases landed across `b8fb3fb` → `924a1e2` in v0.11.0; v0.13.0's `7ed4f2 You are a senior Rust + Bevy developer working on Solitaire Quest. Working directory: . -Branch: master. Direction is OPEN — Quat's smoke-test bug round -landed (3 fixes pushed, 1 resolved as downstream); replay feature is -WIP in the working tree. +Branch: master. Direction is OPEN — v0.14.0 just shipped covering the +Quat bug fixes, the v0.13.0 candidate tail, and the entire +replay-pipeline feature. -State: origin/master at 2716472 (Quat's softlock fix). Working tree -has uncommitted replay-feature WIP across solitaire_data, -solitaire_engine, solitaire_sync — `solitaire_data/src/replay.rs` is -new. Plus untracked CARD_PLAN.md (intentional). Five feature commits -from v0.13.0 round are unpushed (b37f0cb, a0fc0d2, 7ed4f2c, ddc8f27, -plus the v0.13.0 doc commits) — verify with -`git log --oneline origin/master..HEAD`. +State: HEAD at v0.14.0. Working tree clean apart from untracked +CARD_PLAN.md (intentional). Build: cargo clippy --workspace --all-targets -- -D warnings clean. -Tests: 1126 passed / 0 failed (includes Quat's softlock regression). +Tests: 1134 passed / 0 failed. READ FIRST (in order, before doing anything): - 1. SESSION_HANDOFF.md — session 8 changelog + punch list + 1. SESSION_HANDOFF.md — v0.14.0 changelog + open punch list 2. CHANGELOG.md — release-by-release record 3. CLAUDE.md — hard rules (UI-first, no panics, etc.) 4. ARCHITECTURE.md — crate responsibilities + data flow @@ -112,15 +114,14 @@ READ FIRST (in order, before doing anything): may be missing on a fresh machine) DECISION TO ASK THE PLAYER FIRST: - A. Finish the replay-feature WIP, commit, then bundle everything - (replay + v0.13.0 round + Quat fixes) into v0.14.0. - B. Smoke-test the bug fixes on alex first to confirm Quat's three - issues are resolved in real gameplay. - C. Take the deferred Bevy-audio-feature trim (Quat investigation - #2): drop default-features and re-enable explicitly. Worth ~50 - fewer crates. - D. Take the deferred solver toggle (Quat investigation #1): add - "Winnable deals only" Settings toggle. + A. Smoke-test v0.14.0 on the alex machine first to confirm the + three Quat bug fixes hold up in real gameplay and the replay + pipeline works end-to-end (record → upload → web viewer). + B. Take the deferred Bevy-audio-feature trim (Quat investigation + #2) — one-line workspace edit, ~50 fewer transitive crates. + C. Take the deferred solver toggle (Quat investigation #1): add + "Winnable deals only" Settings toggle. Larger. + D. Promote the in-engine "Watch replay" button to real playback. E. Pick from the remaining "next-round candidates" in this doc. F. Take the deferred desktop-packaging item (needs artwork + signing certs from the user).