Closer/Future.md

122 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.
## 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 `<bitmap>` `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.
- **Instrumented / on-device test coverage (currently 0 androidTest).** `app/src/test` has 19 solid unit tests
(encryption, rate limiter, quiet hours, streak, entitlement, …) but `app/src/androidTest` is empty — there is no
automated on-device net for UI behavior, navigation, or DB/DataStore integration; the live QA passes + scanners are the
only thing catching that class. Add a small **Compose UI / Espresso smoke** (sign-in → pair → answer daily Q → open a
game → send a message) wired into the per-round gate (alongside `qa/entrypoint_smoke.sh`), then grow it. *Prompted by:*
the R-? QA-plan gap review — the playbook now runs `./gradlew testDebugUnitTest` but has no instrumented suite to run.
- **✅ 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 "<Name> 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)
- **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.
- **Biometric app-lock re-locks on cold-start/process-death but maybe not plain background→resume.** R15 code review:
`MainActivity` gates `AppNavigation` behind `BiometricLockScreen` when `biometricLoginEnabled` and `sessionVerified`
is false; `sessionVerified` is a `remember{}` that resets on Activity recreation (cold-start, process death) — so the
lock re-arms there — but a plain background→foreground without recreation keeps `sessionVerified = true`, so it may not
re-prompt. Architecturally sound (no compose-tree bypass; content isn't composed until unlocked), but consider
re-locking on `ON_STOP`/timeout so a picked-up unlocked phone re-prompts. *Prompted by:* R15 Pass M code audit (not
live-tested — emulator has no enrolled biometric).
> 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:106110`), 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.
<!--
Completed (2026-06-27, Future.md backend pass — deployed + verified live):
- subscription_entitlement_changed push — new Cloud Function `onEntitlementChanged` (users/{uid}/entitlements/premium
onWrite, edge-triggered inactive→active) notifies the PARTNER "X upgraded — you both have Premium now"; skips if the
partner already had premium. Client type `SUBSCRIPTION_CHANGED` added (routes to Subscription). Verified: QA premium
ON → Sam queued the push; Sam ON while QA already premium → no redundant notify. Deployed to closer-app-22014.
- users/{uid} update-rule field allowlist — `allow update` now uses `affectedKeys().hasOnly([...12 model/aux fields])`,
blocking junk keys + `hasPremium`. Verified raw-API: allowlisted field PATCH 200, junk field 403, hasPremium 403.
(Keep the list synced with User.kt + FirestoreUserDataSource — guard noted in firestore.rules.) Deployed.
Completed (2026-06-27, Future.md fix pass):
- Daily-reveal isRevealed retry — on reveal load, if local says revealed but the server doc's isRevealed is still
false (a prior best-effort markRevealed failed offline/transiently), re-issue markRevealed idempotently so
onAnswerRevealed fires and the partner gets the "opened your answers" push (AnswerRevealViewModel.load()).
- Brand glyphs — G-set copied to res/drawable/; Date Match + Plan Date cards wired to glyph_date_card_heart
(remaining surfaces tracked above + in ClaudeBrandingReview.md).
- Memory Lane "title runs into preview" — investigated, NOT a bug: the capsule list renders title (own line, ellipsized)
+ open-date; sealed content is never previewed. The R12 "run-on" was adb-typed test data landing in the title field.
Completed (2026-06-25) and removed from the backlog:
- Inclusive sex/gender options in onboarding (Female/Male/Non-binary/Prefer not to say) + honest copy
(Desire Sync is already gender-neutral, so no tailoring fallback was needed).
- Turn-aware Home "waiting to play" copy ("Your turn to play.").
- Partner-action/results pushes exempt from the weekly promotional rate-limit ceiling.
- Suppress the redundant results / "partner finished" push when the recipient is already on that
game's screen (new ActiveGameSessionMonitor mirroring ActiveThreadMonitor).
- Friendlier paywall error state: retry-with-backoff, offline-aware message, Continue hidden until plans load.
- Wire iOS illustrations into Android empty states — already wired (history, invite, daily-question,
together-empty, partner-activation all show their illustration).
- Rotating privacy messages on sign-up + forgot-password (login already had it).
-->