Closer/ClaudeReport.md

6.7 KiB
Raw Blame History

Claude QA Report — Games & Notifications

Updated: 2026-06-24 Devices: emulator-5554 (Device A = QATester) + emulator-5556 (Device B = Sam), paired for real (coupleId xNd1H2UGUDNqvyrDGgfu). Focus this pass: every game works end-to-end and notifications fire correctly on game start + finish.

Severity: 🔴 critical · 🟠 high · 🟡 medium · 🟢 low Status: 🔎 found · 🛠 fixing · fixed & builds · verified live · ⚠️ needs deploy


OPEN — current error log

N1. 🔴 FCM token was NEVER registered → no push notifications worked at all — FIXED & VERIFIED

TokenRegistrar.register() (which fetches messaging.token and stores it) was never called anywhere. The only other path, AppMessagingService.onNewToken, bails with currentUserId ?: return — and FCM generates the token at install, before sign-in, so onNewToken ran with no uid and stored nothing; it never fires again afterwards. Result: users/{uid}.fcmToken was empty for every account, so no game/message/daily push could ever be delivered. Fix: MainActivity now observes authState and calls tokenRegistrar.register() whenever a user is authenticated. (MainActivity.kt) Verified live: after the fix both A and B have a stored fcmToken; a direct push to A rendered the heads-up "Your partner started a game — Tap to join them."

N2. 🔴 POST_NOTIFICATIONS permission was never requested → notifications can't display on Android 13+ — FIXED

NotificationPermissionHelper existed but had no caller. On API 33+ notifications are silently dropped without the runtime grant. Fix: MainActivity requests POST_NOTIFICATIONS on launch via an Activity Result launcher. (MainActivity.kt)

N3. 🟠 Game-START notification named the WRONG person — FIXED ⚠️ needs functions deploy

onGameSessionUpdate passed the recipient's name into the body, so the partner saw " has started a game" (live: B/Sam received "Sam has started a game"). It should name the starter. Fix: use startedByUserId → starter's name + avatar for both title and body. (onGameSessionUpdate.ts)

N4. 🟠 Game-FINISH notification only reached one partner — FIXED ⚠️ needs functions deploy

Completion branch gated the "notify both" path on currentData.partnerCompletedAt, a field the client never writes (the client tracks completedByUsers). So when both finished, the partner who'd been waiting often got nothing (or a "tap to continue playing" that no longer applied). Fix: on active → completed (both partners done = reveal ready) notify both partners, each naming the other (" finished — tap to see your results!"). (onGameSessionUpdate.ts)

N5. 🟡 Finish copy was wrong (title + client mapping) — FIXED & VERIFIED

  • FCM title was hard-coded "<name> is playing" even for finish events (shown verbatim when the app is backgrounded). Now type-aware → "<name> finished the game". (onGameSessionUpdate.ts)
  • Client mapped partner_finished_gamePARTNER_COMPLETED_PART ("finished their part, open yours when ready") — wrong once both are done. Added a dedicated GAME_RESULTS_READY type ("Your game results are ready! You both finished — tap to see how you compare."). (PartnerNotificationManager.kt)

N5 verified live: pushed both types to A — start rendered "Your partner started a game — Tap to join them."; finish rendered "Your game results are ready! You both finished…". The client renders the correct copy for each type.

N6. 🟠 Deployed functions are STALE for Date Match — ⚠️ needs functions deploy

Source exports notifyOnDateMatch, but the deployed function is still the old createDateMatchOnMutualLove. onMessageWritten is current; the rest predate recent edits (incl. N3N5). Action: firebase deploy --only functions (allow it to delete createDateMatchOnMutualLove).


Per-game status

Game Functional Start notif Finish notif
Spin the Wheel live (prior) via sessions trigger* via sessions trigger*
This or That live (prior) via sessions trigger* via sessions trigger*
How Well fixed (prior) via sessions trigger* via sessions trigger*
Desire Sync fixed (prior) via sessions trigger* via sessions trigger*
Connection Challenges clean (prior) n/a (completion-based) n/a
Date Match E2EE + rules (prior) ⚠️ notifyOnDateMatch not deployed (N6)
Daily reveal live (prior) onAnswerWritten / sendPartnerAnsweredNotification reveal-ready

* All games share one notification trigger: start writes couples/{id}/sessions/{sessionId}.status="active", finish writes "completed", and onGameSessionUpdate fires on that single doc. So N3/N4/N5 fix start+finish notifications for every game at once. Pipeline is proven (function writes notification_queue + sends FCM; FCM delivery verified in N1); the start-name/finish-both fixes take effect after the deploy in N6.


DEPLOY CHECKLIST (your call — prod deploys/admin writes are blocked for the agent)

  1. firebase deploy --only functions — ships N3, N4, N5 (server side) and the notifyOnDateMatch rename (N6).
  2. firebase deploy --only firestore:rules — Date Match (date_swipes/date_matches) + sealed releaseKeys sender-read, if not already live.
  3. Install the refreshed APK (Closer-v0.1.0-debug-2026-06-24.apk) — ships N1, N2, N5 (client) + the in-app message bubble.
  4. A leftover active this_or_that session is in couples/{id}/sessions from prior testing; it blocks starting a new game until that game is finished (or the doc is removed — admin delete needs your authorization).

Completed earlier (kept for reference, no longer open)

  • Daily reveal sealed-key exchange (release-key tolerant read, epoch-millis updatedAt, id→label mapping) — verified live.
  • Game-start crash (saveSession empty id → invalid path) — fixed; This or That verified live.
  • Game re-entry flicker/re-submit (This or That, How Well, Desire Sync) — pre-check → WAITING.
  • Daily question determinism + shared DailyQuestionResolver verified live.
  • Partner identity (users partner-read rule) — verified live ("Connected with Sam/QATester").
  • Date Match E2EE + rules rewrite — (⚠️ still needs the deploy in checklist #1/#2).