# Solitaire Quest — Session Handoff **Last updated:** 2026-05-07 — `[Unreleased]` accumulating v0.20 candidates. Seven commits sit on top of v0.19.0: three close items from v0.19.0's punch list (pull-failure flake, smart-window-size opt-out, Shareable badge), one extends Help cheat-sheet coverage, and three open a new performance / portability arc — F3 FPS overlay, Android build target (first working APK), and the matching developer-setup runbook. The Android build is the load-bearing change: `solitaire_app` now compiles into both a desktop `bin` and an Android `cdylib` from the same source. ## Status at pause - **HEAD on origin:** `59424a3` (post-Android-runbook commit). - **Working tree:** modified — `CHANGELOG.md` and `SESSION_HANDOFF.md` carry the v0.20-candidates promotion + this refresh, ready to commit. `artwork/` remains untracked. - **Build:** `cargo clippy --workspace --all-targets -- -D warnings` clean (verified this session). - **Tests:** **1170 passing / 0 failing** across the workspace (verified this session). The previously-flaky `pull_failure_sets_error_status` is fixed; no known flakes remain. - **Tags on origin:** `v0.9.0` through `v0.19.0`. ## Where we are v0.19.0's "Possible next-round candidates" list shipped 3 of 4: - ~~**`pull_failure_sets_error_status` flake fix:**~~ shipped at `67c150b`. - ~~**Settings UI for smart-default-size opt-out:**~~ shipped at `e1b8766`. - ~~**Persistent share link URL on selector caption:**~~ shipped at `9b065e5` (as a "Shareable" badge on the Latest-win caption — the Prev/Next selector chips don't have a live spawn site yet, so the badge attaches to the existing single-replay caption). - **App icon round** — still open. 11 PNGs generated by `artwork/Icon Export.html` are *not* in the `artwork/` directory any more (current `artwork/` holds the reverted Rusty Pixel card PNGs); icon-export needs to be re-run before this item can be picked up. Two new threads opened that weren't on any prior punch list: - **F3 FPS / frame-time overlay** (`690e1d2`). `DiagnosticsHudPlugin` wraps `FrameTimeDiagnosticsPlugin`; F3 toggles a top-right readout. Hidden by default; primarily a developer affordance. - **Android build target** (`fb8b2ac` + `59424a3`). `solitaire_app` is now a bin + cdylib hybrid. `cargo apk build` produces a 54 MB APK with full assets. Five gating points resolved (split, manifest, bevy feature, arboard target-gate, keyring target-gate). What's *not* yet verified: APK launch on AVD/phone, `dirs::data_dir()` Android behaviour for the persistence/sync layer. ### Design direction (unchanged) - **Tone:** Balatro — chunky readable type, theatrical hierarchy, satisfying micro-interactions. - **Palette:** Midnight Purple base + Balatro yellow primary + warm magenta secondary. ### Canonical remote `github.com/funman300/Rusty_Solitaire` is the canonical repo. Always push there. ## v0.20 candidates ([Unreleased] in CHANGELOG) | Area | Commit | What landed | |---|---|---| | Async-pull flake fix | `67c150b` | `pull_failure_sets_error_status` swaps a fixed 5-update budget for a 5-second wall-clock deadline + `yield_now` between pumps. Closes the last v0.19-era flake. | | Smart window size opt-out | `e1b8766` | New `Settings::disable_smart_default_size: bool` (#[serde(default)]). `solitaire_app::main` reads the flag at startup and skips `apply_smart_default_window_size` registration when set. Settings → Gameplay row toggles it; tooltip notes saved geometry always wins. | | Shareable badge | `9b065e5` | Stats overlay's Latest-win caption gains `\u{2022} Shareable` when the displayed replay carries a `share_url`. Badge appears on the single-replay caption (no Prev/Next live spawn site yet). | | Help: M / P / Win-Summary-Enter | `35516d3` | Three rows added to F1 Help → Overlays. Closes coverage drift on post-v0.18 keys. | | F3 FPS overlay | `690e1d2` | `DiagnosticsHudPlugin` + `FrameTimeDiagnosticsPlugin`. Hidden by default; F3 toggle is not gated by pause/modal state. Smoothed FPS / frame_time. Anchored top-right at `Z_SPLASH + 100`. | | Android first APK | `fb8b2ac` | `solitaire_app` split into bin + lib (cdylib). `[package.metadata.android]` pins SDK 34 / 26. Bevy gains `android-native-activity`. `arboard` and `keyring`/`keyring-core` target-gated to non-Android. 54 MB APK builds; launch on device not yet verified. | | Android runbook | `59424a3` | Debian 13 toolchain install, `cargo apk build` invocation, post-sign panic workaround, wired-vs-stubbed table. Fresh-clone runnable. | ## Open punch list ### Carried forward from v0.19.0 - **App icon round.** `Window::icon` not yet wired; no `.icns` / `.ico` / Linux hicolor PNG hierarchy. The 11-size icon export the v0.19 handoff referenced is *not* currently in `artwork/` — re-running `artwork/Icon Export.html` is the prerequisite. Half-day task once the PNGs are back in place. No cert dependency. ### New — Phase Android (opened by `fb8b2ac` + `59424a3`) - **APK launch verification on AVD / device.** `adb install` then `adb logcat` against the bevy_test AVD. Shakes out any Bevy/winit Android-specific runtime bugs not caught by the build alone. - **`dirs::data_dir()` Android port.** Persistence (settings, stats, achievements, replays, progress) all route through this function. Android sandboxing usually demands `getFilesDir()` via JNI. Until verified, the APK may launch but fail to persist anything across cold starts. - **JNI ClipboardManager bridge.** Replaces the Android stub for the Stats "Copy share link" toast. `arboard` doesn't ship an Android backend, so this is a small custom JNI call. - **Android Keystore for credentials.** `keyring` is target-gated to a stub that returns `KeychainUnavailable`; replace with Android Keystore via JNI when sync auth ships on mobile. - **Google Play Games (gpgs) integration.** Listed in `solitaire_gpgs` as a Phase-Android target since Phase 1; now unblocked by the build target. - **Cosmetic `cargo apk build --lib` workaround.** The post-sign panic doesn't affect the APK on disk but produces noisy stderr. Either upstream a cargo-apk fix or document the `--lib` invocation as canonical in the runbook. ### Other small candidates - **Cut v0.20.0** — the seven commits are a coherent bundle (3 punch-list closes, 1 docs polish, 1 perf tool, 2 Android arc starters). Tag whenever feels right; the Android arc has more surface to land before it's "done", but the build-works state is a defensible release point. - **Prev/Next selector chips spawn site.** `9b065e5` notes Prev/Next markers exist in `stats_plugin` but no spawn site renders them today — the Shareable badge therefore lands on the single-replay caption. If/when Prev/Next is plumbed to the Stats overlay, the badge will need to follow. ### Process notes (from this round) - **Async-test starvation pattern (resolved twice now).** Auto-save flake (v0.19) and pull-failure flake (v0.20-candidates) shared the same root cause: a fixed N-update budget for an `AsyncComputeTaskPool` task to surface its result, which starves under cargo-test parallelism. The wall-clock-bounded loop pattern (`std::thread::yield_now()` between pumps, deadline = `Instant::now() + Duration::from_secs(5)`) is the canonical fix. Future async-test work should reach for this shape on the first commit, not after a flake materialises. - **Bin → lib + thin shim refactor.** The `solitaire_app` split for cargo-apk is a textbook cdylib-as-NativeActivity pattern. Worth knowing as a reusable shape for any future Bevy-on-mobile project: keep `main.rs` to ≤10 lines that delegate to `pub fn run`, put all setup in `lib.rs`, and let the build system pick which artifact (`bin` or `cdylib`) it needs. - **Target-gating defensive defaults.** Three of v0.20-candidates' Android-arc commits had to disable a default-on dependency (`arboard`, `keyring`, `keyring-core`) for the new target. When a future contributor adds a desktop-implicit dependency, reaching for `[target.'cfg(not(target_os = "android"))'.dependencies]` preemptively is cheaper than rediscovering the gating during the next platform port. ## Resume prompt ``` You are a senior Rust + Bevy developer working on Solitaire Quest. Working directory: . Branch: master. v0.19.0 is tagged on origin; seven commits sit on top opening the v0.20 cycle (3 punch-list closes + a perf tool + the Android build target). State: HEAD at 59424a3 + the v0.20-candidates docs commit on top (this session). Working tree may carry the doc updates if not yet committed. READ FIRST (in order, before doing anything): 1. SESSION_HANDOFF.md — this file 2. CHANGELOG.md — [Unreleased] holds the v0.20 draft 3. CLAUDE.md — unified-3.0 rule set 4. CLAUDE_SPEC.md — formal architecture spec 5. ARCHITECTURE.md — crate responsibilities + data flow 6. docs/android/* — Android setup + build runbook (59424a3) 7. ~/.claude/projects//memory/MEMORY.md — saved feedback / project context (machine-local; may be missing on a fresh machine) DECISION TO ASK THE PLAYER FIRST: A. APK launch verification — `adb install` + `adb logcat` on bevy_test AVD or a physical x86_64 device. Shakes out runtime bugs the build can't catch. B. Phase-Android persistence — port dirs::data_dir() through a JNI getFilesDir() bridge so the APK can survive a cold start. Largest unblocked Android piece. C. App icon — re-run artwork/Icon Export.html, then wire Window::icon + generate .icns / .ico. Half-day task. No cert dependency. D. Cut v0.20.0 — promote [Unreleased] to [0.20.0], tag, push. Mechanical close-out; the Android arc has more surface to land but the build-works state is a defensible release point. WORKFLOW NOTES: - Use the system git config (already correct). - When attributing playtester feedback in commits/docs, use "Quat" not "Rhys" (saved feedback memory). - Sub-agents stage + verify only; orchestrator commits. - Every commit must pass build / clippy / test before pushing. - Push to GitHub (origin) — gh auth setup-git wired on primary dev box; verify on laptop before first push. OPEN AT THE START: ask which of A–D. Don't pick unilaterally. ```