Closer/ClaudeGamesReview.md

126 lines
12 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.

# ClaudeGamesReview — Full Games, Mechanics, Retention & UI Review
> **Session 2026-07-07 (Claude, senior-dev review round).** Scope: all 7 games verified live on a
> fresh 2-emulator couple, mechanics checked against their real-world inspirations (researched),
> retention assessment, full-app UI warmth sweep, code-quality audit + fix batches.
> Bugs are cross-referenced by ID; canonical defect state lives in `ClaudeReport.md`.
> Test couple: **Ava + Ben** (`j39xR7PVZLLCveWnmenU`), created through the real signup → invite-code
> → recovery-phrase pairing flow on `CloserQA2` (5554, light) + `CloserCodexQA` (5556, dark).
> Premium was granted via admin (user-authorized), verified (partner unlock modal + push), then
> revoked (locks confirmed restored). 0 active sessions at session end.
## 1 · Per-game verdicts (mechanics fidelity + live results)
| Game | Real-world analog | Fidelity | Live 2-device result |
|---|---|---|---|
| This or That | Rapid-fire preference quiz | ✅ Faithful | **PASS** — engineered answers → exactly 3/5 "in sync"; simultaneous symmetric reveal; warm framing ("Lots in common 💛", neutral "Differ" chips) |
| How Well Do You Know Me | The Newlywed Game | ✅ Core faithful; no round-2 role swap (see R-2) | **PASS** — subject/guesser roles correct on both intros; engineered 2/5 exact; near-miss scale renders amber "4 → 3" (isClose softening); low score framed "Getting there!" |
| Desire Sync | Mojo Upgrade-style mutual-desire matching | ✅ Canonical privacy contract, verbatim in intro copy | **PASS** — engineered intersection → exactly "2 shared desires / 3 stayed private"; non-mutual yeses never rendered on either device; count doesn't leak whose/which; answers `enc:v1:` at rest |
| Spin the Wheel | Conversation-deck wheel | ✅ Honest randomness (category picked during spin; decorative stop angle) | **PASS** — identical 10-question set both sides; blind until both answered; side-by-side reveal; completion gate blocks finish with unanswered prompts |
| Connection Challenges | 7-day couples habit challenge | ✅ Day-gating real (can't complete 2 days in one day) | **PASS** — premium challenge opened with entitlement; day-1 complete → "waiting for partner"; ⚠ day-2 prompt text visible a day early (spoiler, see U-13) |
| Memory Lane | Time capsule / letter to future selves | ✅ | **PASS** — sealed capsule shows only title + "Opens in 29 days"; title AND body `enc:v1:` at rest (admin-verified) |
| Date Match | Tinder swipe-to-match | ✅ incl. one-sided-like privacy | **PASS** — simultaneous mutual-LOVE race → exactly **1** match doc; celebration modal + "It's a match!" push on BOTH devices |
**Shared engine seams, verified live:** one-active-game gate converges the second starter into the
active session (Ben's How Well attempt → "Ava is playing a Wheel game / Join the game");
first-finisher `partner_completed_part` fires as an in-app banner (foreground) and real push
(killed app, cold-start tap opened the app cleanly, 0 FATAL); mid-game Quit abandons correctly
(admin: 0 active sessions after); "you both finished" results banner on both sides.
## 2 · Bugs found (canonical detail in ClaudeReport.md)
| ID | Sev | Summary | Status |
|---|---|---|---|
| DQ-MISMATCH-001 | **P1** | Partners can resolve **different daily questions** for the same day: server assignment is the unresolvable `q_default_daily` rollout fallback → each device falls back locally, and the fallback **pool depends on the viewer's premium state at that moment** (per-user entitlement, transient failures→false, mid-day flips). Reveal then cross-compares answers to different questions. Reproduced live (Ava: premium pool "tiny win" q; Ben: free pool "tiny debate" q; same clock/tz). | **FIXED** (paired fallback pinned to free pool — premium-independent) + follow-up filed to seed real server assignments |
| DQ-SLUG-001 | **P1** | Daily reveal renders the partner's answer as a raw option-id slug ("fake_awards_should_be_mandatory") when the id isn't in this device's question config (same class as HW-BREAKDOWN-001). | **FIXED** (humanize fallback in both reveal summaries) |
| GAME-HANG-001 (B1) | **P2** | ToT/HowWell/DesireSync answer listeners swallowed snapshot errors → game stuck on WAITING forever, no feedback (Wheel/Capsule already had the correct `close(err)` pattern). | **FIXED** (sources `close(err)` + VM `.catch` → retryable ERROR with "Try again" re-attach) |
| DST-001 (B2) | **P2** | `assignDailyQuestion.ts` hardcoded `CST_OFFSET_HOURS=-6`: wrong date labels + reveal times shifted 1h for the ~8 CDT months. | **FIXED** (Intl/America-Chicago helpers, matches streakReminder.ts pattern; 5 DST regression tests incl. spring-forward round-trip) |
| A-003b | P3 | Challenges catalog still shows 🔒 Premium badge after couple premium unlock (gate itself honors premium — opens fine). | Filed |
| BANNER-LIFE-001 | P3 | "You both finished · View" game banner persists across screens/games long after results were viewed (still up entering a NEW game's setup, and shown on top of the results screen it links to). | Filed |
| AUTH-TRUNC-001 | P3 | Welcome/splash privacy tagline clips mid-sentence without ellipsis ("Only you and your paired") — first-run first impression. | Filed |
| Copy/theming P3s | P3 | "Daily Fun Mc" internal category name as user-facing pill; unthemed teal date picker; DOB asked twice in signup→profile funnel; "It is a match!" vs push "It's a match!"; HW matched-row question text low contrast in dark; Date Match card-underlay text bleed; joiner screen titled "Waiting for X" when it's your turn. | Filed |
## 3 · Retention assessment (Hooked / Fogg / SDT lens)
**Strong foundations:** daily-question blind-reveal is a genuine daily anchor with a variable
reward (partner's answer) · streaks with repair + grace (aligned with Duolingo's leniency findings)
· Memory Lane = stored-value investment · Weekly Recap + Your Progress = progress evidence ·
first-finisher nudges close the async loop (verified: banner + push both fire) · notification suite
respects quiet hours.
**Prioritized recommendations:**
- **R-1 (P1): Fix the daily anchor supply chain.** `q_default_daily` fallback means the server
never really assigns; seed the Firestore `questions` pool (or make the callable assign a real id
from a synced catalog) so both devices always agree. (Client-side consistency fix landed this
session; assignment seeding is the durable fix.)
- **R-2 (P2): Role-swap rematch in How Well.** Newlywed Game's round structure swaps roles; the
results screen should offer "Now you guess — swap roles" (starter becomes guesser). Cheap, doubles
sessions per sitting, and answers "what do we do next?" at the emotional peak.
- **R-3 (P2): Return-tomorrow beat on results screens.** After every reveal the only CTAs are
"Play again / Back to Play". Add the next anchor: tomorrow's question teaser, challenge day N+1,
or streak state ("Day 2 tomorrow — keep it alive").
- **R-4 (P2): Repeat-avoidance for game pools.** Desire Sync's effective pool is 102 binary items
drawn `shuffled().take(n)` per session (no cross-session memory) and Date Match's deck is
deterministic (identical first card for every couple, every install). Persist seen-ids per couple.
- **R-5 (P3): Purify the How Well pool.** `getQuestionsForPrediction()` pulls all choice/scale
questions including 500 `daily_fun_mc` novelty MCs ("laugh-track move") — dilutes the
partner-knowledge premise. Exclude `daily_fun_mc` (or curate a prediction-worthy tag).
- **R-6 (P3): Challenges — hide tomorrow's prompt** (spoiler kills the daily ritual) and consider a
visible 7-day chain with both partners' marks (social accountability).
- **R-7 (P3): Analytics funnel gaps.** `RetentionEvent` has GameStarted/GameCompleted but no
first-answer, reveal-viewed, waiting-abandoned, or notification-tap-attribution events — the async
dead-end (biggest churn risk) is unmeasurable today.
- **Content notes:** 48/150 sexual_preferences items unreachable by Desire Sync (non-binary configs
— fine, Wheel uses them) · `daily_fun_mc` rows carry `sex='neutral'` (currently benign only because
of the binary filter; retag to avoid a future landmine) · This or That mood pools healthy (6075
per mood vs 15 max draw); `rebuilding_trust` (45 q) reachable only via "All topics".
## 4 · UI warmth sweep (vs docs/copy-guide.md)
Scores 15 per cluster (tone / emotional safety / welcome / consistency):
- **Games (all 7): 5/5//4.** Best-in-class results framing (no shaming anywhere: "Getting
there!", "Differ" not ✗, "Nothing in common this round" never blames). Consistency dinged for the
banner lifecycle + slug leak (fixed) + per-game accent divergence (DS magenta is intentional and
works).
- **Onboarding/pairing: 4//5/3.** "You're in / Now bring your person in", value-prop checklist,
recovery-phrase ceremony with word-confirmation are excellent. Dinged: tagline truncation, DOB
asked twice, unthemed date picker, dead-end Create-Profile when signed out.
- **Home/Today: 4/5/5/3.** Daily card + mode system (Tiny Win Tuesday live-verified) is a strong
anchor; "Daily Fun Mc" pill leak; Home-card question can disagree with Today tab (DQ-MISMATCH).
- **Empty states: 5.** Memory Lane's "No capsules yet" is the pattern to copy everywhere.
- Both themes render cleanly on every screen driven this session (fresh-couple run covered light
on 5554 + dark on 5556 throughout); HW matched-row contrast is the one dark-mode ding.
## 5 · Code-quality fixes landed this session
- **Batch 1 (correctness):** B1 observer `close(err)` ×3 + VM `.catch`→retryable ERROR ×3 (+
`GameCopy.SYNC_ERROR`); DQ paired-fallback premium-independence; reveal slug humanization ×2;
DST-safe Chicago date helpers + 5 regression tests.
- **Batch 2 (dead code / hygiene):** see ClaudeReport.md fix log.
- **Filed (structural, specced in Future.md):** generic session-game engine (ToT/HW/DS VMs are
~80% clones; ~250-line state machine ×4), generic replay VM/screen (3× ~60-line clones), shared
waiting/length-picker composables, premium-check invocation helper, `GameConstants` for divergent
`ADVANCE_DELAY_MS` (420/380), dispatcher injection, error-surfacing standardization for 6
silent-load VMs, `npm audit` 10 moderate (GCP transitive).
## 5.1 · R30 polish + experience follow-up (2026-07-07)
All Tier-A P3s and Tier-B UX gaps from the review shipped and were live-verified (detail: ClaudeReport R30). Highlights that change the player experience:
- **Daily question is fixed at the root.** The server pool was empty (every couple got the unresolvable `q_default_daily`); now Firestore `questions` is seeded with the free weekday pool and the server picks the same mode-aware, deterministic question the client would — so both partners get the identical themed question, even across time zones. Verified: server assigns `daily_single_choice_weekly_v1_072` (Tiny Win Tuesday), both devices resolve it, no slugs.
- **Games no longer dead-end:** NextBeatCard ("Tonight's question is ready →" / streak) under every results screen; How Well offers a Newlywed-style role swap.
- **Content freshness:** Desire Sync remembers served questions; Date Match shuffles per couple and skips already-swiped ideas (no more identical first card for everyone).
- **Waiting isn't passive:** a "Send a little nudge 💜" button on the waiting screen (reuses the quiet-hours-safe thinking-of-you callable).
- **Onboarding polish:** themed M3 date pickers; the birth date now survives an app restart mid-signup (no more double-ask).
- **A11y + copy:** score visuals carry TalkBack descriptions; banners speak per-game ("guess their answers"); the internal "Daily Fun Mc" label never faces users.
## 6 · Coverage notes
- Verified live: all 7 games happy path 2-device · result extremes (0-match impossible for DS
reveal? engineered 3/5, 2/5, 2-shared, 3/5-sync variants) · one-active gate · abandon · cold-start
push join · simultaneous-LOVE race · premium grant/unlock/revoke lifecycle · daily answer→reveal ·
day-0 check-in · pairing + recovery-phrase ceremony · deep-link scheme audit (only /join/ links
exist; game entries are notification-driven, all tested).
- Deferred (unchanged from plan): full Pass E matrix, Pass K money path, release build, monkey,
TalkBack full pass (spot: n/a this round), TodayWidget interactive smoke (code-read only),
am-kill mid-session resume (cold-start-from-push covered the recovery class).