Files
Ferrous-Solitaire/SESSION_HANDOFF.md
T
funman300 73ac67d76b docs(handoff): record splash pulse + scanline; mark Option B closed
Bookkeeping pass after 29136d8 (cursor pulse) and a27cf5a (scanline
overlay) shipped — both halves of the splash polish arc deferred in
cacb19c are now done.

Changes:
- "Since the v0.20.0 cut": added per-commit narratives for 29136d8
  and a27cf5a with the implementation notes worth preserving past
  the commit log:
  - The "multiply, don't override" pattern that resolved the
    cursor-pulse / fade-timeline conflict (and generalises to any
    two ECS systems writing the same per-frame component).
  - The texture-α × tint-α GPU-composite trick that integrates the
    scanline with the fade without a new "multiplicative fadable"
    abstraction.
  - Two Bevy 0.18 API surprises (RenderAssetUsages module move;
    pixel_size() returning Result) — pinned for next time we touch
    runtime-generated images.
  - The defensive period <= 0.0 guard on cursor_pulse_factor — a
    cheap NaN-prevention pattern worth mirroring on every trig
    helper.
- "Open punch list" → "Visual-identity follow-ups": collapsed the
  two splash-polish bullets into closed pointers.
- Resume prompt → Option B: marked closed with "no further splash
  work pending unless a new mockup detail surfaces" so a future
  session knows it's a finished arc, not an in-flight one.

Three options now closed (A, B, C); D / E / F remain — all three
have a real blocker (D = multi-session, E = artwork PNGs missing,
F = Android hardware/AVD) so the next session starts with a
genuine commitment-vs-blocker decision rather than picking the
smallest piece.
2026-05-07 22:45:46 -07:00

557 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Solitaire Quest — Session Handoff
**Last updated:** 2026-05-07 — v0.20.0 cut and tagged at `41a009a`,
several post-cut commits sit on top of the tag (see `git log
--oneline origin/master..HEAD` for the live list), 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:** see `git rev-parse HEAD`. Most recent narrative
entry below names the latest substantive commit; this status line
intentionally avoids hard-coding the SHA so a docs-only edit
doesn't immediately stale the handoff.
- **HEAD on origin:** `41a009a` (the v0.20.0 cut). Local master is
ahead of origin by the post-cut commits enumerated under "Since
the v0.20.0 cut" below; run `git log --oneline origin/master..HEAD`
for the live count. 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".)
### `54005d5` `feat(engine): add GAME #YYYY-DDD caption beneath the replay headline`
Adds the right-anchored game-identifier piece of the replay-overlay
mockup, adapted to live *under* the existing "▌ replay" headline as
a `TYPE_CAPTION` (11 px) / `TEXT_SECONDARY` subtitle. Format is
`GAME #{year}-{ordinal:03}` (e.g. `GAME #2026-122` for a replay
recorded 2026-05-02) — year + chrono ordinal gives a compact,
monotonically-increasing identifier matching the mockup's
`GAME #2024-127` motif. New `ReplayOverlayGameCaption` marker, new
pure helper `format_game_caption(state) -> Option<String>` (None
for Inactive / Completed since the replay is consumed in those
branches; spawn-time fall-through to empty string).
**Layout impact:** `BANNER_HEIGHT` bumped 48 → 60 px so the new
left column (headline + 2 px gap + caption ≈ 39 px content) fits
under the scrub bar with room to spare. +12 px banner mass is the
deliberate cost of the new content; no other plugin observes
`BANNER_HEIGHT` so the change is local.
Two new tests (1180 → 1182): `format_game_caption_covers_state_corners`
pins the three branches plus the zero-pad-to-3-digits invariant
for early-January ordinals; `overlay_game_caption_shows_replay_date`
drives `ReplayPlaybackState` end-to-end.
### `e080b49` `feat(engine): restyle replay progress text as Terminal MOVE chip`
Closes the centre-text half of the replay-overlay enrichments. The
plain "Move N of M" text becomes a 1px `ACCENT_PRIMARY`-bordered
chip containing "MOVE N/M" — uppercase + slash separator reads as
a Terminal output line and matches the floating-chip motif in
`docs/ui-mockups/replay-overlay-mobile.html`. The chip lives
in-banner rather than floating above the focused card (the
screen-takeover treatment that requires plumbing cursor → card
identity remains deferred).
**Implementation note:** `BorderColor` in Bevy 0.18 is a per-side
struct, not a tuple — `BorderColor::all(ACCENT_PRIMARY)` is the
correct constructor. Worth pinning for next time we touch a
border-painted UI surface. The `ReplayOverlayProgressText` marker
stays on the inner Text rather than the new chip Node so
`update_progress_text` keeps repainting unchanged — a deliberate
"markers belong on the entity that updates change" choice.
Test count unchanged (1182); `overlay_progress_text_reflects_cursor`
swapped its assertion from "Move 5 of 10" to "MOVE 5/10".
This pair (`54005d5` + `e080b49`) closes Option C from the
SESSION_HANDOFF Resume prompt's banner-local enrichments. Floating-
chip-above-focused-card and the full screen-takeover redesign
remain — both data-layer or cross-plugin and intentionally still
open.
### `29136d8` `feat(engine): add pulsing trailing cursor to splash "▌ ready_" line`
Closes the cursor-pulse half of the splash polish arc deferred in
`cacb19c`. The "▌ ready_" line now ends with a 6×12 px cyan Node
that pulses on a 1 s sine cadence, multiplied with the global
splash fade timeline so the cursor never reaches full alpha while
the rest of the splash is still fading in.
**The "multiply, don't override" pattern.** Two systems write the
same `BackgroundColor` per frame: `advance_splash` writes the
global-fade alpha, `pulse_splash_cursor` overwrites with
`global_alpha × pulse_factor`. Both derive from `SplashAge` on the
root, so the writes are commensurate — the second one isn't
"fighting" the first, just refining it. This is the cleanest fix
for the "fight the global fade timeline" warning the original
`cacb19c` skip note flagged.
**Defensive division guard.** `cursor_pulse_factor(age, period, min)`
short-circuits to `1.0` when `period <= 0.0` so a future
misconfiguration produces a steady cursor rather than NaN
propagation (NaN in alpha = invisible UI, hard to debug). Worth
mirroring on every trig/division helper, not just this one.
One new test (1182 → 1183): `cursor_pulse_factor_corners` pins the
peak (factor = 1 at age = period / 4), trough (factor = min at age =
period × 3 / 4), and the zero/negative-period guard.
### `a27cf5a` `feat(engine): add tiled scanline overlay to splash`
Closes the scanline half of the splash polish arc. A fullscreen
`ImageNode` tiles a runtime-generated 2×2 RGBA8 texture over the
splash content — top row transparent, bottom row `#1a1a1a` at
~30 % alpha — producing the 1 px-pitch horizontal scanline pattern
called for in `docs/ui-mockups/splash-mobile.html`.
**Texture-α × tint-α composite for fade integration.** The 30 %
alpha is baked into the texture pixels, not the `ImageNode.color`
tint. `advance_splash`'s new third query writes
`(1, 1, 1, global_alpha)` into the tint each tick; the GPU
multiplies texture-α by tint-α, so the visible composite is
`0.3 × global_alpha`. Cleaner than building a "multiplicative
fadable" abstraction in the ECS — the GPU already does this
multiplication for free.
**Bevy 0.18 API surprises (worth pinning):**
- `RenderAssetUsages` re-exports under `bevy::asset::`, not
`bevy::render::render_asset::`. Type name unchanged; module
path moved.
- `TextureFormat::pixel_size()` returns `Result<usize, _>` rather
than the bare `usize` you'd expect for a static format query.
Annoying enough that the `debug_assert_eq!` against the buffer
length just hard-codes the `2 × 2 × 4 = 16` literal.
Headless test fixture now also `init_resource::<Assets<Image>>()`
since `MinimalPlugins` doesn't pull `AssetPlugin` — same pattern
`settings_plugin::tests` already used. Without it, the
`Option<ResMut<Assets<Image>>>` parameter on `spawn_splash` would
fall through and the scanline overlay would silently skip,
defeating the new tests.
Two new tests (1183 → 1185):
`build_scanline_image_has_expected_2x2_rgba_bytes` locks the
texture pixels literally so a future tweak can't drift the
appearance silently; `scanline_overlay_spawns_and_fades_with_splash`
asserts spawn placement under `SplashRoot` and the new
fade-images branch's correctness end-to-end.
This pair (`29136d8` + `a27cf5a`) closes Option B from the
SESSION_HANDOFF Resume prompt — both splash polish pieces now
shipped.
## 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 tokens** — `ui_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_COLOUR`
`STATE_WARNING`.
- **`card_plugin` chrome** (`d752870`). Drag-elevation shadow
routes through `CARD_SHADOW_*` tokens. `RIGHT_CLICK_HIGHLIGHT_COLOUR`
`STATE_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 — closed by `a27cf5a`.*
Runtime-generated 2 × 2 RGBA8 texture tiled via
`NodeImageMode::Tiled`; per-pixel alpha × tint alpha gives
multiplicative fade integration without new abstractions.
- *Splash cursor pulse — closed by `29136d8`.* Trailing 6 × 12 px
cyan Node, sine-pulsed, multiplied with the global splash fade
(the "multiply, don't override" pattern that resolves the
original `cacb19c` skip-rationale).
- **Replay-overlay enrichments beyond the scrub bar.** Banner-local
pieces of the mockup (`docs/ui-mockups/replay-overlay-mobile.html`)
all shipped: scrub bar (`c84d9f4`), `▌ replay` cursor-block label
(`6204db8`), `GAME #YYYY-DDD` caption (`54005d5`), `MOVE N/M`
chip restyle (`e080b49`). What's still open are the cross-plugin
/ data-layer pieces: a `MOVE N/M` chip *floating above the
focused card* during playback (would need to thread the cursor
through to the card layer — `update_progress_text` writes the
banner chip but the card-position lookup belongs in `card_plugin`).
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_DEFAULT`
`PILE_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 has unpushed post-cut commits**
— run `git log --oneline origin/master..HEAD` for the live list;
`git 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; several post-cut
commits sit on top locally and have NOT been pushed yet — run
`git log --oneline origin/master..HEAD` for the live list (current
substantives: 39b8496 desktop-adaptation spec, cacb19c splash
boot-screen port, c84d9f4 replay scrub-bar finish, 6204db8 replay
banner ▌ cursor-block label, plus any handoff edits since).
State: HEAD locally — see `git rev-parse HEAD`. Working tree is
clean. All workspace tests pass (~1180+; check with
`cargo test --workspace`), 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. *Closed by `29136d8` + `a27cf5a`.* Both splash polish
pieces shipped (cursor pulse + scanline overlay). No further
splash work pending unless a new mockup detail surfaces.
C. *Closed by `54005d5` + `e080b49`.* Banner-local replay-overlay
pieces all shipped (scrub bar, ▌ label, GAME caption, MOVE
chip). Remaining are cross-plugin (floating MOVE chip above
the focused card — needs cursor → card-position plumbing) or
multi-session (full screen-takeover redesign — move-log
scroll, mini tableau, WIN MOVE marker, data-layer impact).
Either belongs in its own decision tree the next time replay
work surfaces.
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.
```