# Future — ideas & improvements backlog Non-blocking ideas: things that work today but could be better, plus feature ideas. Actual bugs (broken/incorrect behavior) live in `ClaudeReport.md`, not here. ## Backup, restore & E2EE (follow-ons to R24) - **Option B — relay-and-delete for messages.** Now that E2EE backup + restore exist (R24), make the Room `conversation_cache` the **source of truth** and flip Firestore `messages` to a transient relay (TTL / delete-after-delivery), plus **back up media into the snapshot** (since `chat_media` would then be relay-deleted). This is the payoff the R24 backup unblocks. Rewire the chat read to local-first + live-merge (additive, flag-guarded — dedupe Room∪Firestore by message id). - **Backup trigger hardening.** Backups currently run opportunistically from `HomeViewModel.loadHome` (throttled). Move to **WorkManager** (deferred, retried, network/battery-constrained) + an app-background trigger for reliability; add a **resumable, paginated initial backfill** for very large existing histories. - **Settings visibility + control.** "Last backed up: {time} · {N} messages" indicator + manual "Back up now" / "Restore history", and an **opt-out** toggle for users who want zero server retention. Include a dedicated **"Recent restore activity"** list — the R24-b `restore_self_alert` entries already land in `notification_queue` as the raw audit; this surfaces them so the owner can review restores on their account in one place. - **Email-verification challenge for partner-assisted restore (strongest anti-account-takeover control).** Before a restore request is honored, send a code to the account's **registered email** and require the recipient to enter it. A phished-password attacker who lacks inbox access can't complete it (the couple-email match that defeats the on-screen identity check does NOT defeat this). Needs mail infra (SendGrid / a Firebase Auth action) — deferred for that reason. R24-b shipped the on-screen identity + confirm + owner self-alert as the pragmatic interim. - **Restore-request lifecycle cleanup.** A `restore_requests` doc left at `READY` (partner wrapped the key but the recipient never completed) leaves an ECIES `keybox` — ciphertext sealed only to the recipient, useless to anyone else, but untidy. Add a **scheduled cleanup** of expired requests (and their keyboxes). R24-b already enforces expiry at fulfil time and deletes any stale request before a re-request. - **Owner-alert precision.** The R24-b "was this you?" self-alert also reaches the *requesting* device (harmless). Optionally exclude it via a client-written token hint on the request (would add a rules-allowed field). - **Couple-key rotation / forward secrecy.** A couple-key compromise exposes all history incl. backups (no FS today). Add rotation (both devices re-key) — hard but the right long-term hardening. - **Server-independent anti-rollback freshness.** A malicious server could serve a stale manifest to hide recent messages; today mitigated by the `generation` counter + a Phase-1 Firestore cross-check. Add a signed/monotonic freshness signal for the Option-B world. ## UI _(No open UI defects. The P0 onboarding/auth crash filed here 2026-06-28 was fixed + verified live and moved to `ClaudeReport.md` as **O-ONBOARD-001** — root cause was `painterResource` on the `` `ic_launcher_foreground` (not the background, as originally guessed); fixed both `OnboardingScreen.CtaSlide` + `AuthVisuals.AuthLogoMark` to use the raster `closer_launcher_foreground`. The "Add to Bucket List mixed dark/light" item is also fully resolved — dialog tokens R16, add-FAB `Color(0xFFB98AF4)`→`primary` R18.)_ ## QA 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. - **🟡 STARTED (R20) — Instrumented / on-device test coverage (was 0 androidTest).** First cut shipped: `app/src/androidTest/.../ui/FirstRunRenderSmokeTest.kt` — an on-device **Compose render smoke** of the first-run screens (`CtaSlide` + `AuthLogoMark`, light + dark), the exact `painterResource` logo sites that crashed every fresh install in **O-ONBOARD-001**. It's the net for the "composes fine, crashes on first paint" class the JVM unit tests + static scanners structurally can't catch. **Proven (R20):** passes green on-device, and FAILS with the original `IllegalArgumentException: Only VectorDrawables…` when the bug is reintroduced. Infra added: `testInstrumentationRunner` + `ui-test-junit4` (build.gradle.kts); also un-blocked the androidTest source set (the stale `CanonicalVectorCaptureInstrumentTest` couldn't compile against `private deriveKey` → made it `@VisibleForTesting internal`). Run: `./gradlew :app:connectedDebugAndroidTest`. **Still to grow:** it's Hilt-/Firebase-free leaf-composable rendering only — extend toward a fuller sign-in → pair → answer daily Q → open a game → send a message flow (needs a Hilt test runner + fakes), and wire `connectedDebugAndroidTest` into the per-round gate / CI alongside `qa/entrypoint_smoke.sh`. *Prompted by:* the QA-plan render-coverage gap (O-ONBOARD-001 escaped because nothing rendered a composable). - **✅ 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 Sync, Connection Challenges, Memory Lane, Date Match, Plan Date, Question Packs, Bucket List, Past Games — Spin the Wheel keeps its full illustration), WaitingForPartner per-game glyph, and Settings (Subscription/Security/Privacy/ Delete). 4 unused have no clean slot (notif uses `ic_notification_closer`; Today uses hero art; quiet-hours uses its illustration; no export-data row exists). Full map in `ClaudeBrandingReview.md`. _(This-or-That backdrop redesign is Codex C-DARK-UI-001.)_ - **Minor proactive-notification gaps (low priority).** ~~No push when a partner *joins* your active game (`partner_joined_game`)~~ → **BUILT (2026-06-28, pending deploy):** the non-starter opening an active session writes `joinedByUsers` (rule-gated), `onGameSessionUpdate` notifies the starter " joined your game" with the joiner's avatar (one-time `joinNotifiedAt`); foreground shows the standardized in-app banner. **Still open:** no push when a partner *ends/abandons* a game (`game_ended`/`game_abandoned`) — the other partner sees it in-session / on WaitingForPartner, so nothing's broken, just less proactive. *Prompted by:* Pass E (R8) inventory. - **Clarify Connection Challenges day-progress when partners are out of step.** If one partner catches up a *missed* day ("Pick it back up") while the other doesn't, the two devices show different **"Day N of 7"** (seen R10: QA Day 4 vs Sam Day 3) even though the 🔥 streak stays in sync on both. Not broken (plausibly individual-pace-through-the-series by design), but two people in the same shared challenge seeing different day numbers is confusing — consider a shared "you're on Day N together" framing or a clearer caught-up/ahead indicator. *Prompted by:* Pass B (R10) Connection Challenges playthrough. ### Security hardening (defense-in-depth — not vulnerabilities; rules already hold) > **Canonical security doc: [`SECURITY.md`](SECURITY.md)** (2026-06-29) — full threat model, what's > protected vs. exposed (metadata), known caveats, and the **prioritized hardening roadmap**. The P0 > there (**enforce App Check on the backend**, independent audit, release hardening), P1 (encrypt > profile metadata, opt-in telemetry, recovery-phrase UX, biometric re-lock), and P2 (cert pinning, > multi-device keys, data export, key rotation) are the authoritative list; the two items below are the > older notes that fed into it. - **Enforce App Check on Firestore (currently OFF).** Round 7 raw-API test: an authenticated request with **no App Check token** (raw Firestore REST) returned `200` for a member — so rules are the *sole* gate. Rules correctly deny non-members/cross-couple (all `403`), so this is not a live hole, but enabling App Check enforcement on Firestore would block non-app clients entirely (defense-in-depth). *Prompted by:* R7 D3 raw-API angle. - **✅ DONE (R21) — Biometric app-lock now re-arms on background/timeout (was: only cold-start/process-death).** `MainActivity` observes the lifecycle: while the lock is on and the session is unlocked, it records when the app is backgrounded and **re-locks if it returns after >60s away** (`BIOMETRIC_RELOCK_GRACE_MS`) — so a picked-up, already-open phone re-prompts, not only on Activity recreation. The grace window avoids re-locking on quick task-switches (the biometric prompt, photo picker, share sheet). Code-complete + compiles; **live re-lock not yet driven** — emulators have no enrolled biometric/PIN, so verify on a physical device. (SECURITY.md rec #7.) > Artwork to generate (ChatGPT prompts, house-style-matched) lives in `ClaudeBrandingReview.md`, not here. ## Roadmap — features & strategy (consolidated from the old `FUTURE.md`, 2026-06-28) **Unbuilt games (Play Hub is live; these remain):** - **Would You Rather / Truth or Dare** — tiered sweet→spicy, consent-gated; reuses the deck + match engine. Strong **premium "spicy" tier** lever. - **Daily Sync / Rose-Bud-Thorn** — one-tap emotional check-in, see partner's; small/new, free retention driver. **"2026, not 2019" differentiators (strategic):** - **AI-personalized prompts** — server-side (latest Claude) generate/weight prompts from the couple's history/season/unexplored topics; add a `generatePrompts` callable (Cloud Functions already in place). Biggest modern lever; pairs with content-metadata routing below. - **Async + real-time hybrid** — partner-presence ("they're here now") for live co-play, everything still async-friendly (long-distance). - **Multi-modal answers** — voice/photo answers (esp. Memory Lane + daily check-ins). - **Home/lock-screen widgets / live updates** — glanceable today's-prompt + partner status (Glance/Wear surface). - **Gentle gamification** — forgiving "gentle streaks" (grace days) + shared wins, not loss-aversion/guilt. - **Consent-first spicy content** — Intimacy/Dare tiers opt-in, double-confirmed, reveal-only-on-overlap. **Cross-platform:** Android-first is fine for MVP; iOS is the strategic gap (couples split devices). Decision + plan live in `ClaudeiOSPlan.md` — don't start before release/trust polish. ## Product polish (consolidated from the old `FUTURE.md`) - **Skeleton/loading states over bare spinners (P8).** `LoadingState` exists but many screens still use a bare `CircularProgressIndicator`. Add skeletons for question lists, game histories, home modules, paywall offerings, sync/reveal waits. Acceptance: no primary route shows an isolated spinner on an otherwise blank screen. - **Paywall / store value framing (P12).** Paywall has benefits/restore/legal/RevenueCat; needs stronger value framing, real offering/trial clarity, screenshots/previews, and test coverage before release. (Overlaps Pass K/O.) - **Content metadata & personalization (P13).** The bank is clean; the next leap is *routing*, not more questions — tag mood/depth/relationship-stage/conflict-safe/intimacy-level/time-needed and extend the selection APIs so prompts adapt to skipped topics, relationship length, and recent answers. ## Release / pre-ship (consolidated + re-verified 2026-06-28) - **Real release config before any store submission.** Confirmed still open: `app/build.gradle.kts` `versionCode = 1` / `versionName = "0.1.0"` and `core/navigation/ExternalLinks.kt` legal URLs are placeholder TODOs (`https://closer.app/privacy|terms|subscription-terms`). _Already done:_ a release-blocking Gradle check now fails the build if `RC_API_KEY` is unset/placeholder (`build.gradle.kts:106–110`), so the old "add a gradle guard" sub-item is closed. Remaining: set real version, real legal/support URLs, real RC key + verify offerings/purchase/restore on internal testing. (Tracked by **Pass O** release-readiness.) ## Help & support surface (consolidated — old "Notes to Consider") A future Help/Support screen could include: contact support · report a bug · send feedback · FAQ · subscription/billing help · pairing help · recovery-phrase/account help · app version + build number · optional "copy diagnostics" button.