feat(app): occasional plans reminder on app open (flag paywall-reask, off by default)
YI
1d ago+291+291+012345678901234567890123456789-4-4-0123456789
What changed and why
Free users only see the plans today when they hit a limit or open Settings. This adds an experiment that, on app open, occasionally shows the existing plans screen to free users. It is off by default.
- Infrequent by design: at most once a day, with growing gaps between showings, and never on the day someone signs up. Any other plans screen they see resets the timer.
- Never interrupts: it waits in
PromptQueuebehind recordings, calls and other prompts, and is skipped for paid users or if the user upgraded in the meantime. - Behind the feature flag
paywall-reask, with acontrolgroup that sees no change. The flag is off, so nothing changes for anyone until it is turned on. If the flag can't be read or anything fails, nothing is shown. - Reuses the existing paywall (
PaywallRouter.openVariant, falling back to the plans sheet). No new screen or copy. New analytics source:app_open_reask.
Details: app/lib/services/experiments/paywall_reask.md. Flag registered in config/feature-flags.yaml and backend/docs/feature-flag-registry.md.
Split out of #21711 to keep that PR focused.
Try it (debug builds)
flutter run --flavor dev --dart-define=OMI_PAYWALL_REASK=test --dart-define=OMI_PAYWALL_REASK_NOW=true
OMI_PAYWALL_REASK=test|control picks the group; OMI_PAYWALL_REASK_NOW=true makes it show on every launch / resume. Both overrides only work in debug builds.
Product invariants affected
none
How it was verified
flutter analyzeon all touched Dart files: no issues.scripts/check_event_registry.py(regeneratedevents.g.dart) andscripts/check_feature_flag_registry.py: OK.make preflight: passes.- Not yet tried on a device or simulator with this branch.
Tests
app/test/unit/paywall_reask_test.dart(new): timing, the once-a-day limit, never on the first visit, other paywalls reset the timer, the control group sees nothing, an unread flag or a signed-out user does nothing.- Existing
paywall_router_test.dart,paywall_experiment_test.dart,outline_paywall_page_test.dartstill pass.
UI evidence
UI-Evidence: none -- no new screen, it reuses the existing paywall
Failure class (fixes)
Failure-Class: none
🤖 Generated with Claude Code