Commit Graph

54 Commits

Author SHA1 Message Date
null 4eed0a8115 feat(premium): couple-shared unlock notification + reveal retry + users update allowlist + brand glyphs
- New Cloud Function: onEntitlementChanged (Firestore onWrite on entitlements/premium) — edge-triggered inactive→active, notifies the OTHER partner so couple-shared unlock isn't silent
- New notification type SUBSCRIPTION_CHANGED → routes to SUBSCRIPTION
- AnswerRevealViewModel: re-issue markRevealed if best-effort failed (offline/transient) so partner_opened_answer push eventually fires
- firestore.rules: harden users/{uid} update allowlist (defense-in-depth; no live hole)
- 18 new brand glyph vector drawables (drawable-nodpi/)
- SettingsScreen / PlayHubScreen / WaitingForPartnerScreen: swap Material icons for new brand glyphs
- ClaudeQA docs + Future.md updated
2026-06-27 16:35:41 -05:00
null 9f09ebbc67 chore: R12 working tree — QA docs, brand illustration updates, date-match paywall routing, theme tweaks 2026-06-27 15:34:38 -05:00
null 9eee3951e9 docs(qa): point Report + Coverage at Engineering Reference Manual (landmines section) 2026-06-27 14:51:48 -05:00
null 2cd0af65a8 chore: working tree changes — QA docs, app tweaks, Cloud Functions updates 2026-06-27 13:31:09 -05:00
null 9c84c36443 fix(qa): R10 fix phase — 5 P2 bugs fixed (C-HOME-001, C-NAV-002, C-NAV-003, C-PW-001, C-SEC-001) 2026-06-27 10:34:26 -05:00
null 32b5b560a2 docs(qa): update report with R10 results — E-GAME-002 fix, foreground banner, C-SEC-001 2026-06-26 20:04:20 -05:00
null e6a8deef67 docs(qa): update report with couple-key encryption, onAnswerRevealed, both-answered unlock 2026-06-26 12:41:22 -05:00
null f924af9445 qa(brand): complete both-theme sweep of the art drop — 0 issues, 0 FATAL
In-context dark+light verified (Bucket List, Quiet hours, Security, Delete account);
A1/A3 + empty-only states via the debug gallery both themes. Caught + fixed a stale
build on 5556. Baseline intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 10:11:42 -05:00
null 63699c09da docs(qa): note the brand art drop landed + Pass C re-verify owed on touched screens
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 10:01:17 -05:00
null 5ba5b4a8ec qa(R9): clean confirmation round — deferred Pass C + Pass F network swept, 0 new findings
Confirmed + pruned I-001/I-002 (0 outcomes denials/CCE on fixed build). Deferred Pass C
deep/list screens swept (Answer History, Together/Activity, Bucket List, Date Match,
Date Matches in dark; Home/Privacy&Terms light parity) — clean, no clipping/contrast/
FATAL. Pass F network: airplane-mode -> cache render no crash; reconnect -> recovers.
Baseline restored (0 sessions, 0 outcomes). FLAWLESS bar: 0 open P0-P2 (1 P3 J-OBS).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 08:42:03 -05:00
null 9505defd29 qa(R9): confirm + prune I-001/I-002 (0 outcomes denials/CCE on fixed build)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 08:33:46 -05:00
null ab29f6b12f fix(outcomes): restore Your Progress read — scope query to allowed dayKeys + coerce Long scores (I-001, I-002)
I-001: getOutcomes() did a bare collection list .get() on couples/{cid}/outcomes,
which firestore.rules denies (reads allowed only for dayKey in day_0/30/60/90) ->
always PERMISSION_DENIED, swallowed to emptyList(). Now scopes the query with
whereIn(FieldPath.documentId(), OUTCOME_DAY_KEYS) so it satisfies the rule.

I-002 (found while fixing I-001): toOutcomeScores() cast values to Map<String,Int>,
but Firestore returns integer fields as Long on Android -> ClassCastException ->
scores dropped (same shape submitOutcomeCallable writes, so the real path was broken
too). Now coerces (value as? Number)?.toInt().

Verified live: 0 outcomes PERMISSION_DENIED after relaunch; seeded a day_0 baseline
(int64) -> "Your Progress" shows "Baseline recorded" (was "No baseline yet"). Seed
removed, couple baseline restored (0 outcomes, 0 active sessions). Both pending one
re-QA confirmation round before pruning.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 23:58:37 -05:00
null 35d36e6851 qa(R8): Pass J accessibility — font scale 2.0 usable, semantics clean, reduce-motion honored
font_scale 2.0: Home/Paywall/Settings reflow+scroll, no clipped/hidden buttons (only
ellipsis + nav-label wrap). Semantics: 0 unlabeled Icon(), 111 decorative null'd,
meaningful labels on key controls, loader has "Loading…". Reduce-motion honored in 7
surfaces, no hang/crash. Touch targets mostly 48dp. Finding J-OBS (P3): a few
conversation icon-buttons ~42-45dp wide. No P0/P1/P2 a11y blockers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 23:47:27 -05:00
null f740b1d9a1 qa(R8): Pass I performance — route smoke checklist + I-001 (P1) outcomes read denied
Cold start 1253ms; core tabs 6.3% janky; conversation/Play-hub scroll ~36ms 90th;
no window/Activity/listener leak (meminfo stable over open/close x6); lazy-load (17),
Coil (11), Room caching all in place. Found I-001 (P1): FirestoreOutcomeDataSource
.getOutcomes() does a bare collection list .get() that firestore.rules:658 denies
(reads allowed only for dayKey in day_0/30/60/90) -> always PERMISSION_DENIED, swallowed
to emptyList() -> "Your Progress" never shows recorded outcomes + re-prompts done days.
Fix (fix phase): whereIn(documentId, [day_0..90]).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 23:41:08 -05:00
null 11208c6fb5 qa(R8): re-confirm F-RACE-001 live (race -> 1 session, loser joins same set) + prune
Round 8 chunk 1. Simultaneous mood-tap on both emulators -> exactly 1 active session
(was 2 pre-fix); race-loser hit WaitingForPartner -> "Join the game" -> joined the
winner's session at the same Q1 (shared reveal preserved). Regression smoke clean:
no FATAL, game opens both devices, inbox loads, messages + date_swipes ciphertext at
rest. F-RACE-001 pruned to the archived-ID line per report hygiene; 0 open P0-P3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 23:32:31 -05:00
null 96987bf29a docs(qa): merge notification-suite playbook, add report hygiene + finding-routing, clean report/coverage
- ClaudeQAPlan.md: fold the deep notification + join-game suite into Pass E (both-client
  matrix, 6 assertions, expanded inventory, game/join-game suites, payload security,
  malformed/stale tests); add Pass B join-paths + Pass C routes-into-games; add missing
  batch rows G/H; add Report-hygiene (one-confirmation-round prune) + coverage-matrix
  hygiene + easy-to-read mandate; add "Where every finding goes" routing table.
- ClaudeReport.md: collapse stacked R1-R7 run-states + fixed tables to current-state
  (0 open P0-P3; F-RACE-001 pending one confirm; older fixed IDs archived).
- ClaudeQACoverage.md: current-status matrix (flip stale fail->A-001 to pass, drop
  contradictory Pass B footer, add status-at-a-glance, surface todo/deferred).
- removed stray seed/questions/Claude_QA_Playbook_Full_App_QA_Notifications_Merged.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 23:23:30 -05:00
null 23dd6a75e8 fix(games): atomic session start to prevent duplicate sessions on concurrent start (F-RACE-001)
Simultaneous game start by both partners created two divergent active sessions (TOCTOU: a
non-transactional check-then-create in GameSessionManager.startGameWithCouple). Each partner
ended up in a separate session with different questions → no shared reveal.

Fix: QuestionSessionRepository.startSessionAtomically runs a Firestore transaction on a
per-couple pointer doc (couples/{cid}/sessions/_active). It reads the pointer (+ the pointed
session) and either returns AlreadyActive (caller joins the existing session) or atomically
creates the new session and re-points the lock. Concurrent starts contend on the one pointer,
so the loser's transaction retries, sees the now-set pointer, and joins instead of duplicating.
The pointer self-heals (checks the pointed session's status) so no clear-on-finish is needed,
and it carries no status/completedAt so it's invisible to the active/history queries.
GameSessionManager routes all 7 games through it. firestore.rules adds member-write for
sessions/_active (deployed).

Verified live on both emulators: atomic create → 1 session + pointer; sequential 2nd start →
joins (1 session); literal parallel-tap race → 1 session (was 2); 0 FATAL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 21:43:06 -05:00
null 4686a2c200 docs(seed): replace question guides with v2 — content guide, rewrite plan, new quality checklist 2026-06-25 18:48:37 -05:00
null 83d3d59903 qa(r5): functions deployed (E-OBS + E-003 results-ready) + expanded re-QA — 0 open P0-P3
E-OBS FIXED+DEPLOYED: all 12 FCM senders set android channelId; backgrounded chat push verified on
partner_activity (was fcm_fallback). E-003 results-ready FIXED+DEPLOYED: finished-game deep-link ->
per-session results screen (verified). New Pass G (account creation + fake-account abuse) CLEAN:
sign-up/validation, isolation, duplicate-email rejected, invite single-use/expiry + bogus-code
rejected, recovery phrase client-gen. Varied gameplay (Standard/Deep 0-match) + nav fuzzing: no new
bugs. Severity board: 0 open at all levels. Baseline restored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 13:12:40 -05:00
null ee19ef3f59 qa(r4): fix phase + re-QA complete — E-003/B-004/A-OBS fixed; 0 open P0-P2
Round 4: the 2 new P2 (E-003 game-push deep-link, B-004 WaitingForPartner Join escape) + 1 new P3
(A-OBS paywall copy) from R3 are fixed, verified live, committed. Regression smoke clean (launch,
This-or-That end-to-end + B-001 auto-close, chat enc:v1 at rest, C-NAV-001 back->launcher).
Only E-OBS (P3) open — bg push channel, needs server change + user-gated functions deploy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:35:49 -05:00
null 0c8586fa9e qa(r3): Round 3 full re-QA (A-F) COMPLETE — 12 fixes hold; 5 new issues logged
Pass F: offline (cache renders, no crash), rotation (state preserved), process-death (~6 clean
restarts), concurrency (simultaneous game play synced) all OK.
RESULT: all 12 prior fixes re-verified holding live; no P0/P1, no security/encryption findings.
New: 2xP2 (B-004 intermittent guesser-stuck; E-003 game notif -> Play hub not the game),
2xP3 (A-OBS paywall copy [env]; E-OBS bg fallback channel); C-OBS resolved (debug menu is gated).
Not yet flawless (2 open P2) -> fix phase + Round 4 re-QA. Baseline restored (both free, 0 active).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:35:49 -05:00
null 682a9d8ea7 qa(r3): Pass E live notif tests — chat_message full chain OK; log E-003 + E-OBS
chat_message: deliver (bg) + content-free + tap deep-links to exact conversation with content loaded.
partner_started_game: deliver + content-free OK; tap -> generic Play hub (not the game).
E-003 (P2): game pushes routeFor->AppRoute.PLAY (not the specific game/results) despite 'Tap to join!';
  extend B-002's game-route resolver to notifications.
E-OBS (P3): backgrounded pushes use fcm_fallback_notification_channel, bypassing code-defined
  CHANNEL_GAMES/chat channels (server sends notification not data-only).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:35:49 -05:00
null afd81e8120 qa(r3): Pass C visual sweep + Pass D security re-audit clean
Pass C: ~14 screen-types in dark (Home, Play, all 7 games, paywall, Settings+Subscription+Appearance,
Today, Messages inbox, Conversation) render clean, no FATAL, no new contrast issues, 0 enc:v1 leaked
to UI. C-DS-001 holds. C-OBS: debug menu entries (verify BuildConfig.DEBUG-gated). Remaining standard
list/detail screens deferred (nav-drift).
Pass D: deployed rules re-audited (B-001 + D-001 fixes present, hasPremium/entitlements server-only,
ciphertext enforced, no catch-all); at-rest chat text + preview = enc:v1. D3 live deferred (3rd acct).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:35:49 -05:00
null c7140b1e10 qa(r3): Pass A + Pass B fully re-verified live
Pass A: neither->paywall, partner->couple-shared unlock, self->unlock, A-003 badges both
directions. New A-OBS (P3): paywall plan-load shows raw 'credentials issue' (env: no RC sandbox).
Pass B: all 7 game areas played; B-001 holds across 4 async types (auto-complete); B-002 clean
case works; B-003 + C-DS-001 hold; Date Match deck + Wheel + How Well + Desire Sync + ToT all PASS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:35:49 -05:00
null ddecfabcd9 qa(r3): How Well verified (5/5, B-001 holds); log B-004 intermittent guesser-stuck on WaitingForPartner
How Well two-device playthrough PASS x2 (subject+guesser, reveal correct, session auto-closes).
B-002 clean case works (Play now -> guess INTRO when subject done).
NEW B-004 (P2, intermittent): guesser can land on generic WaitingForPartnerScreen for How Well
during a rapid game-to-game transition and get stuck (screen only exits on session end).
Not reproduced in clean case; escalate to P1 if deterministic. Report-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:35:49 -05:00
null 185d27b921 qa(r3): re-QA round 3 — nav + premium + Desire Sync/This-or-That fixes re-verified live
Round 3 full re-QA in progress. Re-verified LIVE on both emulators (build ce7fc2e):
C-NAV-001 (back->launcher), C-CC-001 (single header), back-stack clean,
A-001 couple-shared (Sam free unlocks Desire Sync+Memory Lane), A-003 (0 badges),
D-001 (capsules load, no PERMISSION_DENIED), B-001 (2 consecutive games auto-close),
B-002 (Home Play-now resumes exact game), B-003 (coherent counts), C-DS-001 (dark contrast).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:35:49 -05:00
null 6eeb86320b qa(fix-phase): all P0-P3 FIXED (P3x4: A-003/B-003/E-002/F-OBS); severity board all clear 2026-06-25 10:14:18 -05:00
null 0c67c505be qa(fix-phase): mark B-002/C-CC-001/C-DS-001 (P2) FIXED+verified; P0-P2 all clear, P3x4 remain 2026-06-25 09:58:27 -05:00
null 1108a57c4a qa(fix-phase): mark B-001 + C-NAV-001 (both P1) FIXED+verified; severity P1 0 open/4 fixed 2026-06-25 09:37:38 -05:00
null 4e49b92be2 qa(round2-B2): Date Match PASS (3 mutual matches + live It's-a-match push + Matches view); PASS B COMPLETE all 7 games; Sam reverted to free 2026-06-24 23:31:18 -05:00
null f79b38c07c qa(round2-B2): Memory Lane PASS (capsule create+seal, encrypted at rest enc:v1:, cross-device sealed view, no crash); D1 capsule ciphertext verified live 2026-06-24 23:15:23 -05:00
null e76a84f5da qa(round2-B2): C-NAV-001 P1 CONFIRMED — back from Home resurfaces onboarding/auth (back stack not popped after login); clean cold-start repro 2026-06-24 23:07:28 -05:00
null 1303597d4a qa(round2-B2): Connection Challenges PASS (day-cycle synced both, streak ok, no crash); C-CC-001 P2 (duplicate header + double back) 2026-06-24 23:03:15 -05:00
null c71d858283 qa(round2-B2): Desire Sync PASS (free partner enters via A-001; 3 mutual desires revealed correctly, mismatches hidden, results match); B-003 P3 (confusing counts), C-DS-001 P2 (dark contrast) 2026-06-24 22:57:50 -05:00
null f8dc8119cb qa(round2-B2): This or That PASS (user-nav, 5/5, results match); B-001 escalated P3->P1 (Back to Play doesn't close finished session -> blocks next game); B-002 P2 (Play now lands on hub) 2026-06-24 22:42:18 -05:00
null 8fa922fb70 qa(round2): RESTART Pass B from game #1 (play-as-user) — coverage reset, build reinstalled both devices 2026-06-24 22:30:09 -05:00
null 21504098c2 qa(plan): Pass B — play-as-the-user mindset; report-first-then-workaround on any broken flow 2026-06-24 22:27:40 -05:00
null f9c6e42d92 qa(round2): Pass B — How Well full two-device playthrough PASS (5/5 predict, results match both) 2026-06-24 22:22:48 -05:00
null 60a6ce1dbf docs(qa): continue across auto-compaction without the user (file-state is authoritative)
Don't hand back when context fills: harness auto-summarizes + you continue from the committed
run-state + coverage. Can't self-invoke /compact and don't need to. Commit before interruptible
work; session-start ritual recovers stuck sessions. Only true blockers (denied gated action /
macOS) stop the run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 22:13:48 -05:00
null 693ecd28ef qa(round2): Pass B — This or That full two-device playthrough PASS (5/5, results match both, no crash) 2026-06-24 22:10:15 -05:00
null e7073fc5f8 qa(round2): R2-1 done — A-001 re-verified all features + free-gate; D-001 fixed. Pass B next 2026-06-24 22:04:54 -05:00
null b05a72605e fix(rules): add capsules + challenges member rules (D-001 P1) — Memory Lane/Challenges were broken
couples/{id}/capsules and /challenges had NO rules -> default-deny -> Memory Lane hung on
loader, Connection Challenges couldn't load (live PERMISSION_DENIED). Added member-read +
ciphertext-enforcing capsules rule (title/content/promptUsed = enc:v1:) and a challenges
rule (catalog-referenced progress). Deployed + verified live: both features load, 0 perm
errors. Found during Round-2 re-verify of A-001 (Memory Lane couple-shared also confirmed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 22:02:40 -05:00
null efe0ddbf29 qa: record standing authorization (deploy firestore rules + admin access)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:51:45 -05:00
null 84dd5f1152 docs(qa): senior-QA review additions — Pass F, env/matrix, migration, iOS-native dims
- Pass F (cross-cutting): concurrency/realtime races, lifecycle/process-death, network
  resilience, idempotency/rapid-input, time-dependent (daily rollover/streaks/capsules),
  account/couple lifecycle, crash reporting.
- Methodology: prefer Firebase emulator/staging over prod; device/OS matrix; automate the
  smoke; test-data hygiene.
- Pass D7: encryptionVersion 0->1->2 migration. Reporting/re-QA now A-F.
- iOS: iOS-native QA dims (Dynamic Type/VoiceOver/safe-area/edge-swipe-back/sizes),
  real-device/sandbox needs (App Attest/APNs/StoreKit), crypto golden vectors.
- Logged D-OBS: PERMISSION_DENIED on outcomes/challenges/capsules to investigate in Round 2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:44:02 -05:00
null 16ba464752 docs(qa): autonomous run-to-completion mode — never stop; unblock by fixing; finish to flawless
Adds Execution-mode directive: run all passes -> fixes -> re-QA continuously to a flawless
round without checking in; fix anything that BLOCKS progress inline (stale data, crash, build
break, broken nav) to keep going; context limits = checkpoint not stop. Only a denied gated
action (prod deploy / admin write / entitlement toggle) may be surfaced, after all other work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:28:45 -05:00
null 99f0ae0c49 docs(qa): Pass C also checks navigation from every entry point + back-stack/double-back
UI review now verifies each screen opens correctly from ALL its entry points (inbox/Discuss/
notification, Play/notification, paywall from each gate) and that back (system + in-app)
returns correctly with no dead-ends, exit-app surprises, or two-back/duplicate-stack issues.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:26:23 -05:00
null f121eab67f docs(qa): require a full one-time playthrough of each game (not just launch)
Pass B now mandates playing each game end-to-end on both devices (start -> every step ->
finish/reveal/results); launch-only = partial. Reflected in playbook, report run-state,
and coverage (full playthroughs owed in Round 2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:24:08 -05:00
null 6ca65ce7e9 qa(round1): close out — all P0-P2 fixed; P3 + deferred coverage tracked for round 2
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:08:22 -05:00
null ce12abb1a6 fix(notifications): route daily_question + challenge_day_ready taps (E-001 P2)
Client fromRemoteType mapped only daily_question_reminder/challenge_waiting; functions
send daily_question/challenge_day_ready too. Tapping those now deep-links to Today /
Connection Challenges. Also records Pass C (main screens clean) + Pass D (security clean).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:07:32 -05:00
null 58208fd443 qa(round1): A-001 fixed+verified (couple-shared premium); A-003 P3 logged (static badge)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 20:53:39 -05:00