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