Files
Ferrous-Solitaire/SESSION_HANDOFF.md
T
funman300 13ae16051d docs(handoff): cross-link skipped items + flag the ▌ replay.tsx deviation
Three small clarity touch-ups to SESSION_HANDOFF.md so a future-session
reader doesn't have to reconstruct intent from git log alone:

- The c84d9f4 narrative listed "header text treatment" as still open;
  6204db8 closed it the same session. Added a parenthetical pointer.
- The cacb19c "Skipped" sub-section now cross-links each item to its
  follow-up status in the punch list (scanline + cursor pulse → still
  open; "RUSTY SOLITAIRE" wordmark → closed, the in-engine wordmark
  stays "Solitaire Quest").
- 6204db8 adopted "▌ replay" instead of the mockup's literal
  "▌replay.tsx" — the .tsx was a Stitch/React prototyping leak.
  Documented the deviation alongside the existing RUSTY SOLITAIRE
  precedent so the in-engine string isn't second-guessed later.

Pure docs; no code changes, no test impact.
2026-05-07 22:07:36 -07:00

21 KiB
Raw Blame History

Solitaire Quest — Session Handoff

Last updated: 2026-05-07 — v0.20.0 cut and tagged at 41a009a, four post-cut commits sit on top of the tag, working tree is clean. The cut itself shipped two through-lines: a full Terminal visual- identity port (token system, modal scaffold, gameplay-feedback, toasts, table / card chrome, splash cursor) and the Android persistence shim that closes the dirs::data_dir() = None pitfall flagged in CLAUDE.md §10. Since the cut, four more pieces landed: the rules-based desktop-adaptation spec (closes the spec gap exposed when we noticed 23 of 24 mockups were mobile-only), the splash boot-screen port (full mockup-spec splash with header, boot log, progress bar, palette swatches, version footer + the SplashFadable scaffold refactor), the replay-overlay scrub bar (1 px cyan fill at the bottom of the banner, mirroring cursor / total), and the replay banner label port to the ▌ replay cursor-block treatment that aligns it with the splash boot-screen idiom.

Status at pause

  • HEAD locally: 6204db8 (replay banner label port).
  • HEAD on origin: 41a009a (the v0.20.0 cut). Local master is 4 commits ahead of origin39b8496, cacb19c, c84d9f4, and 6204db8 are not yet pushed. Decide whether to roll these into v0.20.1 / v0.21.0-candidates before pushing.
  • Working tree: clean. No WIP outstanding.
  • artwork/ directory: still untracked. Intentional.
  • Build: cargo clippy --workspace --all-targets -- -D warnings clean.
  • Tests: 1180 passing / 0 failing across the workspace. Up from 1176 at the v0.20.0 cut: the splash boot-screen port added two (splash_renders_terminal_boot_screen_content, fadables_start_transparent_and_reach_full_alpha) and the replay scrub-bar finish added two more (scrub_pct_covers_state_corners, overlay_scrub_fill_tracks_cursor).
  • Tags on origin: v0.9.0 through v0.20.0. v0.20.0 is on 41a009a.

Since the v0.20.0 cut (un-pushed)

39b8496 docs(ui): add Terminal desktop-adaptation spec

docs/ui-mockups/desktop-adaptation.md — 283 lines covering viewport assumptions, seven universal adaptation rules, and per- screen geometry rules for the priority surfaces (Game Table, Win Summary, Settings, Help, Pause, Home, Splash, Stats, and the modal-pattern screens Profile / Achievements / Theme Picker / Daily Challenge). Closes the spec gap — 23 of 24 mockups were mobile-only, but the v0.20.0 token-port pass was already layout- agnostic so nothing shipped broken. The spec matters for next ports.

Why rules > visual mockups for this gap: Stitch's generate_variants API timed out on the layout-only adaptation prompt (server-side flake, not a prompt-shape issue — confirmed by polling list_screens with no new variant landing). A markdown rules file applies to every screen including the 9 missing-plugin surfaces (splash, challenge, time-attack, weekly-goals, leaderboard, sync, level-up, replay-overlay, radial-menu) that aren't in the Stitch project at all. It's also referenceable from code comments and commit messages without loading an image.

cacb19c feat(engine): port the splash to the Terminal boot-screen treatment

Implements the full mockup-spec splash from docs/ui-mockups/splash-mobile.html plus the desktop adaptation rules:

  • Header: cursor block (96 px ), wordmark ("Solitaire Quest"), 192 px divider, "TERMINAL EDITION" subtitle.
  • Boot log: three ✓ check rows (assets loaded, theme: terminal, progress restored) + a ▌ ready_ line. Capped at 480 px width on desktop (else 70 % viewport).
  • Progress bar: 1 px track (BORDER_SUBTLE) with a 100 %- width cyan (ACCENT_PRIMARY) fill + DONE · 247 ASSETS caption. Capped at 720 px on desktop (else 80 %).
  • Footer: BASE16-EIGHTIES label, eight palette swatches (12 × 12 px each — one per named token in the design system), version line.

Refactored the alpha-fade scaffold from per-marker queries (SplashTitle / SplashSubtitle / SplashCursor) to a single SplashFadable { base_color: Color } + SplashFadableBg variant. ~15 fadable elements share one global query each; adding more is one component-attach, not three new query types.

Skipped, with rationale captured in the commit:

  • Scanline overlay (needs a tiled-pattern asset or custom shader). Open in "Visual-identity follow-ups" below.
  • Pulsing cursor on the "ready_" line (would fight the global fade timeline). Open in "Visual-identity follow-ups" below.
  • "RUSTY SOLITAIRE" wordmark from the mockup (the actual product is "Solitaire Quest"; the mockup leaked the repo name). Closed — the in-engine wordmark stays "Solitaire Quest".

c84d9f4 feat(engine): scrub fill bar + per-frame updater for replay overlay

Closes the WIP described in the prior handoff. Adds the 1 px cyan scrub bar called for in docs/ui-mockups/replay-overlay-mobile.html: a track in BORDER_SUBTLE spans the bottom edge of the banner and the cyan ACCENT_PRIMARY fill mirrors cursor / total via a new ReplayOverlayScrubFill component + update_scrub_fill system. The pure scrub_pct helper is shared between the spawn path (initial fill width) and the per-frame updater so the first paint already reflects state instead of popping 0 → cursor on the first tick — same shape as the existing format_progress / update_progress_text split. Two new tests cover the four corners of scrub_pct and an end-to-end drive of ReplayPlaybackState asserting Node.width on the unique scrub-fill entity. Same change-detection guard as the text updaters, so an idle replay leaves the node untouched.

Header text treatment (closed by 6204db8 immediately below), move-log scroll, MOVE chip, and WIN MOVE callout from the same mockup are still open — separate commits.

6204db8 feat(engine): port replay banner label to ▌ cursor-block treatment

Aligns the replay overlay's headline with the splash boot-screen idiom landed in cacb19c: Replay▌ replay and Replay complete▌ replay complete. The cursor block (, U+258C) prefixed to a lowercased label reads as a Terminal output line rather than a generic UI title, tightening the family resemblance between the two top-level overlay surfaces. Pure text-content change; no behavioural shift, no new components, no new systems.

Mockup deviation (intentional): the source mockup string in docs/ui-mockups/replay-overlay-mobile.html is ▌replay.tsx. The .tsx is a prototyping leak — Stitch renders in React, so the mockup author reached for a familiar filename — and was dropped for the in-engine version since the codebase is Rust. The + lowercase pattern is what reads as a Terminal-output-line; the extension is incidental. (Same shape as the "RUSTY SOLITAIRE" wordmark deviation noted under cacb19c — the mockup leaked the repo name; the actual product is "Solitaire Quest".)

What shipped in v0.20.0 (frozen at 41a009a)

Terminal visual-identity port

Top-down stack — every commit downstream of the token system reads from it, so swapping the palette is now a one-file edit:

  • ui_theme token system (0d477ac). base16-eighties palette, 5-rung type scale, 7-rung 4-multiple spacing scale, 3-step radius, 14-rung z-index hierarchy, full motion budget, 4 invariant-pinning unit tests. Card-shadow alphas pinned to 0 (Terminal achieves depth via 1px borders + tonal layering).
  • Modal scaffold already on tokensui_modal was ported in the same commit's wake; three stale "loud yellow" / "magenta secondary" doc comments fixed.
  • Gameplay feedback → semantic state tokens (ceec4fc). Selection / valid-drop tints route through ACCENT_PRIMARY / STATE_WARNING / STATE_SUCCESS.
  • Toasts (a137607). New ToastVariant enum (Info / Warning / Error / Celebration); opaque BG_ELEVATED
    • 1px accent border + bottom-anchor. All ten call sites pass their semantic variant.
  • table_plugin chrome (651f406). PILE_MARKER_DEFAULT_COLOUR promoted; cursor_plugin imports it, replacing a "kept in sync" doc comment with a compile- enforced invariant. HINT_PILE_HIGHLIGHT_COLOURSTATE_WARNING.
  • card_plugin chrome (d752870). Drag-elevation shadow routes through CARD_SHADOW_* tokens. RIGHT_CLICK_HIGHLIGHT_COLOURSTATE_SUCCESS. Stock recycle "↺" text → TEXT_PRIMARY @ 0.7α. Card-face / suit / card-back palette intentionally NOT migrated (artwork dependency — see open-list item below).
  • Splash cursor (cdcadda). The signature cyan glyph (96 px) added above the wordmark, matching the spec. Subsequently expanded post-cut by cacb19c into the full boot-screen treatment.
  • Hint-source / dest pairing (9891ae4). input_plugin's source-card tint now matches the destination pile's STATE_WARNING.
  • Design system + 24-mockup library (fa7f98a). docs/ui-mockups/design-system.md + 24 Stitch mockups (HTML + PNG) covering every screen plus 9 missing-plugin surfaces.
  • card_shadow_params test aligned (1d1543e). Drag-vs- idle shadow assertion loosened to >= to accept the Terminal "no shadow" intent without losing the regression-guard.

Android persistence

  • solitaire_data::data_dir shim (4b51e50). New solitaire_data::platform::data_dir() falls through to dirs::data_dir() on desktop and returns the per-app sandbox at /data/data/com.solitairequest.app/files on Android — no JNI needed (package id pinned in [package.metadata.android]). Six solitaire_data callsites + solitaire_engine/assets/user_dir.rs migrated. Settings, stats, achievements, replays, game-state, time-attack sessions, and user themes now persist on Android.

Inherited from earlier in the cycle (pre-session)

  • Android build target + APK (fb8b2ac), runbook (59424a3), F3 FPS overlay (690e1d2), Smart Window Size opt-out (e1b8766), Shareable badge (9b065e5), Help cheat-sheet M/P/Enter rows (35516d3), pull_failure_sets_error_status flake fix (67c150b).

Open punch list

Phase Android (build + persistence shipped; runtime gaps remain)

  • APK launch verification on AVD / device. adb install then adb logcat against the bevy_test AVD or an x86_64 device. The build works and persistence is wired, but no end-to-end device run has been logged. Shakes out runtime bugs the build + unit tests can't catch.
  • JNI ClipboardManager bridge. Replaces the Android stub for the Stats "Copy share link" toast. arboard doesn't ship an Android backend; small custom JNI call.
  • Android Keystore for credentials. keyring is target-gated to a stub returning KeychainUnavailable; replace with Android Keystore via JNI when sync auth ships on mobile.
  • Google Play Games (gpgs) integration. Listed as a Phase-Android target since Phase 1; now unblocked by the build target.
  • Cosmetic cargo apk build --lib workaround. Post-sign panic doesn't affect the APK on disk but produces noisy stderr. Either upstream a cargo-apk fix or document --lib as canonical in the runbook.

Visual-identity follow-ups (opened by v0.20.0's port)

  • Card-face / suit / card-back artwork regeneration. The Terminal spec calls for dark #1a1a1a cards with light suit pips (pink for hearts/diamonds, foreground gray for spades/ clubs); the runtime path still renders the legacy white-card PNG artwork. The fallback constants in card_plugin (CARD_FACE_COLOUR, RED_SUIT_COLOUR, BLACK_SUIT_COLOUR, CARD_FACE_COLOUR_RED_CBM, card_back_colour palette) are intentionally unmigrated and should swap in lockstep with the artwork. Largest visible payoff remaining in the visual- identity arc.
  • Splash boot-loader scanline overlay. cacb19c shipped the rest of the boot screen but skipped the scanline overlay (1px lines at 2 px pitch in #1a1a1a over the whole splash, 30 % opacity). Needs a tiled-pattern asset (a 2 × 2 px PNG) or a custom shader. Pure aesthetic, no behaviour change.
  • Splash cursor pulse. The "ready_" line's mockup pulses a cyan 6 × 12 px block at the end of the text. cacb19c skipped this because a per-element pulse fights the global SplashFadable fade timeline. Either layer the pulse on top of the fade (multiply alphas) or accept the static cursor.
  • Replay-overlay enrichments beyond the scrub bar. The scrub bar (c84d9f4) and the ▌ replay cursor-block label (6204db8) are the minimum of the mockup (docs/ui-mockups/replay-overlay-mobile.html). Tractable banner additions still open: a right-aligned GAME #N · MOVES caption (mirrors the ▌replay.tsx left-aligned filename motif), a MOVE 47/87 chip floating above the focused card during playback (would need to thread the cursor through to the card layer). The full mockup's screen-takeover treatment — mini- tableau preview, playback controls, move-log scroll, WIN MOVE marker on the scrub bar — is a multi-session redesign with data-layer impact (move-log scroller; the WIN MOVE marker needs a win_move_index field on Replay that doesn't yet exist). Banner-overlay behaviour is intentionally preserved for now.
  • Toast Warning / Error variants. The ToastVariant enum has slots for Warning (gold) and Error (pink) but no in-engine event uses them yet. Wire when a warning- or error- flavoured toast event materialises.

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/ (current artwork/ holds the reverted Rusty Pixel card PNGs and is intentionally untracked); icon-export needs to be re-run before this item can be picked up. Half-day task once the PNGs are back in place. No cert dependency.

Other small candidates

  • Prev/Next selector chips spawn site. v0.19.0's 9b065e5 noted 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, the badge will need to follow.
  • Toast queue / immediate unification. The two toast paths (spawn_queued_toast for InfoToastEvent queue; spawn_toast for fire-and-forget) now share visual treatment but remain separate functions because they serve different temporal needs (sequential vs. parallel). If overlap becomes a UX issue, merge into one queue with priority lanes.

Process notes

  • The desktop-adaptation spec is the canonical reference for geometry decisions when porting any future plugin. Read docs/ui-mockups/desktop-adaptation.md first; apply the universal rules to every surface; consult the per-screen table for the priority surfaces. The 9 missing-plugin screens (splash now ported; eight remaining) inherit the universal rules without dedicated guidance.
  • Stitch generate_variants is unreliable for layout-only adaptation prompts as of 2026-05-07. The first call timed out and no variant ever landed in list_screens. If a future session wants visual desktop mockups, prefer generate_screen_from_text with a fresh narrow prompt per screen rather than generate_variants against existing mobile screens.
  • Token-port pattern. v0.20.0's chrome-migration commits set a reusable shape for "centralised design system applied across N plugins":
    1. Constants module (ui_theme.rs) is the source of truth.
    2. Const sites that can't call Alpha::with_alpha (not yet const on stable) use a literal RGB matching the token, with a unit test pinning the RGB to the token (e.g. MARKER_VALID, HINT_PILE_HIGHLIGHT_COLOUR, RIGHT_CLICK_HIGHLIGHT_COLOUR).
    3. Cross-plugin duplication (e.g. MARKER_DEFAULTPILE_MARKER_DEFAULT_COLOUR) collapses to a single promoted const re-exported from one plugin and imported by the other — replaces "kept in sync" doc comments with a compile-time invariant.
    4. Domain colours (suit pips, card faces, lerp helpers) stay as literals with a comment naming the rationale; only UI chrome routes through tokens.
  • SplashFadable scaffolding pattern (introduced in cacb19c). Any future overlay that needs to fade N >> 3 elements together should follow the same shape: one tiny marker carrying the full-alpha base colour, one global query that lerps every marker's alpha each frame, no per-element query plumbing. Cleanly outscales the Without<X>, Without<Y> query exclusion pattern that the old splash was hitting at three siblings.

Canonical remote

github.com/funman300/Rusty_Solitaire is the canonical repo. Always push there. Local master is currently 4 commits ahead of origingit push is the next durability step (or roll the post-cut commits into v0.20.1).

Design direction (Terminal — base16-eighties)

  • Tone: retro-terminal / synthwave — flat depth (no box-shadows), monospaced-forward typography (JetBrains Mono / FiraMono), tight 16 px edge margins, 8 px card radius.
  • Palette: near-black surface ramp (#151515 / #202020 / #2a2a2a / #353535), cyan primary CTA (#6fc2ef), lime success (#acc267), gold warning (#ddb26f), pink error / suit-red (#fb9fb1), lavender celebration (#e1a3ee), teal info (#12cfc0).
  • Two-color suits. Red = #fb9fb1, black = #d0d0d0. Outlined glyphs for diamonds & clubs are always on; the Settings "color-blind mode" toggle only swaps red → cyan.

Resume prompt

You are a senior Rust + Bevy developer working on Solitaire Quest.
Working directory: <Rusty_Solitaire clone path on this machine>.
Branch: master. v0.20.0 is tagged at 41a009a; four post-cut commits
sit on top locally (39b8496 desktop-adaptation spec, cacb19c splash
boot-screen port, c84d9f4 replay scrub-bar finish, 6204db8 replay
banner ▌ cursor-block label) — these have NOT been pushed yet.

State: HEAD locally at 6204db8. Working tree is clean. 1180 tests
pass, clippy clean.

READ FIRST (in order, before doing anything):
  1. SESSION_HANDOFF.md  — this file
  2. CHANGELOG.md        — [0.20.0] section is the most recent cut
  3. CLAUDE.md           — unified-3.0 rule set
  4. CLAUDE_SPEC.md      — formal architecture spec
  5. ARCHITECTURE.md     — crate responsibilities + data flow
  6. docs/ui-mockups/    — design system + 24-mockup library +
                           desktop-adaptation.md (the rules-based
                           companion to the mockups; read this
                           before any plugin port)
  7. docs/android/*      — Android setup + build runbook
  8. ~/.claude/projects/<this-project>/memory/MEMORY.md
                         — saved feedback / project context
                           (machine-local; may be missing on a
                           fresh machine)

DECISION TO ASK THE PLAYER FIRST:
  A. Push the post-cut commits to origin. Either as-is on master
     or rolled into a v0.20.1 cut (CHANGELOG entry + tag).
     Mechanical, but local master diverges from origin until done.
  B. Splash polish — scanline overlay + cursor pulse. The two
     pieces of the mockup `cacb19c` skipped (scanline needs a
     tiled-pattern asset or shader; pulse needs to layer on top
     of the SplashFadable timeline). Pure polish; no behaviour
     change.
  C. Replay-overlay enrichments beyond the scrub bar. Tractable
     banner additions still open: a right-aligned
     `GAME #N · MOVES` caption, a `MOVE 47/87` chip floating
     above the focused card during playback. Stops short of the
     screen-takeover redesign (move-log scroll, mini tableau,
     WIN MOVE marker — multi-session, data-layer impact).
  D. Card-face artwork regeneration. Generate Terminal-aesthetic
     card PNGs (dark face, light suit pips), then migrate
     CARD_FACE_COLOUR / RED_SUIT_COLOUR / BLACK_SUIT_COLOUR /
     CARD_FACE_COLOUR_RED_CBM in lockstep. Largest visible
     payoff remaining in the visual-identity arc. Multi-session.
  E. App icon round — re-run artwork/Icon Export.html (the
     export PNGs are not currently in `artwork/`), then wire
     Window::icon + generate .icns / .ico. Half-day task. No
     cert dependency.
  F. APK launch verification on AVD / device + the JNI bridges
     it would shake out (ClipboardManager, Keystore).

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 AF. Don't pick unilaterally.