Commit Graph

345 Commits

Author SHA1 Message Date
null 7a92bc05f9 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 11:37:06 -05:00
null 15e2a1a884 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 11:32:32 -05:00
null 5e7ef19b8f docs(brand): add asset-system.md, cross-link from visual-identity.md 2026-06-25 11:26:21 -05:00
null 3c4a4133be 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 11:25:19 -05:00
null bb3b74f41e 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 11:18:07 -05:00
null 274fa5ca61 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 11:06:02 -05:00
null eb4db1a321 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 10:46:16 -05:00
null ce7fc2edcb 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:14 -05:00
null ab2ff8dbc7 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:14 -05:00
null 497e641a90 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:14 -05:00
null 652e3a7f4e 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:14 -05:00
null f1fdefb987 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 bc8f519778 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:24 -05:00
null c3c5438bcc 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:24 -05:00
null e0d33ea85d 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:24 -05:00
null eb9facc2d5 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 f4a7019c3c qa(fix-phase): mark B-001 + C-NAV-001 (both P1) FIXED+verified; severity P1 0 open/4 fixed 2026-06-25 09:37:36 -05:00
null c1ef8d6630 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:36 -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
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 bbd7ef0806 docs(qa): define per-pass chunk granularity (sub-batch to one context window)
Round-1 calibration: A & D fit as single batches; B/C/E overflowed and got deferred.
Add a batch-sizing table: B=1 game/chunk, C=1 screen-group/chunk, D=~4 sub-areas,
E=3-5 types/chunk, F=1 dimension/chunk. Chunk = largest unit that finishes+commits in one
window; commit + run-state update per chunk.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:47:04 -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 e907453f3f docs(plan): add Part 2 (build iOS to parity) + Part 3 (iOS QA) — ClaudeiOSPlan.md
Program now Part1 Android QA -> Part2 iOS build -> Part3 iOS QA + cross-platform.
iOS = native SwiftUI (iphone/ scaffold, audit stale at v0.2.0). Decisions: full
Tink-compatible E2EE (Android<->iOS decrypt), working-parity build (no App Store).
Hard constraint: iOS build/run/QA needs macOS (not this Linux box) — Linux = author
Swift + refresh audit only; compile/run/QA deferred to a Mac.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:37:26 -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
null e8892a9669 fix(premium): couple-shared premium everywhere (A-001)
Route all feature gates (Play hub, Desire Sync, Memory Lane, Connection Challenges,
Question Packs, wheel category/spin/history) through CouplePremiumChecker instead of
per-user EntitlementChecker. CouplePremiumChecker now exposes isPremium()/hasPremium()
that resolve the partner internally (self OR partner premium). Verified live: Sam premium →
QA enters Desire Sync; both free → QA → paywall.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 20:52:22 -05:00
null c54ceb16c3 qa(round1): Pass B games launch sweep — no crashes; stale session recovery verified (B-001 P3)
This or That / How Well / Connection Challenges / Spin the Wheel launch clean (no FATAL).
A stale active wheel session blocked all games; in-app 'End their game' recovery works.
Full two-device lifecycle partial this round. Report-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 20:04:54 -05:00
null 452aaf787a qa(round1): Pass A complete — couple-shared premium gap (A-001, P1)
Only chat uses CouplePremiumChecker; all other gates are per-user → a free partner of a
premium user stays locked. Confirmed live (Sam premium, QA still locked on Desire Sync +
Memory Lane). Report-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 19:59:04 -05:00
null 64f0a7e6c8 docs(qa): save full-app QA playbook (5 passes: premium, games, visual, security, notifications)
Reusable QA → fix → re-QA plan. Report-only passes with severity labels, then fix
one-at-a-time by severity, then re-QA until flawless. State/resume lives in ClaudeReport.md
+ ClaudeQACoverage.md. Not yet executed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 19:43:19 -05:00
null 6580299f05 feat(packs): route pack Discuss to the unified conversation
Replace the orphaned in-thread QuestionDiscussionThread (which wrote to question_threads
and sent no notifications) with a 'Discuss this together' button that opens the conversation
(q_<questionId>), same as the daily flow — so pack discussions are notified and appear in
the Messages inbox.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:55:40 -05:00
null 7a9ff31ae6 feat(chat): couple-shared premium gating for sending media + reactions
CouplePremiumChecker ORs self.isPremium with a live read of the partner's entitlement
doc (reactive). Composer photo/camera/voice buttons + keyboard GIF/sticker insert + the
reaction action gate on canSendMedia: locked buttons show a lock badge and route to the
existing PaywallScreen (with a chat_media paywall analytics event). Text/viewing/receiving
stay free. Rules: paired partner may read the entitlement doc. Verification pending deploy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:52:50 -05:00