Closer/ClaudeQACoverage.md

11 KiB
Raw Blame History

Claude QA Coverage Matrix

Resume anchor — current status only. Statuses: pass | fail→id | todo | n/a | not implemented→Future.md | blocked→id. Build 23dd6a7. Position + verdict: see ClaudeReport.md run-state. Verdict: AG + I covered (I-001 open P1 — outcomes read), J pending. Hygiene: this is a current-status matrix, not a per-round changelog — fail→id flips to pass once a fix is confirmed (ID archived below); finished rounds collapse to the history line. (See Report hygiene in ClaudeQAPlan.md.)

Status at a glance

Pass Coverage Status
A — Couple-shared premium all gated features × neither/partner/self pass
B — Games lifecycle all 7 games played full, 2-device, real user-nav pass
C — Visual (light+dark) ~14 core screen-types both themes pass · deep/list screens deferred
D — Security & encryption D1 at-rest · D2 rules · D3 live raw-API · D4D6 clean
E — Notifications chat + game start/finish/results live, both-client + suppression pass · full fg/bg/killed matrix partial
F — Resilience concurrency · offline · lifecycle · process-death · time pass
G — Account creation / fake-account sign-up · validation · duplicate · invite-abuse pass
H — Branding & artwork consumer brand walk → prompts see ClaudeBrandingReview.md
I — Performance & route efficiency cold-start, jank (core/conversation/hub), leak proxy, caching done · I-001 (P1) outcomes read denied
J — Accessibility todo (Round 8)

Archived issue IDs (fixed + confirmed, detail in git): A-001 · A-003 · A-OBS · B-001 · B-002 · B-003 · B-004 · C-CC-001 · C-DS-001 · C-NAV-001 · D-001 · E-001 · E-002 · E-003 · E-OBS · F-OBS. Pending one confirm: F-RACE-001.


Pass A — Couple-shared premium (neither / partner-only / self)

Feature neither→locked partner→both unlock self→unlock Status
Chat media + reactions pass pass pass pass (reference pattern)
Play: Desire Sync pass pass pass pass
Play: Memory Lane pass pass pass pass
Play: Connection Challenges pass pass pass pass
Question Packs (premium) pass pass pass pass
Wheel: Category Picker / Spin / History pass pass pass pass
Date Match / Plan Date pass pass pass pass
Subscription screen (own status) n/a n/a n/a pass (by-design per-user)

Verified live: neither→paywall ("Go deeper together"); partner→couple-shared unlock (Sam free entered Desire Sync + Memory Lane); self→unlock; premium badges hidden under premium / shown when free. (A-001 couple-shared gap + A-003 badge fixed & confirmed.)

Pass B — Games lifecycle (start / play / finish + results, 2-device, real user-nav)

All 7 played one complete time through on both devices via the real in-app path; gameplay all PASS.

Game starts plays finishes/results no crash Evidence
1. This or That pass pass pass pass 5/5 via Play hub, answers synced, results match both ("Two peas in a pod").
2. How Well Do You Know Me pass pass pass pass QA answered 5 (incl. 15 scale); Sam predicted via hub → 4/5, wrong one marked ✗ on both, scoring accurate.
3. Desire Sync pass pass pass pass QA(free) entered w/o paywall; both answered 5 Yes/No → exactly 3 mutual desires, mismatches hidden, match on both.
4. Connection Challenges pass pass pass pass Gratitude Week → both did Day 1 → 🔥1, advanced to Day 2 synced. (7-day series time-gated; per-day cycle verified.)
5. Memory Lane pass pass (create+seal) pass (sealed) pass Capsule sealed "Opens in 29 days", encrypted at rest (title+content enc:v1:), cross-device. Unlock future-dated.
6. Spin the Wheel pass pass pass pass Spun → category → both answered all 10, per-Q You/partner breakdown matches both, session synced.
7. Date Match pass pass pass pass Both swiped deck, 3 mutual likes → 3 date_matches, "It's a match!" modal + live push, "Your Matches" shows all 3.

Note: exit each game via "Back to Play" between games so the session closes (B-001 auto-completion fix verified). F-RACE-001 (simultaneous start) fixed — see Pass F.

Pass C — Visual (light + dark), all ~50 routes

~14 screen-types swept Dark (5554) + several Light (5556): all render clean, readable, no FATAL, no dark-mode contrast issues; 0 enc:v1: leaked to conversation UI. Covered: Home, Play hub, all 7 game screens (setup/play/reveal), Paywall, Settings (+Subscription +Appearance), Today/daily-question (+answer detail), Messages inbox, Conversation (image+voice+text+reaction). Back-stack clean (deep→hub→Home→launcher, no double-back).

  • Deferred (standard list/detail, lower risk; cover in Round 8): Question Packs detail · Bucket List · Past Games · Wheel History · Answer Reveal (sealed) · Date Builder/Plan Date · fresh-account auth/onboarding/pairing.

Pass D — Security & encryption (D1D6) — clean, no P0/P1

  • D1 at-rest (admin ground-truth): messages text + lastMessagePreview, all 4 game-answer collections (this_or_that/how_well/desire_sync/wheel, both users), capsule title+content, date_swipes.actions = enc:v1:; wrappedCoupleKey ciphertext (recovery-phrase-wrapped, argon2id); encryptedRecoveryPhrase server-blind + wiped on acceptance; plaintext inviteCode not exploitable (no code-encrypted secret persists; /invites/{code} readable only by inviter).
  • D2 rules: no catch-all, no blanket if true; sessions update allowlist + immutable startedByUserId + monotonic status; hasPremium + entitlements server-only; ciphertext enforced on private fields; capsules/challenges member-scoped.
  • D3 raw-API negative (LIVE): non-member ID token → Firestore REST on couple doc/conversation/messages/answers/session/capsules/partner-profile = all 403; non-member writes incl. real users/{uid}/entitlements/premium = all 403 → no self-grant. Member token reads 200 → App Check not enforced on Firestore; rules are the sole gate and hold.
  • D4/D5/D6: wrapped couple key + KDF; App Check (client), gitignored SA JSONs, allowBackup=false; analytics metadata-only. Unchanged, hold.
  • Two hardening notes → Future.md (App Check off on Firestore; users/{uid} update rule allows arbitrary non-hasPremium fields).

Pass E — Notifications (type × {foreground / background / killed} + tap-to-open, both clients)

Full live two-device run (games + messages):

  • chat_message end-to-end — channel partner_activity, title "Sam sent a message" (name, not private), body content-free, text NOT in payload; tap → exact conversation with content; white monochrome small icon.
  • partner_started_game — channel game_activity, "QA is playing… Tap to join!" (content-free); tap → joins the active session.
  • partner_finished_game / results — results push delivered to backgrounded partner, channel game_activity, content-free; tap → per-session results (per E-003 fix).
  • results-suppression — partner foregrounded on the session received 0 pushes (ActiveGameSessionMonitor), while backgrounded partner got the results push. Delivery + suppression both confirmed.
  • Deferred (Round 8): the full 17-type × {fg/bg/killed} matrix isn't exhaustively run live — remaining types are routing-code-verified + centralized in PartnerNotificationType; date_match push verified live. New types added to the plan's Pass E inventory (join_game, partner_joined_game, game_ended, date_plan_update, etc.) = todo.

Pass F — Resilience / lifecycle / concurrency / time

  • Concurrency race: F-RACE-001 (P1) fixed + re-confirmed live (R8): simultaneous mood-tap on both devices → 1 session (was 2); race-loser landed on WaitingForPartner → "Join the game" → joined the winner's session at the same Q1 (shared reveal preserved). Archived. (Minor pre-existing note: loser can alternatively land on Play hub; not seen this run.)
  • Offline: airplane mode → Today renders from cache, no crash.
  • Lifecycle: rotation/config-change → state preserved; ~6 cold restarts → clean to Home (auth persists).
  • Robustness: malformed/abusive deep-link intents (unknown type, missing extras, injection/path-traversal) → 0 crash; killed-state cold-start chat deep-link → conversation loads.
  • Deferred (Round 8): time-travel-gated content (capsule unlock, challenge day-gating); broader network-flaky across answers/dates; account-lifecycle (unpair→re-pair, deletion cascade) deep run. Minor note: race-loser sometimes lands on Play hub vs WaitingForPartner (no dup/crash; pre-existing routing).

Pass G — Account creation, validation & fake-account abuse

Sign-up end-to-end (email/pw/confirm → 3-step profile → unpaired home) ; weak-password → friendly "at least 8 characters" ; fresh-account isolation (zero couple data) ; duplicate-email → auth/email-already-exists rejected ; invite single-use + 24h expiry, bogus code → "Invite not found." ; recovery phrase client-generated ; sign-out → onboarding → debug-token restore . No security findings. (Non-member READ denial = live D3 above + app-level isolation.)

Pass I — Performance & route efficiency (R8, build 23dd6a7, emulator-5554, debug build)

Route smoke-test checklist (re-runnable: dumpsys gfxinfo closer.app reset → drive route → read gfxinfo):

Route / list Jank / latency Notes
Cold start → Home 1253ms to first frame acceptable (debug; release AOT-faster); no skipped frames, no ANR
Core tabs (Home/Today/Play/Messages/Settings) 6.3% janky frames smooth; no Choreographer-skip spam
Conversation (realtime listener) scroll 90th 36ms / 95th 53ms minor debug hitching; no leak
Play hub scroll 90th 36ms / 95th 38ms smooth
  • Caching / lazy-load: LazyColumn/Row/Grid in 17 files; Coil (AsyncImage) in 11; Room DAOs cache static question/category data locally — all in place, no load-all anti-patterns seen.
  • Leak check: conversation open/close ×6 → ViewRootImpl=1, Activities=1, Views +2, PSS bounded after trim → no window/Activity/listener leak.
  • Redundant reads: precise per-read counts need an instrumented/Perfetto build (Firestore success reads aren't in adb logcat); no failing-read spam except I-001; no leaked listeners.
  • Finding: I-001 (P1)getOutcomes() bare-list query is rules-denied → "Your Progress"/outcomes silently broken (see ClaudeReport.md).

Passes H / J

  • H Branding — deliverable in ClaudeBrandingReview.md (consumer brand walk → ready-to-paste art prompts).
  • J Accessibilitytodo (Round 8): font_scale 1.3/1.5/2.0, TalkBack semantics, WCAG-AA contrast, 48dp targets, keyboard, reduce-motion.

Round history (one line each)

  • R7 — security/concurrency deep dive (multi-angle): cornerstone clean; F-RACE-001 found+fixed+verified. 0 new open.
  • R6 — branding drop + Future.md backlog regression: 0 new open.
  • R5 — Cloud Functions deployed (E-OBS/E-003) + new Pass G clean: 0 open.
  • R2R4 — play-as-user game restart + fix phase; all P0P2 fixed + verified (archived IDs above).