Commit Graph

565 Commits

Author SHA1 Message Date
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 f47c8e2b64 feat(qa): clear Future.md backlog — inclusive gender, turn-aware copy, push budgets, paywall polish, auth rotator
Implements the QA improvement backlog from Future.md:
- Inclusive sex/gender options (Female/Male/Non-binary/Prefer not to say) in onboarding +
  Edit Profile; honest copy (Desire Sync is already gender-neutral, no tailoring fallback needed).
- Turn-aware Home "waiting to play" copy ("Your turn to play.").
- Partner-action/results pushes exempt from the weekly promotional rate-limit ceiling
  (NotificationRateLimiter); reminders still bound by it. Tests updated.
- Suppress the redundant results / "partner finished" push when the recipient is already on that
  game's screen — new ActiveGameSessionMonitor (mirrors ActiveThreadMonitor), wired into the
  This or That / How Well / Desire Sync VMs + Wheel results; guarded in PartnerNotificationManager.
- Paywall: retry-with-backoff, offline-aware error copy, Continue hidden until plans load.
- Privacy-message rotator on Sign up + Forgot password (Login already had it).

iOS illustrations were already wired into the Android empty states (no change needed).
Brand-glyph G-set remains in Future.md — blocked on generated art.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 16:00:58 -05:00
null 95cad84cb5 brand: loading state, themes, manifest, art preview, pairing screen updates 2026-06-25 15:24:46 -05:00
null fed91dbe46 brand: finalize app icon, brand docs, onboarding visuals, feature graphic 2026-06-25 14:52:21 -05:00
null 520eea2236 brand: update launcher foreground, feature graphic, auth visuals, brand docs 2026-06-25 14:48:57 -05:00
null 93b5599605 brand: add closer-mark SVGs (on-dark + on-light variants) 2026-06-25 14:44:31 -05:00
null 334cb079fa brand: update app icon, iOS assets, Android drawables, brand docs (Pass H) 2026-06-25 14:34:27 -05:00
null 450ddccd16 qa(plan): add Pass H Branding & artwork + ClaudeBrandingReview.md (house style + ChatGPT prompts)
- Pass H: consumer-mindset branding review of every screen; output = ready-to-paste ChatGPT image
  prompts; must lock the house style first (read brand docs + open existing illustrations) so all
  generated art matches the shipped artwork.
- ClaudeBrandingReview.md: canonical House Style prompt prefix + palette + negatives; screen-by-screen
  audit (every route); 12 illustration prompts (A1-A12) + glyph set + pack-art prompt, all reusing the
  house style; flags 'wire existing iOS art into Android' vs new generation.
- Future.md QA: non-art branding ideas (wire iOS illustrations to Android, consistent glyphs, rotate
  privacy messages on auth screens).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 13:46:06 -05:00
null 2545da5c8b qa(plan): play every depth x question-count + consumer mindset; add Future.md (QA backlog)
- Pass B: cover the full depth x round-length matrix (Light/Everyday/Deep/All x 5/10/15), not one combo;
  short+long, shallow+deep, every answer type.
- Methodology: THINK AS A CONSUMER (approach from many angles); capture works-but-could-be-better /
  feature ideas to Future.md '## QA' (kept separate from the ClaudeReport.md bug log).
- New Future.md seeded with 5 grounded QA improvement ideas (inclusive onboarding options, turn-aware
  'waiting to play' copy, rate-limit exemption for high-value pushes, suppress redundant results push,
  friendlier paywall error state).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 13:39:16 -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 765916a8ef build(functions): recompile dist for E-OBS + E-003 results-ready changes
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:40:38 -05:00
null 21b078a180 fix(notif): set FCM channel on remaining reminder senders (E-OBS)
Add android.notification.channelId to the 4 senders not covered by the earlier batch:
scheduledOutcomesReminder + dailyQuestionReminder + reengagement -> 'reminders';
sendGentleReminderCallable -> 'partner_activity'. Completes E-OBS so backgrounded pushes land on
their proper channels (importance/sound + per-category toggle) instead of fcm_fallback. tsc green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:39:59 -05:00
null aaab768cb0 fix(notif): deep-link results-ready pushes to per-session results/replay screen (E-003) 2026-06-25 12:35:49 -05:00
null 3de4178fd3 qa(plan): add varied gameplay styles, exhaustive nav fuzzing, Pass G account-creation/fake-account
- Pass B: vary style of play (lengths/moods/answer types, result patterns, turn orders, exit/resume
  styles, edge inputs) to hit different code paths.
- Pass C: 'take every avenue' exhaustive nav fuzzing — tap every element, every order, rapid/repeated
  input, interrupt mid-nav, hunt dead-ends/traps.
- Pass G (new): account creation happy path + validation + duplicate/conflict + fake/malicious
  creation attempts (live D3 non-member denial, invite-code abuse, App Check, self-premium).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:35:49 -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 d99fa6c6ea fix(paywall): show friendly plan-load error, not raw SDK message (A-OBS)
The paywall ErrorState rendered uiState.error verbatim, surfacing developer-facing billing/RC SDK
text ('There was a credentials issue. Check the underlying error for more details.') to users.
Now always shows friendly copy. Verified live: free user -> paywall -> 'We couldn't load
subscription options right now. Check your connection and tap to try again.' (no raw error).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:35:49 -05:00
null f1549c642c fix(games): add 'Join the game' escape to WaitingForPartner screen (B-004)
The generic WaitingForPartner screen only exited when the session became null, so a partner who
landed there for an async game they could actually play (every current game is async — both play
on their own device) was stuck waiting forever, recoverable only via Back to Games. Now the screen
resolves the active session's game route and offers a primary 'Join the game' action that drops the
user into the game (which auto-joins the session). Deterministic repro: QA starts How Well, Sam
opens a different game -> one-game lock routes Sam to WaitingForPartner -> 'Join the game' -> How
Well guess intro. Verified live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:35:49 -05:00
null 1b9d8cf8dc fix(notif): game pushes deep-link into the waiting game, not the Play hub (E-003)
partner_started_game / partner_completed_part now route to the specific game route
(gameRouteForType(payload.gameType)) so the game screen auto-joins the couple's active
session — fulfilling the 'Tap to join!' promise. Server already sends game_type in the FCM
data; client now parses it (AppMessagingService + MainActivity) into PartnerNotificationPayload
and routeFor maps it. game_results_ready stays on the hub pending a server change to also send
game_session_id (completed sessions aren't returned by getActiveSession, so the plain game route
would show setup). Verified live: backgrounded partner tapped the start-game push -> opened This
or That at 1/5 (joined), not the hub.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:35:49 -05:00
null a2b38485b1 docs(seed): align type names with repo schema — single_choice, this_or_that, answer config 2026-06-25 12:35:49 -05:00
null d1026c7312 docs(seed): split question guide into three documents — content guide, schema, rewrite plan 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 fa6d80602a docs(brand): add asset-system.md, cross-link from visual-identity.md 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 8e08823e83 fix(memorylane): propagate snapshot-listener errors so the screen doesn't hang (F-OBS P3)
observeCapsules swallowed listener errors (return@), so on PERMISSION_DENIED the flow never
emitted or closed and Memory Lane hung on its loading heart forever. Now close(err)s the
flow -> the ViewModel's existing onFailure -> ERROR state with Retry. (Root cause that
masked D-001.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 10:14:17 -05:00
null 46010508a9 fix(notifications): route partner_left/partner_deleted_account to Home (E-002 P3)
Added PARTNER_UNPAIRED type for the two real 'you are unpaired' pushes -> Home, where the
now-solo user gets the Invite CTA (matches body 'Tap to create a new invite'). Documented
that invite_created (server audit log, read:true) and spki (a crypto key-format string in
the RevenueCat webhook, not a notification) are false positives needing no routing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 10:14:16 -05:00
null f63188d97a fix(desiresync): clearer privacy counts on reveal (B-003 P3)
Per-person tiles showed '$total private' (e.g. '5 private'), contradicting the caption's
'N kept private' (e.g. '2 kept private'). Tiles now read just 'Private' (your individual
answers always stay private); the caption keeps the real shared/kept breakdown. Verified:
'You: Private / Sam: Private' + 'N shared, M kept private', no contradiction.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 10:14:15 -05:00
null f8ae15a41b fix(play): hide Premium badge on Desire Sync/Memory Lane cards when couple has premium (A-003 P3)
Threaded showPremiumBadge=!hasPremium into DesireSyncCard/MemoryLaneCard and gated the
lock badge behind it. The feature was already accessible (A-001) — only the static badge
was misleading. Verified: with couple premium the Play hub shows no Premium badge on them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 10:14:14 -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 a94f44d3ec fix(home): 'Play now' resumes the waiting game, not the generic hub (B-002 P2)
Resolve the active session's gameType to its resume route (gameRouteFor) and carry it on
HomeAction.gameRoute / HomeUiState.waitingGameRoute; HomeActionTarget.Game now navigates
there (fallback Play hub). Each game screen auto-joins the couple's active session on open,
so the Home 'Play now' CTA drops the user straight into the actual waiting game.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 09:58:26 -05:00
null 1fe4dea9c1 fix(desiresync): theme-aware reveal text for dark mode (C-DS-001 P2)
DesireMatchCard used a hardcoded dark plum (Color(0xFF3D1F2E)) for the shared-desire
text -> readable on the light card in light mode, but dim/low-contrast on the dark-tinted
card in dark mode. Switched to MaterialTheme.colorScheme.onSurface so it adapts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 09:58:25 -05:00
null edc00d2a5f fix(nav): drop Connection Challenges from shellBackRoutes (C-CC-001 P2)
The screen renders its own header (title + 'Pick a series…' subtitle + back) for both
the pick and active views, so the nav-scaffold app bar drew a SECOND identical header +
back arrow on top. Removed it from shellBackRoutes -> single header, single back.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 09:58:24 -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 a82c43ad90 fix(rules): allow completedByUsers on session update so finished games close (B-001 P1)
The sessions allow-update rule required affectedKeys().hasOnly(['status','completedAt']),
but the async-game completion path (markUserComplete) always writes completedByUsers, so
every 'I reached results' write was denied and the session stayed active forever -> the
couple was locked out of starting any new game (only the destructive 'End their game'
worked, since abandonSession only diffs status/completedAt). Rule now permits
['status','completedAt','completedByUsers'], lets any couple member record completion
progress, keeps startedByUserId immutable and status monotonic (active->completed).
Deployed + verified live: both finish a game -> session auto-completes (completedByUsers
=[both]) -> next game starts immediately (no 'Waiting for partner' block).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 09:37:37 -05:00
null ebd3b2ed1f fix(nav): clear onboarding/auth back stack on entry->Home (C-NAV-001 P1)
Navigating to Home from any entry route (onboarding/profile/pair/login/signup/forgot)
now resets the stack (popUpTo(0) inclusive) so Home is the back-stack root. Previously
the graph start (ONBOARDING) lingered under Home, so system Back from Home walked
backward into the onboarding carousel -> welcome/login, making a signed-in user look
logged out. Verified: Back from Home now exits the app to the launcher.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 09:37:36 -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 01feee8321 qa(round2-B2): Spin the Wheel PASS (free partner enters; spin->Date Night->both answer 10->reveal matches both, no crash) 2026-06-24 23:22:25 -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 3c9037d8e4 qa(round2-B2): How Well PASS (user-nav, predicted 4/5 w/ deliberate miss + scale, results match both, no crash); B-001 re-corroborated (Back to Play leaves session active) 2026-06-24 22:50:59 -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