Files
Ferrous-Solitaire/SESSION_HANDOFF.md
T
funman300 2b1ad2161a test(e2e): add Playwright spec for /play Bevy canvas route
play_canvas.spec.js covers the window.__FERROUS_DEBUG__ bridge on the
/play route (five tests): bridge availability + seed param, draw3 URL
param, applyLegalMove/undo round-trip, failureReport schema, and
autonomous autoplay invariant batch across 7 seeds.

All tests drive exclusively through the debug bridge — no DOM selectors,
because the Bevy canvas is a single <canvas> element with no HTML
controls.

Also update SESSION_HANDOFF.md to reflect post-v0.35.1 work (10 commits
since 2026-05-18 handoff), new e2e architecture notes, and HiDPI fix doc.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 14:03:25 -07:00

8.3 KiB
Raw Blame History

Ferrous Solitaire — Session Handoff

Last updated: 2026-06-02 — Web e2e test suite complete; /play canvas bridge added and tested. All commits on origin/master.


Current state

  • HEAD: play_canvas.spec.js added (Playwright tests for /play Bevy canvas route)
  • Latest tag: v0.35.1
  • Working tree: clean
  • Build: cargo clippy --workspace -- -D warnings clean
  • Tests: 1243 Rust tests passing; Playwright suite in solitaire_server/e2e/

What shipped since the last handoff (v0.35.1 → present, 2026-06-02)

Commit Summary
64f975e 14 cross-platform UX/UI fixes from 500-game audit
763fdb4 Fix input: hit-test deck at correct position; accept waste click
1cdb78c cargo fmt; add analytics domain to CSP
baf524e Rebuild Bevy canvas WASM; add SolitaireGame interactive API
9ff0585 Remove Quaternions registry auth; canvas WASM drift guard
de7ae16 Delay first-run modal until splash screen despawns
8b736ca Debug drag failures (temp logging, removed in next commit)
8b262af Clamp wgpu surface to CSS pixels on HiDPI (prevented WASM panic)
d45b7cb Add Playwright e2e test suite for web routes
2cf7282 Add window.__FERROUS_DEBUG__ bridge to /play for automation

Key audit bugs fixed (all 7 from 500-game UX audit): timer-after-undo, radial-menu clamping, Android resume flash, tab-hidden timer, orphaned tmp files, drag threshold 4→6px, Draw-1 recycle doc comment.

HiDPI wgpu fix: WindowResolution::default().with_scale_factor_override(1.0) added to the Bevy canvas app. Root cause was physical pixels (CSS×DPR) exceeding WebGL2's 2048px per-dimension limit on HiDPI displays.

E2E test architecture: three-tier — Rust unit tests → Playwright smoke/review specs → cycle regression gate. Debug bridge contract in docs/testing-architecture.md.


What shipped before v0.35.1

See git log. CHANGELOG.md currently ends at v0.33.0 (documentation debt, low priority).


What shipped since the last handoff (v0.23.0 → v0.35.1)

v0.34.0 — Android polish + code-quality sweep (2026-05-16/17)

Commit Summary
9623bde Wire FiraMono to Android corner label; CardImageSet load tests
980312c Fix wrong bottom-right suit symbol on JS/QS/KS card assets
04e99a8 Correct Android waste fan overlap and resume layout desync
3bb3ddb Eliminate panics, fix dismiss hit-test scope, guard home respawn
f8f1f26 Adaptive drop zones, touch event correctness, modal lifecycle guards
1eb4043 Auth-guard avatar serving; atomic write; user_id assertion in merge
69c6e88 Deterministic pile serialization, undo skip, url-encode bytes, merge_at
aa7b0f6 Gate frame-hot ECS systems on resource changes (perf)
6727126 Consolidate APP_DIR_NAME; add #[must_use] on pure fns
a4dfb0c Differentiate leaderboard opt-in vs opt-out error toasts (M-12)
7fc98f8 WASM: state() and step() return Result, errors throw JS exceptions (CR-6)
ffed6b2 Share Tokio runtime across all network tasks (M-16)
fa84152 Correct Android help hint label to ! (M-17)
18d7937 Derive Copy for DrawMode; drop redundant .clone() calls (M-18)
132fea9 Use saturating_add for move_count increments (M-19)
0ecc1a9 Add missing derives to AchievementContext (M-20)
2301cc6 Align android_keystore temp extension with cleanup glob (M-21)
2e52f54 Enforce 32-char display_name limit at sync client boundary (M-22)
c8878d6 Fix stale FOCUS_RING colour comment (M-23)
4aafc0a Name HUD popover Z-layers; replace raw Z arithmetic (M-24)

v0.35.0 — Accessibility + sync reliability (2026-05-18)

Commit Summary
eb6c93f Silence B0004 by adding Transform to ModalScrim
6f5cebd Fire WarningToastEvent on sync pull failure (was InfoToastEvent)
87aec5b Gate all decorative motion animations under reduce_motion_mode

reduce_motion_mode now gates: score pulse, score floater, streak flourish (hud_plugin), card-shake on rejected move, foundation completion flourish (feedback_anim_plugin). Pattern: gate at the trigger/start system, never at the tick system — if the component isn't inserted, the tick path never runs.

v0.35.1 — Leaderboard bug fixes (2026-05-18)

Commit Summary
8f86d66 Fix three leaderboard bugs: wrong toast type, stale label, name not synced

Three bugs fixed:

  1. Wrong toast type on errorpoll_opt_in_task / poll_opt_out_task error branches now fire WarningToastEvent instead of InfoToastEvent.

  2. Display name not pushed to server on changeSettings gains leaderboard_opted_in: bool (serde-defaulted false). Set true/false when opt-in/out tasks succeed and persisted to settings.json. handle_display_name_confirm now spawns an opt_in_leaderboard task when already opted in — the server's upsert endpoint updates only display_name without re-opting-in.

  3. "Public name" label stale after name changeLeaderboardPublicNameText marker component added to the label node. update_leaderboard_public_name_label system rewrites the text each frame the panel is open; O(0) cost when panel is closed.

5 new regression tests cover all three bugs.


Open punch list

1. CHANGELOG documentation debt

CHANGELOG.md currently ends at v0.33.0. All post-v0.33.0 work is in git log. Low priority — git log is authoritative.

2. Android APK launch verification (Option A)

Physical device test: install the latest APK on a real Android device (not AVD), confirm:

  • App launches without crash
  • Safe area insets arrive and shift HUD correctly after ~3 frames
  • All modal Done buttons are above the gesture bar
  • Drag-and-drop works on all pile types
  • Leaderboard panel opens and the "Public name" label updates correctly after using "Set Name"

This has never been gated in CI. AVD adb shell input tap doesn't deliver real touch events, so physical-device smoke testing is the only gate.

3. Matomo analytics wiring

Settings has analytics_enabled: bool and matomo_url: Option<String> but no engine code consumes them — the analytics toggle in Settings is a no-op. If analytics are ever needed, the Matomo HTTP Tracking API client needs to be written and wired to GameStateResource events.


Architectural notes for next session

  • Reduce-motion pattern: always gate in the start_* / detect_* system (the trigger), not the tick_* system. If the component is never inserted, the tick path never runs. See hud_plugin.rs::detect_score_change and feedback_anim_plugin.rs::start_shake_anim for the canonical pattern.

  • Leaderboard server upsert: POST /api/leaderboard/opt-in is idempotent — calling it when already opted in just updates display_name. Safe to call from handle_display_name_confirm without tracking a separate "needs update" flag.

  • Messages<T> API (Bevy 0.18.1): write with resource_mut::<Messages<T>>().write(value); read in tests with msgs.get_cursor() + cursor.read(msgs).next().

  • Test input-state pitfall: MinimalPlugins has no input-tick system, so ButtonInput::just_pressed state persists across frames unless explicitly cleared with input.release(key); input.clear() between updates.

  • /play debug bridge design: play.html runs two independent WASM instances in Promise.all([bootstrap(), init()]). bootstrap() sets window.__FERROUS_DEBUG__ (logic layer via solitaire_wasm.js); init() starts the Bevy canvas. The bridge operates its own SolitaireGame — moves applied through the bridge do NOT affect the Bevy visual game. This is intentional for automation/invariant checking.

  • HiDPI Bevy canvas: WindowResolution::default().with_scale_factor_override(1.0) is set in the canvas app. Without this, physical pixels exceed WebGL2's 2048px limit on HiDPI displays, causing an immediate wgpu panic on the first resize event.

  • /play-classic vs /play in e2e: smoke.spec.js + gameplay_review.spec.js target /play-classic (DOM-heavy game.html); play_canvas.spec.js targets /play using only the __FERROUS_DEBUG__ bridge (no DOM selectors). cycle_metrics.js supports both via --route play-classic|play.