docs(qa): R30 polish/UX/seed round — reports, coverage, review §5.1
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
ca67187754
commit
15e4579410
|
|
@ -103,6 +103,16 @@ Scores 1–5 per cluster (tone / emotional safety / welcome / consistency):
|
||||||
`ADVANCE_DELAY_MS` (420/380), dispatcher injection, error-surfacing standardization for 6
|
`ADVANCE_DELAY_MS` (420/380), dispatcher injection, error-surfacing standardization for 6
|
||||||
silent-load VMs, `npm audit` 10 moderate (GCP transitive).
|
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
|
## 6 · Coverage notes
|
||||||
|
|
||||||
- Verified live: all 7 games happy path 2-device · result extremes (0-match impossible for DS
|
- Verified live: all 7 games happy path 2-device · result extremes (0-match impossible for DS
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
> **Resume anchor — current status only.** Statuses: `pass | fail→id | todo | n/a | not implemented→Future.md | blocked→id`.
|
> **Resume anchor — current status only.** Statuses: `pass | fail→id | todo | n/a | not implemented→Future.md | blocked→id`.
|
||||||
|
|
||||||
|
> **R30 (2026-07-07, polish + UX + seed):** all remaining P3s + 8 UX gaps FIXED & live-verified (see ClaudeReport R30 / ClaudeGamesReview §5). **Daily-question pool SEEDED + server made mode-aware/deterministic + deployed** — both devices resolve the same real server-pinned question (DQ-MISMATCH/DQ-SLUG closed at the root, verified live). Gates: unit pass · functions 58 · cold-start smoke run. Still deferred: TodayWidget interactive, TalkBack full pass, release/minify (O), device/OS matrix.
|
||||||
|
|
||||||
> **R29 (2026-07-07, games/mechanics/retention/UI review):** Pass B (all 7 games) **re-run live 2-device on a fresh couple = pass** (engineered-answer verification; see ClaudeGamesReview.md) · onboarding/signup/pairing/recovery-ceremony/day-0 check-in **pass (live, first full run since R24)** · daily answer→reveal loop **fail→DQ-MISMATCH-001/DQ-SLUG-001 → FIXED e5868bd, re-verified** · premium grant/unlock/revoke lifecycle **pass** · cold-start push open **pass (organic, killed app)** · one-active gate/abandon/simultaneous-match race **pass** · TodayWidget interactive + TalkBack spot + am-kill mid-session resume **todo (deferred; cold-start-from-push covered the recovery class)** · Pass D at-rest spot-check (game answers + capsule on new couple) **pass (`enc:v1:`)**.
|
> **R29 (2026-07-07, games/mechanics/retention/UI review):** Pass B (all 7 games) **re-run live 2-device on a fresh couple = pass** (engineered-answer verification; see ClaudeGamesReview.md) · onboarding/signup/pairing/recovery-ceremony/day-0 check-in **pass (live, first full run since R24)** · daily answer→reveal loop **fail→DQ-MISMATCH-001/DQ-SLUG-001 → FIXED e5868bd, re-verified** · premium grant/unlock/revoke lifecycle **pass** · cold-start push open **pass (organic, killed app)** · one-active gate/abandon/simultaneous-match race **pass** · TodayWidget interactive + TalkBack spot + am-kill mid-session resume **todo (deferred; cold-start-from-push covered the recovery class)** · Pass D at-rest spot-check (game answers + capsule on new couple) **pass (`enc:v1:`)**.
|
||||||
> **R28 (2026-07-02) — closed R27's two gaps: FIXED HW-BREAKDOWN-001 (P3) + ran the 3 premium games live 2-device under an explicitly-authorized admin grant, then revoked. 0 P0/P1/P2, 0 FATAL.** **HW-BREAKDOWN-001 (P3) FIXED** — added `humanizeOptionId()` (`_`→space + Title-case) to all 3 fallback branches of `HowWellAnswer.displayText()` in `ui/howwell/HowWellScreen.kt` (proper `config` labels still win); built + installed both emulators → archived. **Premium grant (user-authorized this occurrence) → couple-shared unlock re-confirmed:** QA `entitlements/premium`=true (source `qa_admin`) unlocked BOTH — Sam's free Play hub dropped the 🔒 + QA got the one-time **"Premium unlocked ✨ You both have Premium now"** modal. **Pass B premium games — all 3 PASS live 2-device:** **Desire Sync** (Sam Y·Y·Y·Y·Y / QA Y·Y·Y·N·N → both devices show identical **"3 shared desires / 2 stayed private"** = the 3 mutual-YES only; You/partner both "Private"; Sam waiting-screen auto-flipped + fired "You both finished — View" banner) · **Memory Lane** (create+seal capsule → list shows **title-only under lock**, **sealed body does NOT leak**; clears up long-title pre-existing rows) · **Date Match** (mutual like on "Sunrise hike + thermos coffee" → **"Matched"** in couple-shared "Your Matches" **on both devices**; premium-tagged idea "Overnight camping getaway" swipeable/matched → A-201 gate lifts under premium). **Premium REVOKED after testing** (admin hasPremium/isActive/premium=false + `revokedAt`) — verified at DB (all-true→all-false) **and live in UI** (Memory Lane + Past Games 🔒 **Premium** again; free fixture restored). **Copy fix BANNER-RESULTS-COPY-001 (P4 cosmetic) — FIXED:** foreground game-results banner "See how you and **Your partner** compare" (capitalized generic fallback awkward mid-sentence) → `GamePromptBanner.styleFor()` RESULTS line now name-branches ("See how you and Sam compare" / name-free **"See how you both compare"**); compiles + installed both. **O-AGE-001 (P2) — 18+ age gate IMPLEMENTED + live-verified (throwaway 5558):** `AgeGate`(18+)+`User.birthDate`+datasource(read/create/`updateBirthDate`)+`firestore.rules` update-allowlist `+birthDate`+`SignupHandoff`; sign-up **Date of birth** picker validated **before account creation** (under-age → no account) + conditional DOB step in CreateProfile for Google/legacy (skipped for email via handoff). Live: DOB-required error ✓; **age 17 → "You must be at least 18 to use Closer." + no account** ✓; adult → account created → CreateProfile NAME step (DOB skipped, Step 1/3) ✓; profile save succeeds ✓. **Landmine caught live:** birthDate on the *update* path hit PERMISSION_DENIED (rules allowlist undeployed) → broke profile save → fixed by making the write **best-effort** (`runCatching`). **birthDate persistence: `firestore:rules` DEPLOYED by user + verified against the live rule** (authed update PATCH `{birthDate,lastActiveAt}` → 200 ALLOWED + persisted; non-allowlisted field → 403, so nothing weakened). Unit suite **279 green** (+8 AgeGate). **0 FATAL, both emulators.**
|
> **R28 (2026-07-02) — closed R27's two gaps: FIXED HW-BREAKDOWN-001 (P3) + ran the 3 premium games live 2-device under an explicitly-authorized admin grant, then revoked. 0 P0/P1/P2, 0 FATAL.** **HW-BREAKDOWN-001 (P3) FIXED** — added `humanizeOptionId()` (`_`→space + Title-case) to all 3 fallback branches of `HowWellAnswer.displayText()` in `ui/howwell/HowWellScreen.kt` (proper `config` labels still win); built + installed both emulators → archived. **Premium grant (user-authorized this occurrence) → couple-shared unlock re-confirmed:** QA `entitlements/premium`=true (source `qa_admin`) unlocked BOTH — Sam's free Play hub dropped the 🔒 + QA got the one-time **"Premium unlocked ✨ You both have Premium now"** modal. **Pass B premium games — all 3 PASS live 2-device:** **Desire Sync** (Sam Y·Y·Y·Y·Y / QA Y·Y·Y·N·N → both devices show identical **"3 shared desires / 2 stayed private"** = the 3 mutual-YES only; You/partner both "Private"; Sam waiting-screen auto-flipped + fired "You both finished — View" banner) · **Memory Lane** (create+seal capsule → list shows **title-only under lock**, **sealed body does NOT leak**; clears up long-title pre-existing rows) · **Date Match** (mutual like on "Sunrise hike + thermos coffee" → **"Matched"** in couple-shared "Your Matches" **on both devices**; premium-tagged idea "Overnight camping getaway" swipeable/matched → A-201 gate lifts under premium). **Premium REVOKED after testing** (admin hasPremium/isActive/premium=false + `revokedAt`) — verified at DB (all-true→all-false) **and live in UI** (Memory Lane + Past Games 🔒 **Premium** again; free fixture restored). **Copy fix BANNER-RESULTS-COPY-001 (P4 cosmetic) — FIXED:** foreground game-results banner "See how you and **Your partner** compare" (capitalized generic fallback awkward mid-sentence) → `GamePromptBanner.styleFor()` RESULTS line now name-branches ("See how you and Sam compare" / name-free **"See how you both compare"**); compiles + installed both. **O-AGE-001 (P2) — 18+ age gate IMPLEMENTED + live-verified (throwaway 5558):** `AgeGate`(18+)+`User.birthDate`+datasource(read/create/`updateBirthDate`)+`firestore.rules` update-allowlist `+birthDate`+`SignupHandoff`; sign-up **Date of birth** picker validated **before account creation** (under-age → no account) + conditional DOB step in CreateProfile for Google/legacy (skipped for email via handoff). Live: DOB-required error ✓; **age 17 → "You must be at least 18 to use Closer." + no account** ✓; adult → account created → CreateProfile NAME step (DOB skipped, Step 1/3) ✓; profile save succeeds ✓. **Landmine caught live:** birthDate on the *update* path hit PERMISSION_DENIED (rules allowlist undeployed) → broke profile save → fixed by making the write **best-effort** (`runCatching`). **birthDate persistence: `firestore:rules` DEPLOYED by user + verified against the live rule** (authed update PATCH `{birthDate,lastActiveAt}` → 200 ALLOWED + persisted; non-allowlisted field → 403, so nothing weakened). Unit suite **279 green** (+8 AgeGate). **0 FATAL, both emulators.**
|
||||||
> **R27 (2026-07-02) — full-plan COMPLETION (live-ran the passes R26 carried): P·I·J·F·G·H + Pass-B free games; 1 new P3 (HW-BREAKDOWN-001).** Same build as R26 (UI-only; app unchanged, only doc edits). **Pass P** question-bank PASS (6103 Qs: 0 empty/dupe/placeholder; choice/scale answer-configs all present; daily pack 500 intact; 22 categories; **Room identity hash `7e7d78…` preserved**). **Pass I** perf PASS (core-tabs **6.67% janky**/90th 31ms/0 missed-vsync; conversation scroll **3.04%**/90th 19ms — smoother than R8). **Pass J** a11y PASS (font_scale 2.0 → Home/Play/Settings reflow, scroll, no hidden critical actions [nav-label wrap known-acceptable]; reduce-motion no hang; **TalkBack 160/160 `Icon()` have contentDescription**; touch-targets carried [Batch-8 48dp]). **Pass F** resilience PASS (Messages **renders from cache in airplane-mode**, 0 FATAL, no dead-end; **portrait-lock holds** `requestedOrientation=PORTRAIT` under forced landscape; process-death via 6/6 smoke; concurrency carried). **Pass G/D3** security PASS — **live raw-API negative:** non-member ID token → couple doc / messages / daily answers / **date_reflections** / partner user-doc = **403 all**; **self-grant own premium = 403**; own-doc = 404 (valid auth, rules are the gate). **Pass H** branding PASS (all driven screens on-brand; 2 P3 backlogs carried). **Pass B free games (live 2-device this session):** This-or-That 5/5 (R26) · **How Well** answered→predicted→**"2 of 3"** with correct scale/choice breakdown · **Connection Challenges** resume→Day6 complete→advance Day7→mutual per-day gate ("waiting for partner"), streak/missed-day recovery · **Spin-the-Wheel** spin/category/session/written+choice answer/cap/quit (full 10-Q completion carries R18b). **NEW FINDING HW-BREAKDOWN-001 (P3):** How Well results breakdown renders a wrong *choice*-guess as its **raw option ID** (`a_small_romantic_surprise`) instead of the human label (correct answer resolves to text) — cosmetic ID-leak, untouched feature. **Pass B premium games (Desire Sync · Memory Lane · Date Match):** paywall **GATE verified** (all → Paywall for free: Desire Sync [Pass A], Date Match [free-swipe→paywall, A-201 holds], Memory Lane [premium-badged]); **GAMEPLAY `blocked→premium-grant-authorization`** (admin grant declined by auto-mode; gameplay carries R12/R14). **K/O `blocked→needs-device`/pre-ship** (unchanged). **0 FATAL, 0 P0/P1/P2; 1 new P3.**
|
> **R27 (2026-07-02) — full-plan COMPLETION (live-ran the passes R26 carried): P·I·J·F·G·H + Pass-B free games; 1 new P3 (HW-BREAKDOWN-001).** Same build as R26 (UI-only; app unchanged, only doc edits). **Pass P** question-bank PASS (6103 Qs: 0 empty/dupe/placeholder; choice/scale answer-configs all present; daily pack 500 intact; 22 categories; **Room identity hash `7e7d78…` preserved**). **Pass I** perf PASS (core-tabs **6.67% janky**/90th 31ms/0 missed-vsync; conversation scroll **3.04%**/90th 19ms — smoother than R8). **Pass J** a11y PASS (font_scale 2.0 → Home/Play/Settings reflow, scroll, no hidden critical actions [nav-label wrap known-acceptable]; reduce-motion no hang; **TalkBack 160/160 `Icon()` have contentDescription**; touch-targets carried [Batch-8 48dp]). **Pass F** resilience PASS (Messages **renders from cache in airplane-mode**, 0 FATAL, no dead-end; **portrait-lock holds** `requestedOrientation=PORTRAIT` under forced landscape; process-death via 6/6 smoke; concurrency carried). **Pass G/D3** security PASS — **live raw-API negative:** non-member ID token → couple doc / messages / daily answers / **date_reflections** / partner user-doc = **403 all**; **self-grant own premium = 403**; own-doc = 404 (valid auth, rules are the gate). **Pass H** branding PASS (all driven screens on-brand; 2 P3 backlogs carried). **Pass B free games (live 2-device this session):** This-or-That 5/5 (R26) · **How Well** answered→predicted→**"2 of 3"** with correct scale/choice breakdown · **Connection Challenges** resume→Day6 complete→advance Day7→mutual per-day gate ("waiting for partner"), streak/missed-day recovery · **Spin-the-Wheel** spin/category/session/written+choice answer/cap/quit (full 10-Q completion carries R18b). **NEW FINDING HW-BREAKDOWN-001 (P3):** How Well results breakdown renders a wrong *choice*-guess as its **raw option ID** (`a_small_romantic_surprise`) instead of the human label (correct answer resolves to text) — cosmetic ID-leak, untouched feature. **Pass B premium games (Desire Sync · Memory Lane · Date Match):** paywall **GATE verified** (all → Paywall for free: Desire Sync [Pass A], Date Match [free-swipe→paywall, A-201 holds], Memory Lane [premium-badged]); **GAMEPLAY `blocked→premium-grant-authorization`** (admin grant declined by auto-mode; gameplay carries R12/R14). **K/O `blocked→needs-device`/pre-ship** (unchanged). **0 FATAL, 0 P0/P1/P2; 1 new P3.**
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,15 @@
|
||||||
# Claude QA Report — Full-App QA (living report)
|
# Claude QA Report — Full-App QA (living report)
|
||||||
|
|
||||||
|
> **Verdict (2026-07-07): R30 = POLISH & EXPERIENCE round on top of R29 — closed all remaining P3s + 8 UX gaps + SEEDED the daily-question server pool (R29's R-1). 8 commits, functions deployed, all live-verified on the Ava+Ben pair.**
|
||||||
|
> **Batches (each built + installed + live-verified + committed):**
|
||||||
|
> **C1 defect-polish (951ce87):** A-003b Challenges 🔒-badge-after-unlock (verified via grant/revoke) · BANNER-LIFE-001 results banner now consumed on the session's screen + replaced across sessions · joiner "Your turn — {name} already played their part" copy · How Well matched-row dark-mode contrast (theme-aware pair) · Date Match top-card opaque (no underlay bleed) · **+found live:** false "finished — see results" push on abandoned sessions → guarded on completedByUsers≥2.
|
||||||
|
> **C2 funnel+gating (258674f):** both DOB pickers → shared Compose M3 DobPickerDialog (verified purple in dark) · SignupHandoff → DataStore per-uid so **DOB survives process death** (verified: am-kill mid-profile → no re-ask) · Challenges day-N+1 spoiler hidden behind a "unlocks tomorrow 🌙" teaser (statusDay exposed + 2 tests).
|
||||||
|
> **C3 game-loop UX (e08374f):** NextBeatCard under every game's results ("Tonight's question is ready →" / streak line; verified live on How Well) · How Well **role-swap** CTA "Your turn — let {name} guess you" (verified) · Date Match per-couple shuffle + skip-already-swiped (verified: swiped card gone) · Desire Sync seen-question memory (SeenDesireQuestionStore) · How Well pool excludes daily_fun_mc novelty MCs.
|
||||||
|
> **C4 nudge+copy+a11y (66ab59c):** "Send a little nudge 💜" on the waiting screen via thinkingOfYou callable (verified: partner_activity push landed) · per-game banner copy ("Your turn — guess their answers" for How Well etc., client + server mirror; verified) · merged contentDescription on the ToT badge + How Well score ring.
|
||||||
|
> **SEED daily-question pool (54b6125) — R29 R-1 DONE + deployed:** seeded Firestore `questions` with the 75 FREE daily_fun_mc weekday questions (ids match the asset DB); rewrote server selection `pickDailyQuestionId(date)` to be **mode-aware + deterministic** (mirrors the client's frozen DOW_DEFAULTS + epochDay offset). **Verified live end-to-end:** callable now assigns a REAL id (`daily_single_choice_weekly_v1_072`, Tiny Win Tuesday) not `q_default_daily`; **both devices resolve the IDENTICAL question**; theming preserved ("Tiny Win Tuesday"); reveal shows real option text, **no slugs**. Also fixes partners in different timezones (server-authoritative now, was device-local weekday). Reveal category chip "Daily Fun Mc"→"Daily Fun" (2nd surface of the C1 pill fix, central helper). Functions deployed OK (assignDailyQuestion + callable + game triggers).
|
||||||
|
> **Gates:** unit **BUILD SUCCESSFUL** · functions **58** green · assembleDebug clean · entrypoint cold-start smoke run (nav/notifications touched). **State clean:** 0 active sessions, premium revoked (locks confirmed). All batches committed per-batch, no push. Ben was signed out→back-in mid-round (throwaway signup for DOB checks) — pairing intact. Test passwords now recorded (`qa/README.md` 85b875d).
|
||||||
|
|
||||||
|
|
||||||
> **Verdict (2026-07-07): R29 = SENIOR-DEV GAMES/MECHANICS/RETENTION/UI REVIEW round (user-mandated) — all 7 games verified live 2-device on a FRESH couple created through the real signup→invite→recovery-phrase flow; found & FIXED 2×P1 + 2×P2; two fix batches committed.**
|
> **Verdict (2026-07-07): R29 = SENIOR-DEV GAMES/MECHANICS/RETENTION/UI REVIEW round (user-mandated) — all 7 games verified live 2-device on a FRESH couple created through the real signup→invite→recovery-phrase flow; found & FIXED 2×P1 + 2×P2; two fix batches committed.**
|
||||||
> **New fixture couple:** Ava(5554/CloserQA2, light) + Ben(5556/CloserCodexQA, dark), coupleId `j39xR7PVZLLCveWnmenU` (old QA fixture's device state was lost environmentally; QA-account credential ops are blocked in auto mode, so the round created a real couple — which itself live-verified onboarding/signup/pairing/recovery-ceremony/day-0 check-in).
|
> **New fixture couple:** Ava(5554/CloserQA2, light) + Ben(5556/CloserCodexQA, dark), coupleId `j39xR7PVZLLCveWnmenU` (old QA fixture's device state was lost environmentally; QA-account credential ops are blocked in auto mode, so the round created a real couple — which itself live-verified onboarding/signup/pairing/recovery-ceremony/day-0 check-in).
|
||||||
> **Games (all PASS live, engineered-answer verification):** ToT exactly 3/5 in-sync · HowWell 2/5 with amber near-miss (isClose) + correct subject/guesser roles · DesireSync exactly "2 shared desires / 3 stayed private", non-mutual answers never rendered, `enc:v1:` at rest · Wheel identical 10-q set + completion gate · Challenges real day-gating (premium challenge opened under grant) · Memory Lane sealed capsule (title+body ciphertext, no list leak) · Date Match simultaneous-LOVE race → exactly 1 match + both pushes. Engine seams: one-active-game convergence ("Join the game"), first-finisher banner+push, cold-start push open (0 FATAL), quit-abandon → 0 active sessions (admin-verified).
|
> **Games (all PASS live, engineered-answer verification):** ToT exactly 3/5 in-sync · HowWell 2/5 with amber near-miss (isClose) + correct subject/guesser roles · DesireSync exactly "2 shared desires / 3 stayed private", non-mutual answers never rendered, `enc:v1:` at rest · Wheel identical 10-q set + completion gate · Challenges real day-gating (premium challenge opened under grant) · Memory Lane sealed capsule (title+body ciphertext, no list leak) · Date Match simultaneous-LOVE race → exactly 1 match + both pushes. Engine seams: one-active-game convergence ("Join the game"), first-finisher banner+push, cold-start push open (0 FATAL), quit-abandon → 0 active sessions (admin-verified).
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue