diff --git a/ClaudeQACoverage.md b/ClaudeQACoverage.md index 7d698a10..8ea65be5 100644 --- a/ClaudeQACoverage.md +++ b/ClaudeQACoverage.md @@ -18,7 +18,7 @@ | B — Games lifecycle | R12: 4 async games full 2-device end-to-end (ToT Light×5, Wheel mixed-types, How Well asym, Desire Sync shared/private) + start/join/first-finisher/finish/results/back-stack; CC+MemoryLane+DateMatch render/core (full per R10) | ✅ pass (first-finisher nudge + C-NAV-002 + Ready=Start re-verified live; MemoryLane title/preview run-on → Future.md) | | C — Visual (light+dark) | R16: 9 theme-scan hits triaged → **3 reclassified** (1 @Preview false-pos [scanner now excludes], 2 dead `PlaceholderScreen` deleted) + **6 real FIXED** (BucketList/DateMatch/WheelHistory/QuestionThread tokens); theme-scan CRITICAL **9→0**. R18: **C-DARKART-002 FIXED** (all art follows in-app theme via uiMode-sync) — verified all 4 theme/art states; incidental confirms paywall/pack/Today/chat both directions. | ✅ theme-scan CRIT 0 · C-DARKART-002 fixed (pending 1 confirm) · ⚠️ **BRAND-DARK-COVERAGE (P3) open** — light-only illustrations, see `ClaudeBrandingReview.md` | | D — Security & encryption | R13 LIVE (rules/functions unchanged this session): D3 non-member GET couple+messages → 403; D5 self-grant entitlement PATCH → 403; member GET own couple → 200; D1 chat at-rest `enc:v1:`. D2/D4/D6/D7 carried R7/R10 | ✅ clean — cornerstone holds | -| E — Notifications | R18 LIVE: backgrounded QA → Sam chat → **FCM `notification` delivered** (`partner_activity`, high, vis=PRIVATE) title "Sam sent a message"/body "Tap to read and reply" — **no content in push** (privacy-safe); **tap deep-links to the correct conversation** (clean Home-baseline test). R16 cold-start smoke 6/6. R12 Pass-B trigger fan-out. | ✅ pass · delivery + privacy + warm deep-link confirmed R18 | +| E — Notifications | **R18b LIVE full suite:** cold-start crash-triage smoke **6/6 both emulators** (launcher + 5 push types killed→tap→opens&stays); **routing verified background→tap for 7 types on Sam + 3 on QA (both clients)** — chat→exact conversation, answered/daily_question→Today, started_game/completed_part→game screen, finished_game→per-session results, date_match→Matches; **foreground** game-start banner + chat bubble ✅; **malformed/stale** intents all graceful; **payload privacy** P0 clean (code audit of all 6 trigger payloads + at-rest D1 all `enc:v1:`). 0 FATAL. R18 warm chat deep-link carried. | ✅ pass · delivery + routing + privacy + both-client confirmed R18b · Doze/battery = needs-device | | F — Resilience | R12: concurrency (F-RACE-001 atomic-start code + R8 live) · process-death (smoke `am kill`×5 → push → cold-start recovered each) · offline(R9) | ✅ pass · time-travel + deletion-cascade deferred | | G — Account creation / fake-account | R10: abuse live via D3 (non-member denied, no self-grant) + invite rules; happy/validation R5-clean (unchanged) | ✅ pass | | H — Branding & artwork | R13: ToT redesign + Premium-unlock modal done. **2026-06-27 brand audit opened 2 backlogs.** | ⚠️ **BRAND-DARK-COVERAGE** (light-only illustrations need dark variants) + **BRAND-ICON-CUSTOM** (~60 generic Material icons → bespoke `glyph_*`) — full asset lists in `ClaudeBrandingReview.md` | @@ -94,6 +94,14 @@ Full live two-device run (games + messages): - **New speculative types — `not implemented → Future.md` (R8 code check, 0 files each):** `join_game`/`game_invite`, `partner_joined_game`, `game_abandoned`/`game_ended`, `date_plan_update`, `memory_capsule_created`, `challenge_day_completed`, `subscription_entitlement_changed`. Worthwhile ones (couple-premium-unlock push; join/end pushes) logged to `Future.md` `## QA`. Not counted as pass. - **partner_answered (couple-key reveal, 2026-06-26)** ✅ live both-client — `onAnswerWritten` fires on each answer; the second answer hits the **both-answered "Your answers are unlocked ✨"** copy (recipient already answered). `onAnswerRevealed` ✅ fires when `isRevealed` flips false→true → "opened your answers" push to the partner (witnessed live). Privacy gate (raw API): partner content **403** until both answer, **200** after; non-member **403** throughout. Reveal screen shows the partner's answer **both directions**. At-rest: content-free metadata + gated `enc:v1:` `secure/payload`. - **Deferred (Round 9):** the full implemented-type × {fg/bg/killed} matrix isn't exhaustively re-run live — implemented types are routing-code-verified + centralized in `PartnerNotificationType`; chat/game start/finish/results + date_match verified live (R3/R5/R6). +- **R18b (2026-06-28) — full live re-run, 0 FATAL:** + - **Cold-start crash-triage smoke 6/6 on BOTH emulators** (`qa/entrypoint_smoke.sh`): launcher + `partner_started_game`/`partner_completed_part`/`partner_finished_game`/`chat_message`/`partner_answered` each killed (`am kill`) → real push → tapped from shade → **opens & stays** (0 fail, 0 blocked). This is the shared cold-start path (splash/onCreate) where the splash-exit crash class hid — clean. + - **Routing (background→tap, landed-screen verified, not just "opens"):** Sam received all 7 types; QA received 3 (both-client). chat_message(conv=main)→**exact conversation thread** (composer + Seen); partner_answered & daily_question→**Today/daily-question**; partner_started_game & partner_completed_part(tot)→**game screen**; partner_finished_game(wheel,session)→**per-session wheel results** ("Here's how you each answered", completed→results not a dead active session); date_match→**"Your Matches"**. Every tap: correct destination, app alive, **0 FATAL**. + - **Foreground in-app delivery (onMessageReceived intercepts; no OS tray):** `partner_started_game`→**in-app banner** "Your partner started a game · This or That" with Join/dismiss ✅; `chat_message`→**draggable chat-head bubble** ✅ (verified via real open-thread→back→Home→send flow + a distinct conv id; the `conversation_id=main` suppression seen on a process-death-restored back stack is **by-design read-suppression**, `ActiveThreadMonitor`, that clears on normal back-nav — not a defect). + - **Malformed / stale intents (all graceful, 0 FATAL):** unknown type→no nav, no crash; chat_message w/o `conversation_id`→**Messages inbox** (fallback); partner_started_game w/o `game_type`→**Play hub** (fallback); partner_finished_game w/ **deleted session id**→graceful "waiting" state w/ Back-to-Play/End escape (no crash, no dead-end). + - **Payload privacy (P0) — code audit of every sender + at-rest D1:** `onMessageWritten`, `onGameSessionUpdate` (+ part-finished), `onAnswerWritten`, `onAnswerRevealed`, `createDateMatch`, `onCoupleLeave` — each `data` block carries only routing IDs (type, couple_id, conversation/question/game/session id) + optional **public** avatar URL; titles use only the partner's display name; bodies are static. **No message/answer/date/swipe content, no keys/invite codes/recovery phrases.** At-rest cross-check: latest 6 `conversations/main/messages` all `enc:v1:` (server-blind source). Cross-checks D6. + - **Real `onMessageWritten` live re-drive NOT re-run this round** (UI-automation thrash on the composer send button) → carried from **R18 live** (exact copy "Sam sent a message"/"Tap to read and reply", no content) + this round's payload code audit + at-rest D1. + - **Doze / battery-optimization / App-Standby delivery → `blocked → needs-device`** (emulators never enter these states; the #1 real-world "notifications don't work" cause). Run on a physical device before any store push (`dumpsys deviceidle force-idle`, app Optimized→Restricted). ## Pass F — Resilience / lifecycle / concurrency / time - **Concurrency race:** F-RACE-001 (P1) fixed + **re-confirmed live (R8):** simultaneous mood-tap on both devices → **1 session** (was 2); race-loser landed on WaitingForPartner → **"Join the game"** → joined the winner's session at the **same Q1** (shared reveal preserved). Archived. *(Minor pre-existing note: loser can alternatively land on Play hub; not seen this run.)* diff --git a/ClaudeReport.md b/ClaudeReport.md index 6be50882..ead10fdd 100644 --- a/ClaudeReport.md +++ b/ClaudeReport.md @@ -18,6 +18,7 @@ > to the archived-ID line below (full detail stays in git history). See **Report hygiene** in `ClaudeQAPlan.md`. ## Run-state (current) +- **R18b (2026-06-28) — Pass E full live re-run (user: "run ClaudeQAPLan pass E") — ✅ CLEAN, 0 P0/P1, 0 FATAL.** Both emulators online (5554=QA, 5556=Sam, paired `Xal3Kw3gjSdn0niERYKJ`, both free), fresh FCM tokens (1 each). **Cold-start crash-triage smoke 6/6 on BOTH** (`qa/entrypoint_smoke.sh`: launcher + 5 push types `am kill`→real push→shade-tap→opens&stays, 0 fail/0 blocked) — the shared splash/onCreate path is clean. **Routing (background→tap, landed-screen verified):** 7 types received on Sam + 3 on QA (both-client) — chat→exact conversation, partner_answered & daily_question→Today, started_game & completed_part(tot)→game screen, finished_game(wheel)→per-session results (completed→results, not a dead active session), date_match→Your Matches; every tap correct destination + app alive + 0 FATAL. **Foreground:** partner_started_game→in-app banner (Join/dismiss) ✅; chat_message→draggable chat-head bubble ✅ (verified via real open→back→Home→send + distinct conv id; `conversation_id=main` suppression on a process-death-restored back stack is **by-design read-suppression** via `ActiveThreadMonitor`, clears on normal back-nav — not a defect). **Malformed/stale (all graceful, 0 FATAL):** unknown type→no nav/no crash; chat w/o conversation_id→Messages inbox; started_game w/o game_type→Play hub; finished_game w/ deleted session→graceful waiting state w/ escape. **Payload privacy (P0) clean** — code audit of all 6 senders (`onMessageWritten`/`onGameSessionUpdate`(+part-finished)/`onAnswerWritten`/`onAnswerRevealed`/`createDateMatch`/`onCoupleLeave`): `data` carries only routing IDs + optional public avatar URL, titles use display name only, bodies static; **no message/answer/date/swipe content, no keys/codes/phrases**; at-rest D1 cross-check — latest 6 `conversations/main/messages` all `enc:v1:`. **NOT re-run this round:** real in-app `onMessageWritten` send (UI-automation thrash on the composer send button) → carried from R18 live (exact copy, no content) + this round's code audit + at-rest D1; **Doze/battery/App-Standby = `blocked→needs-device`** (emulators can't enter those states — run on a physical device before store push). **No app-code changes** (pure QA round); only `ClaudeReport.md` + `ClaudeQACoverage.md` touched (user commits). Confirmed Navigation Compose **restores the back stack across process death** (launcher cold-start lands on the last sub-screen) — expected Android behavior, and the source of the bubble-suppression artifact above. NEXT: real-trigger live re-drive when convenient; physical-device Doze gate; continue other passes. - **R18b (2026-06-28) — Future.md review → found+fixed a P0 (user: "review Future.md and do fixes if needed. verify bugs and why").** **O-ONBOARD-001 (P0) — onboarding CRASHES on the final slide for EVERY fresh install** (and the login/signup screen too). **Verified live before/after on `emulator-5558` (fresh, API 34):** old build → onboarding slide-3 `CtaSlide` → `FATAL EXCEPTION: java.lang.IllegalArgumentException: Only VectorDrawables and rasterized asset types are supported` at `PainterResources…loadVectorResource` ← `OnboardingScreen.kt:246`; fixed build → CtaSlide renders the logo + "Create account", and the signup screen (AuthLogoMark) renders too — full onboarding→signup reachable, 0 FATAL. **Why (root cause, git-confirmed):** `ic_launcher_foreground.xml` was a `` until commit **334cb07 "brand: update app icon"** which swapped it to a **``** wrapper; `painterResource` routes any `.xml` drawable through the VectorDrawable loader, which throws on a `` root. The two Compose call sites — `OnboardingScreen.kt` `CtaSlide` + `AuthVisuals.kt` `AuthLogoMark` — weren't updated. **Regression invisible to recurring QA** because 5554/5556 are past onboarding + logged-in (signed up before 334cb07); every fresh install since crashes. (Future.md's root-cause guess — background/aapt quirk — was wrong.) **Fix:** both sites now `painterResource(R.drawable.closer_launcher_foreground)` (the raster the `` wraps; same pattern `LoadingState.kt:146` already uses); the `` XML stays for the real adaptive launcher icon. Scanned the whole app — **no other `painterResource`-on-non-vector-XML remains** (only `ic_launcher_foreground`/`_monochrome` are ``; monochrome isn't used via painterResource). Also fixed the remaining BucketList **add-FAB** hardcoded `Color(0xFFB98AF4)` → `MaterialTheme.colorScheme.primary` (closes the Future.md "BucketList mixed dark/light" item — dialog was R16, FAB was the leftover; verified live light). **Build clean; 205 unit + 24 functions green; all 3 emulators on the fixed APK.** **Regression guard ADDED + proven:** `scripts/painter-xml-scan.sh` flags any `painterResource(R.drawable.X)` where `X` is a non-`` XML drawable (the exact crash class); demonstrated it catches the bug when reintroduced (exit 1) and passes clean on the fix (exit 0); wired into the plan's cheap-gates (step 3). Uncommitted (user commits): `OnboardingScreen.kt`, `AuthVisuals.kt`, `BucketListScreen.kt`, `scripts/painter-xml-scan.sh`, `ClaudeQAPlan.md`, `Future.md`, `ClaudeReport.md`, `ClaudeQACoverage.md`. NEXT: prune O-ONBOARD-001 after 1 confirm; the instrumented onboarding→signup smoke (androidTest, currently 0) remains a `Future.md` idea (would have caught this too). - **R18 (2026-06-28) — continuing full run (user: "why are you stopping?" → don't hand back at checkpoints).** Both emulators online (5554=Dark, 5556=Light, both reset to Device-default after testing); package is `closer.app` (launcher `closer.app/app.closer.MainActivity`). **C-DARKART-002 FIXED + verified live across all 4 theme/art states** (see Severity-board R18 note + the issue row): `MainActivity` now drives `AppCompatDelegate.setDefaultNightMode` from `ThemeMode` (sync initial read → no flicker loop; `LaunchedEffect` for runtime toggles), so every `painterResource` + BrandIllustration follows the in-app theme via the real Configuration uiMode. The previously-broken **pack-art banners now render DARK** in decoupled in-app-Dark + system-light, and the Today hero does too; symmetric in-app-Light + system-dark → light; both coupled states correct. **C-DARKART-001 re-confirmed.** The test-suite gate also caught **TEST-002** (flaky `MemoryCapsuleGenerator` determinism test — un-injected `System.currentTimeMillis()` clock violated the documented "pure" contract; **no production caller yet** so zero runtime impact, but it intermittently reddened the suite) → fixed by injecting `createdAtMillis`. **Build clean; 205 unit + 24 functions green.** Uncommitted (user commits): `MainActivity.kt`, `MemoryCapsuleGenerator.kt` (+ its test), `ClaudeReport.md`. DONE this round: **Pass A ✅ / B ✅ / E ✅ / L ✅ / P ✅** + **M-001 confirmed** (recommend prune). NEXT: prune C-DARKART-002 / TEST-002 / P-GRAMMAR-001 / M-001 after this confirm round; resolve **O-AGE-001** (P2 pre-ship age gate — product call); P3 backlogs (BRAND-DARK-COVERAGE, BRAND-ICON-CUSTOM, C-ORIENT-001); optional deeper re-runs of F/G/I (last full sweep R12). Board: **0 open P0/P1 · 1 open P2 (O-AGE-001) · 3 open P3**. **Pass A ✅ (R18, live, Sam free on 5556 — both members confirmed free via admin read):** premium gate enforced across **two distinct surfaces** — Desire Sync (game) → Paywall, premium **Boundaries** pack → Paywall; negative control: **Mixed** "Communication" pack opens and a **free prompt is accessible** (answer composer shown), so the gate isn't over-broad; **Free** filter shows a graceful "Nothing in free yet" empty state (catalog note: no fully-free packs). Paywall billing plans don't load on the non-GMS emulator ("Couldn't load plans / Try again") — expected Pass K env limit, degrades gracefully (no crash). - **R17 (2026-06-28) — continuing full run (user: "complete full run, don't stop").** Both emulators on R16 build (5554=Dark, 5556=Light); HEAD `8b7bbc2` + working-tree fixes. **Theme fixes confirmed LIVE (dark, 5554):** C-THEME-005 (Wheel-History lock → surfaceVariant/primary), C-THEME-008/009 (Date-Match heart→primaryContainer + count badge→error) — joining 001/002 from R16. **NEW finding C-DARKART-002 (P2):** dark-variant art doesn't render in the decoupled in-app-Dark + system-light state — pack art (`QuestionPackLibraryScreen:223` via `packArtworkRes`) + ~7 literal `painterResource` sites resolve `-night` off SYSTEM uiMode, not the in-app theme; **proven live** (in-app-Dark + system `auto` → light pack art; system night=yes → correct dark art). The BRAND-DARK-COVERAGE batch art is correct but only shows under system-night. **Pass D1 at-rest = CLEAN (admin read R17):** messages `text`, `lastMessagePreview`, Memory Lane capsule content+title, **all 4 game answers (this_or_that/desire_sync/how_well/wheel) + date_swipe `action`** all `enc:v1:`; only metadata in clear. **Pass D3 = CLEAN (live raw-API R17):** minted non-member token → couple doc / messages / capsules / desire_sync reads + premium self-grant all **DENIED 403** (`scratchpad/d3_negative.js`). **C-DARKART-002 fully diagnosed** (routing through BrandIllustration is insufficient — `createConfigurationContext` doesn't resolve `-night` for these resources; recommended fix = sync config uiMode to in-app theme; my probe edit reverted; tree clean; build+units green; theme-scan CRIT still 0). NEXT (R18): **C-DARKART-002 fix** (uiMode-sync, architectural) + re-verify C-DARKART-001 holds; **M-001** quiet-hours backgrounded-push re-test → prune; live-confirm C-THEME-004 + light-side spot-check; then Pass A (premium gate) / B (a game) / E (full notif) / L / P. Cornerstones D1+D3 ✅ this round.