Closer/ClaudeQACoverage.md

20 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 2cd0af6 + R11/R12/R13 working-tree changes (rebuilt + installed both emulators). Position + verdict: see ClaudeReport.md run-state. Verdict: R13 = open-backlog fix pass + full fresh AJ — FLAWLESS, 0 open P0P3. Fixed all 5 carried/open UI items (C-DARK-UI-001 ToT dark redesign · C-DARK-UI-002 check-in label · C-DARK-UI-003 bottom insets · C-ART-EDGE-002 8 opaque heroes feathered · J-OBS 48dp), confirmed A-201 live → pruned, shipped the Premium-unlock modal (one-time, both partners). Pass D cornerstone re-verified LIVE. All app changes in the working tree (user commits); diff is UI-only (no rules/functions/crypto).

📖 Architecture reference: see docs/Engineering_Reference_Manual.md — contains architecture, security model, data model, and the Known landmines section that backs every fix-and-pruned ID below. 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 R13: A-201 confirmed live → pruned (free QA → Date Match Love ★Premium → Paywall, deck didn't advance) + Desire Sync free→Paywall re-verified; couple-shared unlock holds pass (all gates couple-shared incl. Date Match)
B — Games lifecycle R12: 4 async games full 2-device end-to-end (ToT Light×5, Wheel mixed-types, How Well asym, Desire Sync shared/private) + start/join/first-finisher/finish/results/back-stack; CC+MemoryLane+DateMatch render/core (full per R10) pass (first-finisher nudge + C-NAV-002 + Ready=Start re-verified live; MemoryLane title/preview run-on → Future.md)
C — Visual (light+dark) R13: ToT setup+gameplay both themes, Play/Home/Paywall insets, Today/Paywall heroes, conversation, Premium modal both themes — all verified live C-DARK-UI-001/002/003 + C-ART-EDGE-002 all FIXED + verified live R13 (ToT theme-aware redesign · check-in label weight · bottom clearance · 8 opaque heroes feathered); pending 1 confirm
D — Security & encryption R13 LIVE (rules/functions unchanged this session): D3 non-member GET couple+messages → 403; D5 self-grant entitlement PATCH → 403; member GET own couple → 200; D1 chat at-rest enc:v1:. D2/D4/D6/D7 carried R7/R10 clean — cornerstone holds
E — Notifications R12 LIVE: Pass B verified start/first-finisher(partner_completed_part)/finish triggers→correct partners+copy; cold-start tap smoke 6/6 (launcher + 5 notif types open & stay) pass · splash-crash class clean on fresh APK
F — Resilience R12: concurrency (F-RACE-001 atomic-start code + R8 live) · process-death (smoke am kill×5 → push → cold-start recovered each) · offline(R9) pass · time-travel + deletion-cascade deferred
G — Account creation / fake-account R10: abuse live via D3 (non-member denied, no self-grant) + invite rules; happy/validation R5-clean (unchanged) pass
H — Branding & artwork R13: ToT gameplay brand redesign + Premium-unlock modal (A13) both implemented + verified live; 8 opaque heroes feathered see ClaudeBrandingReview.md (no open branding implementation work remains)
I — Performance & route efficiency R10: core-tabs jank 5.53% (R8 6.3%), 90th 32ms, leak proxy bounded no regression
J — Accessibility R13: J-OBS FIXED + verified live (composer media/voice/retry buttons → 48dp; measured 126px=48dp both axes); font scale 2.0 reflows + reduce-motion×7 (R10) hold done · J-OBS fixed (pending 1 confirm)

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).

  • R10 re-sweep (both themes where relevant): Messages inbox (dark+light: conversations, avatars, unread dot, previews decrypted, no enc: leak), Conversation (image/voice/text/reaction/read-receipt/date-sep, E2E lock glyphs, correct attribution both dirs), per-question Discussion thread , Today/daily-question (dark+light, paired-books art on-brand), Activity/Together (dark). 5 P2 found: C-HOME-001 (Home shows top pending action twice — primaryAction hero + buildPendingActions row), C-NAV-002 (wheel results→BACK re-enters finished play screen, no popUpTo), C-NAV-003 (Wheel History/Past Games + PartnerHome double app-bar — route in shellBackRoutes while screen owns a TopAppBar; C-CC-001 class), C-PW-001 (dark paywall "What's included" pills light-on-light, BenefitPill onSurface text), C-SEC-001 (accepter recovery copy). Premium-locked Wheel History state renders. Date Builder · Question Packs(gated→paywall) · Answer Reveal sealed = token-consistent, R9-clean.
  • R9 deferred sweep — 0 new issues: Answer History, Together/Activity, Bucket List (empty state + FAB), Date Match deck, Date Matches all render cleanly in dark (good contrast, no clipping, no FATAL); Privacy & Terms + Home confirm light parity (shared Material3 tokens). Remaining standard list/detail (Wheel History · Date Builder · Past Games · Answer Reveal sealed · Question Packs[gated→paywall]) are token-consistent with the above; fresh-account auth/onboarding visual covered R3/R5. No C findings.

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.
  • New speculative types — not implemented → Future.md (R8 code check, 0 files each): join_game/game_invite, partner_joined_game, game_abandoned/game_ended, date_plan_update, memory_capsule_created, challenge_day_completed, subscription_entitlement_changed. Worthwhile ones (couple-premium-unlock push; join/end pushes) logged to Future.md ## QA. Not counted as pass.
  • partner_answered (couple-key reveal, 2026-06-26) live both-client — onAnswerWritten fires on each answer; the second answer hits the both-answered "Your answers are unlocked " copy (recipient already answered). onAnswerRevealed fires when isRevealed flips false→true → "opened your answers" push to the partner (witnessed live). Privacy gate (raw API): partner content 403 until both answer, 200 after; non-member 403 throughout. Reveal screen shows the partner's answer both directions. At-rest: content-free metadata + gated enc:v1: secure/payload.
  • Deferred (Round 9): the full implemented-type × {fg/bg/killed} matrix isn't exhaustively re-run live — implemented types are routing-code-verified + centralized in PartnerNotificationType; chat/game start/finish/results + date_match verified live (R3/R5/R6).

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.
  • R9 network resilience: airplane-mode on → Date Match + Messages render from cache, no crash, no error dead-end; reconnect → inbox refreshes, no stuck state, 0 FATAL (extends R3 offline-Today-from-cache).
  • Deferred (Round 10, low-risk): time-travel-gated content (capsule unlock, challenge day-gating — needs clock manipulation); account-lifecycle deletion-cascade deep run (disruptive on the baseline couple). Minor note: race-loser can land 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) — FIXED+VERIFIED getOutcomes() bare-list query was rules-denied → fixed with whereIn(documentId, dayKeys); 0 PERMISSION_DENIED after. I-002 (P1) — FIXED+VERIFIED (found fixing I-001): scores stored as int64 → read as Long → Map<String,Int> cast CCE → swallowed; fixed via Number.toInt(). Live: seeded day_0 → "Your Progress" shows "Baseline recorded". Both pending Round-9 confirm.

Pass J — Accessibility (R8, emulator-5554)

  • Font scaling (font_scale 2.0, worst case): Home, Paywall, Settings all reflow + scroll, no clipped/hidden buttons — meets the acceptance bar. Minor: long subtitles/email ellipsize, bottom-nav labels wrap ("Mess ages"). Restored to 1.0.
  • TalkBack / semantics: 0 Icon() calls without contentDescription; 111 explicit null (decorative silenced); meaningful labels on all key controls (Back ×26, Send, Close, Dismiss, photo actions, date-swipe Love/Maybe, capsule, edit/delete); loader uses clearAndSetSemantics + "Loading…" message.
  • Touch targets: most controls 48dp; J-OBS (P3): a few conversation icon-buttons measure ~4245dp wide (48dp tall) — single-axis marginal, fully operable; bump to 48dp.
  • Reduce-motion (animator_duration_scale 0): nav sweep + screens work, no hang/unreachable content, 0 FATAL; honored in code across 7 surfaces (LoadingState, CelebrationOverlay, AnswerReveal, DesireSync, ThisOrThat, BrandMessageRotator, LocalQuestionContent). Restored to 1.
  • Contrast: covered by Pass C both themes (C-DS-001 dark-contrast fixed); precise WCAG ratios need a measurement tool — spot-checks clean, no new dim areas.
  • Keyboard/IME: text fields validated functionally in Pass G (sign-up/profile); full hardware-keyboard tab-order deferred (emulator HW-keyboard harness).
  • Findings: J-OBS (P3) only; no P0/P1/P2 a11y blockers.

Pass H

  • H Branding — deliverable in ClaudeBrandingReview.md (consumer brand walk → ready-to-paste art prompts).

Round history (one line each)

  • R13 — open-backlog fix pass + full fresh AJ, FLAWLESS (0 open P0P3): fixed C-DARK-UI-001 (ToT dark redesign), C-DARK-UI-002 (check-in label), C-DARK-UI-003 (bottom insets), C-ART-EDGE-002 (8 opaque heroes feathered), J-OBS (48dp targets); confirmed A-201 live→pruned; shipped Premium-unlock modal (one-time, both partners, couple-shared, verified live). Pass D cornerstone re-verified LIVE (non-member 403, self-grant 403, member 200, at-rest enc:v1:). Diff UI-only → E/F/G carried. 0 FATAL both emulators.
  • R12 — FRESH FULL AJ run + fix phase, FLAWLESS (0 open P0P2): found+fixed A-201 (P1 Date Match premium bypass — gated via CouplePremiumChecker→Paywall, verified live); 4 async games full 2-device E2E; security cornerstone live-clean (non-member 403 read+write, self-grant 403); smoke 6/6; jank 4.10%; new P3 C-ART-EDGE-002 (hero edges, deferred); C-DARKART-001+C-ART-EDGE-001 held→pruned; Pass A retrospective added (badge≠gate).
  • R11 — confirmation round, FLAWLESS (0 open P0P2): fixed C-DARKART-001 (P2, art follows in-app theme via LocalAppInDarkTheme + config-overridden context) + C-ART-EDGE-001 (P3, edge feathering) in shared BrandIllustration/EmptyState, verified live both decoupled theme directions (system-light+app-Dark→dark art · system-dark+app-Light→light art), 0 FATAL; re-confirmed + pruned the 5 R10 P2 fixes (C-HOME-001/C-NAV-002/C-NAV-003/C-PW-001/C-SEC-001); entrypoint smoke 6/6 green on fresh APK (launcher + 5 notif cold-starts open & stay). Art fixes in working tree; rest committed 2cd0af6.
  • R10 — FULL run AJ + fix phase: 5 P2 found+fixed+verified-live (C-HOME-001 dup card · C-NAV-002 wheel back-stack · C-NAV-003 dup app bar · C-PW-001 dark paywall · C-SEC-001 recovery wrong-store); E-GAME-002 confirmed live (start push+banner+Join) & pruned; concurrency double-start→1 session; security D1D7 clean; perf/a11y no regression. 0 open P0P2 (5×P2 pending 1 confirm).
  • 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).