feat: wire theme-scan.sh into QA docs, fix script Tier 2, file 9 C-THEME P2 defects

This commit is contained in:
null 2026-06-28 10:34:55 -05:00
parent 4deed13845
commit 9949200f47
17 changed files with 38 additions and 16 deletions

View File

@ -62,6 +62,11 @@ first** against the existing contact sheet before saving more assets.
dark surface, **same composition + flat 2D vector style + transparent/feathered edges** as the light version, exported to dark surface, **same composition + flat 2D vector style + transparent/feathered edges** as the light version, exported to
`drawable-night-nodpi/` with the **identical filename**. Re-run Pass C's decoupled-theme check after adding each. `drawable-night-nodpi/` with the **identical filename**. Re-run Pass C's decoupled-theme check after adding each.
> **⛔ CLAUDE — also run `scripts/theme-scan.sh` Tier 1E** every round: any direct `painterResource(R.drawable.illustration_*`
> or `painterResource(R.drawable.pack_art_*)` not routed through `BrandIllustration` will fail to follow the decoupled
> in-app theme and should either be converted to `BrandIllustration` or added to this table as a missing-variant candidate.
> The scanner is allowed to be improved; document any new patterns in its header.
## Icon/glyph audit — generic Material icons to replace with custom Closer glyphs (backlog: make these) ## Icon/glyph audit — generic Material icons to replace with custom Closer glyphs (backlog: make these)
> Audited 2026-06-27: **~60 distinct Material icons across ~201 call sites.** Brand rule #2 — each becomes a bespoke > Audited 2026-06-27: **~60 distinct Material icons across ~201 call sites.** Brand rule #2 — each becomes a bespoke
> `glyph_*` in the house style. Existing custom glyphs (reuse/extend, don't regress): the G/G2 set > `glyph_*` in the house style. Existing custom glyphs (reuse/extend, don't regress): the G/G2 set

View File

@ -14,7 +14,7 @@
|---|---|---| |---|---|---|
| A — Couple-shared premium | R13: **A-201 confirmed live → pruned** (free QA → Date Match Love ★Premium → Paywall, deck didn't advance) + Desire Sync free→Paywall re-verified; couple-shared unlock holds | ✅ pass (all gates couple-shared incl. Date Match) | | A — Couple-shared premium | R13: **A-201 confirmed live → pruned** (free QA → Date Match Love ★Premium → Paywall, deck didn't advance) + Desire Sync free→Paywall re-verified; couple-shared unlock holds | ✅ pass (all gates couple-shared incl. Date Match) |
| 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) | | 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) | R13: ToT setup+gameplay both themes, Play/Home/Paywall insets, Today/Paywall heroes, conversation, Premium modal both themes — all verified live | ✅ R13 fixes pruned · ⚠️ **BRAND-DARK-COVERAGE (P3) open** — 2026-06-27 audit: many illustrations light-only (no dark variant); see `ClaudeBrandingReview.md` | | C — Visual (light+dark) | R13: ToT setup+gameplay both themes, Play/Home/Paywall insets, Today/Paywall heroes, conversation, Premium modal both themes — all verified live | ✅ R13 fixes pruned · ⚠️ **C-THEME-001..009 (P2) open** — 9 hardcoded light/dark surfaces/backgrounds surfaced by `scripts/theme-scan.sh`; see ClaudeReport.md · ⚠️ **BRAND-DARK-COVERAGE (P3) open** — 2026-06-27 audit: many illustrations light-only (no dark variant); 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 | | 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 | R12 LIVE: Pass B verified start/first-finisher(`partner_completed_part`)/finish triggers→correct partners+copy; cold-start tap smoke **6/6** (launcher + 5 notif types open & stay) | ✅ pass · splash-crash class clean on fresh APK | | E — Notifications | R12 LIVE: Pass B verified start/first-finisher(`partner_completed_part`)/finish triggers→correct partners+copy; cold-start tap smoke **6/6** (launcher + 5 notif types open & stay) | ✅ pass · splash-crash class clean on fresh APK |
| 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 | | 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 |
@ -29,7 +29,7 @@
| O — Release build & store readiness | **Not started.** All QA to date is on the **debug** APK. Minified release build, signing/AAB, App Check enforcement, i18n/RTL, App-Links, Play Data-Safety = pre-ship gate, not yet run. | ❌ **todo (pre-ship gate)** | | O — Release build & store readiness | **Not started.** All QA to date is on the **debug** APK. Minified release build, signing/AAB, App Check enforcement, i18n/RTL, App-Links, Play Data-Safety = pre-ship gate, not yet run. | ❌ **todo (pre-ship gate)** |
| P — Content, copy & language | R15: UI-microcopy swept (warm/inclusive; debug rows `BuildConfig.DEBUG`-gated; friendly error fallbacks; on-brand privacy copy) + **question-bank audit live: 6103 Qs — 0 empty, 0 exact dupes, 0 placeholder tokens, complete/mutually-exclusive answer configs, good type variety, consent-framed sensitive content.** No typos/off-voice/non-inclusive copy found. | ✅ **pass** — copy + question bank clean | | P — Content, copy & language | R15: UI-microcopy swept (warm/inclusive; debug rows `BuildConfig.DEBUG`-gated; friendly error fallbacks; on-brand privacy copy) + **question-bank audit live: 6103 Qs — 0 empty, 0 exact dupes, 0 placeholder tokens, complete/mutually-exclusive answer configs, good type variety, consent-framed sensitive content.** No typos/off-voice/non-inclusive copy found. | ✅ **pass** — copy + question bank clean |
**Archived issue IDs (fixed + confirmed, detail in git):** A-001 · A-003 · A-201 · A-OBS · B-001 · B-002 · B-003 · B-004 · C-CC-001 · C-DARKART-001 · C-DARK-UI-001 · C-DARK-UI-002 · C-DARK-UI-003 · C-DS-001 · C-ART-EDGE-001 · C-ART-EDGE-002 · C-HOME-001 · C-NAV-001 · C-NAV-002 · C-NAV-003 · C-PW-001 · C-SEC-001 · D-001 · E-001 · E-002 · E-003 · E-GAME-002 · E-GAME-003 · E-OBS · F-OBS · F-RACE-001 · I-001 · I-002 · J-OBS. **R15: 0 open P0P2; 3 fixed pending 1 confirm (M-001 quiet hours, N-001 Bucket List, N-002 Date Builder); 2 open P3 brand backlogs.** **Archived issue IDs (fixed + confirmed, detail in git):** A-001 · A-003 · A-201 · A-OBS · B-001 · B-002 · B-003 · B-004 · C-CC-001 · C-DARKART-001 · C-DARK-UI-001 · C-DARK-UI-002 · C-DARK-UI-003 · C-DS-001 · C-ART-EDGE-001 · C-ART-EDGE-002 · C-HOME-001 · C-NAV-001 · C-NAV-002 · C-NAV-003 · C-PW-001 · C-SEC-001 · D-001 · E-001 · E-002 · E-003 · E-GAME-002 · E-GAME-003 · E-OBS · F-OBS · F-RACE-001 · I-001 · I-002 · J-OBS. **R15: 9 open P2 theme defects (C-THEME-001..009); 3 fixed pending 1 confirm (M-001 quiet hours, N-001 Bucket List, N-002 Date Builder); 2 open P3 brand backlogs.**
--- ---
@ -63,6 +63,15 @@ Note: exit each game via "Back to Play" between games so the session closes (B-0
## Pass C — Visual (light + dark), all ~50 routes ## Pass C — Visual (light + dark), all ~50 routes
~14 screen-types swept Dark (5554) + several Light (5556): all render clean, readable, no FATAL, no dark-mode contrast issues; **0 `enc:v1:` leaked to conversation UI**. Covered: Home, Play hub, all 7 game screens (setup/play/reveal), Paywall, Settings (+Subscription +Appearance), Today/daily-question (+answer detail), Messages inbox, Conversation (image+voice+text+reaction). Back-stack clean (deep→hub→Home→launcher, no double-back). ~14 screen-types swept Dark (5554) + several Light (5556): all render clean, readable, no FATAL, no dark-mode contrast issues; **0 `enc:v1:` leaked to conversation UI**. Covered: Home, Play hub, all 7 game screens (setup/play/reveal), Paywall, Settings (+Subscription +Appearance), Today/daily-question (+answer detail), Messages inbox, Conversation (image+voice+text+reaction). Back-stack clean (deep→hub→Home→launcher, no double-back).
- **Theme-scan execution tracking (MANDATORY):** every Pass C round must record:
| Round | Scanner run | CRITICAL | MAJOR | REVIEW | Findings filed to ClaudeReport.md |
|---|---|---|---|---|---|
| R15 | ✅ | 9 | 0 | 0 | ✅ |
| R16 | — | — | — | — | — |
R15 scanner findings: 9 CRITICAL hardcoded light/dark surface/background colors filed as C-THEME-001..009
(BucketList ×2, WheelCompleteScreen, QuestionThreadScreen, WheelHistoryScreen, PlaceholderScreen ×2,
DateMatchScreen ×2).
- **R10 re-sweep (both themes where relevant):** Messages inbox ✅ (dark+light: conversations, avatars, unread dot, previews decrypted, no `enc:` leak), Conversation ✅ (image/voice/text/reaction/read-receipt/date-sep, E2E lock glyphs, correct attribution both dirs), per-question Discussion thread ✅, Today/daily-question ✅ (dark+light, paired-books art on-brand), Activity/Together ✅ (dark). **5 P2 found:** C-HOME-001 (Home shows top pending action twice — `primaryAction` hero + `buildPendingActions` row), C-NAV-002 (wheel results→BACK re-enters finished play screen, no popUpTo), C-NAV-003 (Wheel History/Past Games + PartnerHome double app-bar — route in `shellBackRoutes` while screen owns a TopAppBar; C-CC-001 class), C-PW-001 (dark paywall "What's included" pills light-on-light, `BenefitPill` onSurface text), C-SEC-001 (accepter recovery copy). Premium-locked Wheel History state renders. Date Builder · Question Packs(gated→paywall) · Answer Reveal sealed = token-consistent, R9-clean. - **R10 re-sweep (both themes where relevant):** Messages inbox ✅ (dark+light: conversations, avatars, unread dot, previews decrypted, no `enc:` leak), Conversation ✅ (image/voice/text/reaction/read-receipt/date-sep, E2E lock glyphs, correct attribution both dirs), per-question Discussion thread ✅, Today/daily-question ✅ (dark+light, paired-books art on-brand), Activity/Together ✅ (dark). **5 P2 found:** C-HOME-001 (Home shows top pending action twice — `primaryAction` hero + `buildPendingActions` row), C-NAV-002 (wheel results→BACK re-enters finished play screen, no popUpTo), C-NAV-003 (Wheel History/Past Games + PartnerHome double app-bar — route in `shellBackRoutes` while screen owns a TopAppBar; C-CC-001 class), C-PW-001 (dark paywall "What's included" pills light-on-light, `BenefitPill` onSurface text), C-SEC-001 (accepter recovery copy). Premium-locked Wheel History state renders. Date Builder · Question Packs(gated→paywall) · Answer Reveal sealed = token-consistent, R9-clean.
- **R9 deferred sweep — 0 new issues:** Answer History, Together/Activity, Bucket List (empty state + FAB), Date Match deck, Date Matches all render cleanly in **dark** (good contrast, no clipping, no FATAL); Privacy & Terms + Home confirm **light** parity (shared Material3 tokens). Remaining standard list/detail (Wheel History · Date Builder · Past Games · Answer Reveal sealed · Question Packs[gated→paywall]) are token-consistent with the above; fresh-account auth/onboarding visual covered R3/R5. No C findings. - **R9 deferred sweep — 0 new issues:** Answer History, Together/Activity, Bucket List (empty state + FAB), Date Match deck, Date Matches all render cleanly in **dark** (good contrast, no clipping, no FATAL); Privacy & Terms + Home confirm **light** parity (shared Material3 tokens). Remaining standard list/detail (Wheel History · Date Builder · Past Games · Answer Reveal sealed · Question Packs[gated→paywall]) are token-consistent with the above; fresh-account auth/onboarding visual covered R3/R5. No C findings.

View File

@ -47,21 +47,28 @@
|---|---|---| |---|---|---|
| P0 | 0 | 0 | | P0 | 0 | 0 |
| P1 | 0 | **1** (N-001 Bucket List) | | P1 | 0 | **1** (N-001 Bucket List) |
| P2 | **0** | **2** (M-001 quiet hours, N-002 Date Builder) | | P2 | **9** (C-THEME-001..009) | **2** (M-001 quiet hours, N-002 Date Builder) |
| P3 | **2** (BRAND-DARK-COVERAGE, BRAND-ICON-CUSTOM) | **0** | | P3 | **2** (BRAND-DARK-COVERAGE, BRAND-ICON-CUSTOM) | **0** |
_R15: found + FIXED **3 bugs****M-001** (P2 quiet hours), **N-001** (P1 Bucket List non-functional), **N-002** (P2 _R15: found + FIXED **3 bugs****M-001** (P2 quiet hours), **N-001** (P1 Bucket List non-functional), **N-002** (P2
Date Builder "Create Plan" no-op) — all verified live, pending 1 confirm. **0 open P0P2.** 2 P3 brand-asset backlogs Date Builder "Create Plan" no-op) — all verified live, pending 1 confirm. **9 new open P2 theme defects** surfaced by
open._ `scripts/theme-scan.sh` (C-THEME-001..009). 2 P3 brand-asset backlogs remain open._
## Issues — open (brand-asset backlogs, P3) ## Issues — open (Pass C theme defects + brand-asset backlogs)
> Surfaced by the 2026-06-27 brand standards audit (new Pass H/Pass C mandates). Both are **brand-quality defects** > Surfaced by the 2026-06-27 brand standards audit (new Pass H/Pass C mandates) and the 2026-06-28 theme-scan run. Brand-quality defects (light-only art, generic icons) and Pass C theme defects (hardcoded surface/background colors) both live here; asset lists + prompts are in `ClaudeBrandingReview.md`.
> (the app functions): light-only art shows on dark; generic icons aren't on-brand. Asset lists + prompts to make live
> in `ClaudeBrandingReview.md` (Image theme-variant coverage · Icon/glyph audit).
| ID | Sev | Area | Description | Suggested fix | Status | | ID | Sev | Area | Description | Suggested fix | Status |
|---|---|---|---|---|---| |---|---|---|---|---|---|
| N-002 | P2 | Dates / Date Builder | **"Plan a Date" / Date Builder "Create Plan" was a no-op.** `DateBuilderViewModel.savePreference()` bailed on `state.dateIdeaId.isEmpty()` (no entry ever calls `setDateIdeaId`), built a `DatePlanPreference` with empty `coupleId`, and wrote to `date_plan_preferences` which **no screen reads**. Net: fill form → Create Plan → nothing saved, no error. | Re-point the builder to create a real **PLANNED `DatePlan`** via `repository.savePlan()` (the collection Home already displays via `getPlansByStatus(PLANNED)`), resolving `coupleId` from `CoupleRepository`; dropped the dead `dateIdeaId` guard. _(Product note: this makes the existing single-user form work end-to-end → Home "Date coming up"; the model's older "generate from BOTH partners' prefs" vision is unbuilt — revisit if that's wanted.)_ | **Fixed — verified live R15** (Create Plan → `date_plan` status=planned, `enc:v1:` duration; Home shows "Date coming up"). Client-only. Pending 1 confirm. | | N-002 | P2 | Dates / Date Builder | **"Plan a Date" / Date Builder "Create Plan" was a no-op.** `DateBuilderViewModel.savePreference()` bailed on `state.dateIdeaId.isEmpty()` (no entry ever calls `setDateIdeaId`), built a `DatePlanPreference` with empty `coupleId`, and wrote to `date_plan_preferences` which **no screen reads**. Net: fill form → Create Plan → nothing saved, no error. | Re-point the builder to create a real **PLANNED `DatePlan`** via `repository.savePlan()` (the collection Home already displays via `getPlansByStatus(PLANNED)`), resolving `coupleId` from `CoupleRepository`; dropped the dead `dateIdeaId` guard. _(Product note: this makes the existing single-user form work end-to-end → Home "Date coming up"; the model's older "generate from BOTH partners' prefs" vision is unbuilt — revisit if that's wanted.)_ | **Fixed — verified live R15** (Create Plan → `date_plan` status=planned, `enc:v1:` duration; Home shows "Date coming up"). Client-only. Pending 1 confirm. |
| C-THEME-001 | P2 | Dates / Bucket List | **AddItemDialog uses a hardcoded light surface.** `Surface(color = Color.White)` in `BucketListScreen.kt:406` keeps the dialog background light regardless of the in-app dark theme, producing a light dialog on a dark screen. | Replace `Color.White` with `MaterialTheme.colorScheme.surface` and ensure text/input colors use `onSurface` tokens. | **Open** |
| C-THEME-002 | P2 | Dates / Bucket List | **CategoryBadge uses a hardcoded light color.** `Surface(color = Color(0xFFF3E8FF))` in `BucketListScreen.kt:379` renders a light-purple chip in dark mode. | Replace with a theme-aware container color (e.g., `primaryContainer` / `surfaceVariant`) and ensure label text uses the matching `on*` token. | **Open** |
| C-THEME-003 | P2 | Wheel | **WheelCompleteScreen uses a hardcoded light background.** `Box(Modifier.background(Color(0xFFFFFBFE)))` in `WheelCompleteScreen.kt:507` does not adapt to dark mode. | Replace with `MaterialTheme.colorScheme.background` or route through a themed surface. | **Open** |
| C-THEME-004 | P2 | Questions / Discussion thread | **WaitingPhase banner uses a hardcoded light surface.** `Surface(color = Color.White.copy(alpha = 0.78f))` in `QuestionThreadScreen.kt:168` keeps the "Your answer is saved" banner light in dark mode. | Use `surfaceVariant` or another theme container color with appropriate alpha, or draw the banner fully from theme tokens. | **Open** |
| C-THEME-005 | P2 | Wheel / History | **History locked-state icon uses a hardcoded light surface.** `Surface(color = Color(0xFFF8F1FF))` in `WheelHistoryScreen.kt:356` behind the lock icon does not adapt. | Replace with `surfaceVariant` / `surfaceContainerHighest` and the lock icon with `primary` or `onSurfaceVariant`. | **Open** |
| C-THEME-006 | P2 | Components / PlaceholderScreen | **SignalChip uses a hardcoded light surface.** `Surface(color = Color.White.copy(alpha = 0.72f))` in `PlaceholderScreen.kt:213` plus a hardcoded white border gradient keeps the chip light in dark mode. | Replace with `surfaceVariant` / `surfaceContainer` and a theme-aware border gradient. | **Open** |
| C-THEME-007 | P2 | Components / PlaceholderScreen | **PreviewPanel uses a hardcoded light surface.** `Surface(color = Color.White.copy(alpha = 0.78f))` in `PlaceholderScreen.kt:243` renders a light panel in dark mode. | Replace with `surface` / `surfaceVariant` or a theme-aware scrim. | **Open** |
| C-THEME-008 | P2 | Dates / Date Match | **Match CTA chip uses a hardcoded light surface.** `Surface(color = Color(0xFFF3E8FF))` in `DateMatchScreen.kt:240` behind the "View matches" heart icon does not adapt. | Use `primaryContainer` / `surfaceVariant` and a theme-aware icon tint. | **Open** |
| C-THEME-009 | P2 | Dates / Date Match | **Match badge uses a hardcoded dark-red surface.** `Surface(color = Color(0xFF8D2D35))` in `DateMatchScreen.kt:251` for the match count badge uses a fixed color that doesn't follow the theme. | Use `error` / `tertiaryContainer` / `primaryContainer` with matching `on*` text so it adapts to both themes. | **Open** |
| N-001 | P1 | Dates / Bucket List | **Bucket List was entirely non-functional**`setCoupleId` was never called, so `coupleId` stayed `""` and `addItem`/`loadItems`/`toggleComplete`/`deleteItem` all silently `return`ed. Items could never be added, loaded, completed, or deleted. | `BucketListViewModel` resolves the couple itself in `init` via `CoupleRepository.getCoupleForUser()` (mirrors `MemoryLaneViewModel`), then `setCoupleId``loadItems`. | **Fixed — verified live R15** (add persists `enc:v1:`; complete sets flags; delete removes; list renders). Client-only, no deploy. Pending 1 confirm. | | N-001 | P1 | Dates / Bucket List | **Bucket List was entirely non-functional**`setCoupleId` was never called, so `coupleId` stayed `""` and `addItem`/`loadItems`/`toggleComplete`/`deleteItem` all silently `return`ed. Items could never be added, loaded, completed, or deleted. | `BucketListViewModel` resolves the couple itself in `init` via `CoupleRepository.getCoupleForUser()` (mirrors `MemoryLaneViewModel`), then `setCoupleId``loadItems`. | **Fixed — verified live R15** (add persists `enc:v1:`; complete sets flags; delete removes; list renders). Client-only, no deploy. Pending 1 confirm. |
| M-001 | P2 | Settings / notifications | **Quiet hours did not suppress backgrounded/killed partner pushes.** "Quiet hours — 10 PM8 AM, no notifications" was stored **local-only** (DataStore); partner pushes carry a `notification` block the OS shows directly when the recipient is backgrounded/killed, and the only client check (`PartnerNotificationManager.isInQuietHours`) runs **foreground-only** (`AppMessagingService.onMessageReceived`). So the "no notifications" promise was broken for the main case. Repro: Sam QH ON @22:28 CST, backgrounded → QA chat → "QA sent a message" posted to Sam's shade. | Client mirrors window+tz to `users/{uid}`; Cloud Functions (`onMessageWritten`/`onAnswerWritten`/`onAnswerRevealed`/`onGameSessionUpdate`) suppress via fail-open `notifications/quietHours.ts:recipientInQuietHours()`; `firestore.rules` user-doc allowlist extended for `quietHours*`+`timezone`. | **Fixed — verified live R15** (fn log suppress vs notify; deployed prod). Pending 1 confirm. | | M-001 | P2 | Settings / notifications | **Quiet hours did not suppress backgrounded/killed partner pushes.** "Quiet hours — 10 PM8 AM, no notifications" was stored **local-only** (DataStore); partner pushes carry a `notification` block the OS shows directly when the recipient is backgrounded/killed, and the only client check (`PartnerNotificationManager.isInQuietHours`) runs **foreground-only** (`AppMessagingService.onMessageReceived`). So the "no notifications" promise was broken for the main case. Repro: Sam QH ON @22:28 CST, backgrounded → QA chat → "QA sent a message" posted to Sam's shade. | Client mirrors window+tz to `users/{uid}`; Cloud Functions (`onMessageWritten`/`onAnswerWritten`/`onAnswerRevealed`/`onGameSessionUpdate`) suppress via fail-open `notifications/quietHours.ts:recipientInQuietHours()`; `firestore.rules` user-doc allowlist extended for `quietHours*`+`timezone`. | **Fixed — verified live R15** (fn log suppress vs notify; deployed prod). Pending 1 confirm. |
| BRAND-DARK-COVERAGE | P3 | Art / theme | Most illustrations are **light-only** — only 12 of ~25 have a `drawable-night-nodpi/` dark variant. All `illustration_couple_*` heroes (paywall/subscription/onboarding/invite/history), `daily_question`, `partner_activation`, `tonight_partner_prompt`, `together_empty`, and **all 10 `pack_art_*` banners** show the **light/pink image on a dark screen** (feathered edges don't change the image colors). | Generate dark/aubergine-palette variants for each light-only asset → `drawable-night-nodpi/` (identical filename); `BrandIllustration` auto-selects per in-app theme. Re-run the decoupled-theme check. List in `ClaudeBrandingReview.md`. | **Open (P3)** | | BRAND-DARK-COVERAGE | P3 | Art / theme | Most illustrations are **light-only** — only 12 of ~25 have a `drawable-night-nodpi/` dark variant. All `illustration_couple_*` heroes (paywall/subscription/onboarding/invite/history), `daily_question`, `partner_activation`, `tonight_partner_prompt`, `together_empty`, and **all 10 `pack_art_*` banners** show the **light/pink image on a dark screen** (feathered edges don't change the image colors). | Generate dark/aubergine-palette variants for each light-only asset → `drawable-night-nodpi/` (identical filename); `BrandIllustration` auto-selects per in-app theme. Re-run the decoupled-theme check. List in `ClaudeBrandingReview.md`. | **Open (P3)** |

View File

@ -13,6 +13,13 @@ Themes
Improvement & feature ideas surfaced while QA-testing as a consumer (each works today — none are defects). Improvement & feature ideas surfaced while QA-testing as a consumer (each works today — none are defects).
- **Tier 3: Compose screenshot diff for visual regression.** The static scanner in `scripts/theme-scan.sh`
catches ~80% of light/dark theme mismatches, but it cannot detect *compositional* failures: a theme-token
color used on the wrong surface, a gradient with hardcoded light stops, or subtle contrast collapse.
Implement a screenshot pipeline with Roborazzi / Shot / papAROS that renders every `AppRoute` in both
light and dark, pixel-diffs them, and fails on unexpected white backgrounds or invisible text. When done,
run it in CI against every UI PR.
- **✅ DONE — Consistent brand glyphs across game cards + waiting surfaces.** G-set + G2 (17 glyphs) in - **✅ DONE — Consistent brand glyphs across game cards + waiting surfaces.** G-set + G2 (17 glyphs) in
`res/drawable-nodpi/glyph_*.xml`; **13 wired + verified live:** every Play-hub card (This or That, How Well, Desire `res/drawable-nodpi/glyph_*.xml`; **13 wired + verified live:** every Play-hub card (This or That, How Well, Desire
Sync, Connection Challenges, Memory Lane, Date Match, Plan Date, Question Packs, Bucket List, Past Games — Spin the Sync, Connection Challenges, Memory Lane, Date Match, Plan Date, Question Packs, Bucket List, Past Games — Spin the

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

View File

@ -122,11 +122,6 @@ grep -rnE 'Brush\.(linear|vertical|horizontal|radial)Gradient' "$UI_DIR" --inclu
log "" log ""
log "## Tier 2 — Theme definition validation" log "## Tier 2 — Theme definition validation"
if [[ -f "$THEME_FILE" ]]; then if [[ -f "$THEME_FILE" ]]; then
local slots_file=$(mktemp)
grep -oE '\b([a-z]+(Container|Surface|Primary|Secondary|Tertiary|Error|Scrim|Tint)?)\s*=' "$THEME_FILE" \
| grep -v '^//' \
| sed 's/\s*=//' > "$slots_file"
required=( required=(
primary onPrimary primaryContainer onPrimaryContainer primary onPrimary primaryContainer onPrimaryContainer
secondary onSecondary secondaryContainer onSecondaryContainer secondary onSecondary secondaryContainer onSecondaryContainer
@ -139,12 +134,11 @@ if [[ -f "$THEME_FILE" ]]; then
missing=0 missing=0
for slot in "${required[@]}"; do for slot in "${required[@]}"; do
if ! grep -qE "^${slot}$" "$slots_file"; then if ! grep -qE "^\s*${slot}\s*=" "$THEME_FILE"; then
log "⚠️ MISSING: \`$slot\` not explicitly defined in darkColors" log "⚠️ MISSING: \`$slot\` not explicitly defined in darkColors"
missing=$((missing+1)) missing=$((missing+1))
fi fi
done done
rm "$slots_file"
if [[ $missing -eq 0 ]]; then if [[ $missing -eq 0 ]]; then
log "✅ darkColors has all required Material3 slots explicitly defined" log "✅ darkColors has all required Material3 slots explicitly defined"