120 lines
11 KiB
Markdown
120 lines
11 KiB
Markdown
# 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`) or *ends/abandons* one (`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 —
|
||
these speculative types aren't implemented.
|
||
- **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: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.
|
||
|
||
<!--
|
||
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).
|
||
-->
|