- PaywallPricing turns a RevenueCat Package into a headline price + an offer line
(free trial, intro price, or one-time), so a dashboard-configured trial or a
lifetime SKU renders without an app release; unknown shapes degrade to the
plain price. Pure formatting is unit-tested (PaywallPricingTest); the SDK
extraction is type-checked against purchases 8.20.
- Gift framing on the plan chooser ("One subscription covers you both — treat
your partner"): couple-shared premium already unlocks both partners, so this
is copy only, no billing change.
Live paywall (real offerings) still needs a real RC key + sandbox per
docs/SUBSCRIPTION_GO_LIVE.md (owner).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Foreground notification taps carried only app_route, so their
push_notification_opened event logged the generic "app_route" category. Also
put the notification type on the intent so the event records the specific kind
(partner_answered, reveal_ready, ...). Backgrounded taps already carry type via
the FCM data payload. Completes push-opened coverage across both tap paths.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The live couple streak used a raw 48h rule that reset on any 48h gap AND
inflated the count by incrementing on every answer (several a day counted
several times). Replace with StreakCalculator.nextCount: once per calendar day,
one forgiving grace day (a single missed day still continues), reset only after
2+ missed days; skip the redundant write on a same-day answer.
Celebration milestones now include the habit-building early tiers
(1/3/7/14/30/100/365), single-sourced from StreakCalculator so the count and the
celebration can't drift; the milestone dialog handles day 1 grammatically.
Tested (StreakCountTest): same-day idempotency, consecutive, grace, reset, and
partner timezone-skew (never breaks a same-boundary streak). Existing 24
StreakCalculator tests still pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Content-free daily-question state + streak, mirrored from HomeViewModel via a
dedicated DataStore (TodayWidgetUpdater -> TodayWidgetState). App-lock drops it
to a generic card so partner-activity state doesn't sit on the protected home
screen; signed-out/unpaired show neutral prompts. Tapping opens the daily
question via MainActivity's app_route extra.
Verified: provider registers, state bridge writes, headline logic unit-tested
(TodayWidgetTest), no crash on launch. On-home-screen render + a Remote Config
kill switch are tracked in Future.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>