4.9 KiB
Claude QA Report — Full-App QA (living report)
Verdict (2026-06-25): 0 open issues at every severity (P0–P3). Security cornerstone fully clean. App is at the "flawless" bar.
This report shows current state only. Fixed issues live here for one confirmation round, then they're pruned to the archived-ID line below (full detail stays in git history). See Report hygiene in
ClaudeQAPlan.md.
Run-state (current)
Round 7 (multi-angle deep dive) — COMPLETE | 0 open P0–P3 | NEXT ACTION: Round 8 re-QA — confirm F-RACE-001, then prune it; run Passes I/J live.
- Build: client HEAD
23dd6a7(includes the F-RACE-001 fix, verified live), Cloud Functions deployed. - Devices / accounts: emulator-5554 = QA (
Y05AKO2IlTPMa0JQW1BiNIM0uzK2) · emulator-5556 = Sam (imDjjO…) · paired, coupleIdXal3Kw3gjSdn0niERYKJ, both free (baseline restored). - Docs: Playbook
ClaudeQAPlan.md· CoverageClaudeQACoverage.md· IdeasFuture.md## QA· BrandingClaudeBrandingReview.md.
Severity board
| Severity | Open | Fixed (pending 1 confirm) |
|---|---|---|
| P0 | 0 | 0 |
| P1 | 0 | 1 (F-RACE-001) |
| P2 | 0 | 0 |
| P3 | 0 | 0 |
Open issues
None.
Fixed this round — pending one confirmation round (then prune)
| ID | Severity | Area | Description | Fix | Status |
|---|---|---|---|---|---|
| F-RACE-001 | P1 | Games / concurrency | Both partners starting the same game within ~1s created 2 divergent active sessions (different question sets) → no shared reveal; core loop silently defeated. Non-transactional check-then-create in GameSessionManager.startGameWithCouple. |
Atomic Firestore transaction on a per-couple pointer couples/{cid}/sessions/_active (startSessionAtomically): reads pointer → AlreadyActive→join, else atomically sets session + re-points lock. All 7 games funnel through it. Member-writable sessions/_active rule deployed. Files: QuestionSessionRepository[Impl].kt, GameSessionManager.kt, firestore.rules. |
Fixed + verified live (23dd6a7): parallel-tap race → 1 session (was 2); sequential 2nd start → joins; pointer self-heals on completion; 0 FATAL. → Round 8: re-confirm, then delete this row. |
Resolved & confirmed (archived — full detail in git history)
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 — all fixed and re-verified across Rounds 2–6 (commits cited in history). Pruned from the live report per the one-confirmation-round rule. (C-OBS / outcomes list / SubscriptionScreen per-user gate = investigated, not bugs.)
Security cornerstone — clean (Pass D, deep dive, Round 7)
- D1 at-rest: chat text +
lastMessagePreview+ all 4 game-answer collections (ToT / How Well / Desire Sync / Wheel, both users) + Memory Lane capsules + date-swipe actions =enc:v1:. No plaintext content; only metadata in clear. - D2/D3 access: non-member denied all reads/writes (raw Firestore REST → 403); real premium write
users/{uid}/entitlements/premiumdenied (server-only → no self-grant); cross-couple denied. - D4 keys: couple key phrase-wrapped (argon2id); recovery phrase server-blind;
encryptedRecoveryPhrasewiped on acceptance; plaintextinviteCodenot exploitable (invite readable only by inviter; no code-encrypted secret persisted). - Robustness: malformed/abusive deep-link intents (unknown type, missing extras, injection/path-traversal) → 0 crash; killed-state cold-start chat deep-link → conversation loads.
Round history (one line each)
- R7 — multi-angle security/concurrency deep dive → cornerstone fully clean; F-RACE-001 found + fixed + verified. 0 new open.
- R6 — branding drop + Future.md backlog regression (white-keyhole icons/loader/splash, inclusive gender, copy, rate-limit split, results-push suppression, paywall retry/offline) → 0 new open.
- R5 — Cloud Functions deployed (E-OBS channel fix, E-003 results routing) + new Pass G (account creation / fake-account abuse) clean → 0 open.
- R1–R4 — baseline Passes A–F report-only; every P0–P2 found was fixed + verified (see archived IDs).
Operational constants
- Execution mode: autonomous run-to-completion — don't stop; fix blockers inline; cycle fix→re-QA until flawless. Don't hand back when context fills — re-read this run-state + coverage after any compaction. Commit before interruptible work; recover stuck sessions via the session-start ritual.
- Standing authorization (user, 2026-06-24): may
firebase deploy --only firestore:rules+ has admin access (Firestore reads/writes/seeds + entitlement toggles) — run without pausing. Only the macOS requirement for iOS (Parts 2/3) is a hard stop. - Hardening backlog → Future.md: App Check not enforced on Firestore;
users/{uid}update rule allows arbitrary non-hasPremiumfields (tighten to a field allowlist).