feat(app): Settings puts the most-opened groups first; plan card A/B (settings-plan-card)
YI
2h ago+791+791+012345678901234567890123456789-66-66-01234567890123456789
Summary
Settings puts what people open most at the top and makes upgrading one tap from the sheet.
- Order: Device, Recording & Transcription, Notifications & Display and Data & Privacy (most Settings visits) sit right under Account and Plan & Usage. Voice, Integrations and Connect an Agent come next; Memories and Goals keep their group; Referral moves next to Help & About and Feedback and drops the NEW tag.
- Device row shows the battery, or Connected / Disconnected, so you can check it without opening the page.
- Account is the name (large) and email. No placeholder picture, since there are no profile photos.
- No handle band at the top of the sheet: the header with the close X is the top, and the sheet still swipes down to close.
app/docs/ux-contract.mdnotes the exception. - Plan & Usage for Free and Plus gets the minutes meter and a full-width Upgrade, behind an A/B test (below). Upgrade opens the paywall in place via
openUpgradePaywall(sourceusage_page), sopaywall-variantstill picks the design. Paid plans keep the plain row.
Nothing moved deeper: every row is still on the sheet or one tap below it (settings_structure_test).
upgrade_paywall.dart and standalone_plans_sheet.dart are byte-identical to the copies in #21711, so whichever lands second merges cleanly.
Screenshots
| Before (main) | After (card arm) |
|---|---|
![]() | ![]() |
Before: widget-test render of main's SettingsDrawer (Free plan). After: iOS simulator, local test account on Free, no device connected.
A/B test: settings-plan-card
- PostHog flag
settings-plan-card(963589) with draft experiment 486057, 50/50 by distinct id. Not launched: launch once this build is in the stores. control: the Plan & Usage row with the minutes meter and a full-width Upgrade under it.card: a plan card with the plan name large, the meter and a full-width Upgrade; tapping the card outside Upgrade opens Plan & Usage.- Exposure:
$feature_flag_called(viarecordFeatureFlagExposure, which also sets$feature/settings-plan-card) when the section is drawn for a Free or Plus reader with a PostHog-assigned arm. No identity, an unread flag or an unknown variant shows control and is not counted. - Primary metric:
Upgrade Succeededwithin 7 days of exposure. Secondary:Upgrade Plan Selected. Guardrail:Product Errorper user. - Sample: about 6,800 readers open Settings a month and about 8% upgrade, so roughly 3,000 exposed per arm per month detects about a 25% relative change. Plan: run 4 weeks. Pre-registration in
product/experiments/settings-plan-card.md; flag added toconfig/feature-flags.yaml. - Debug builds:
--dart-define=OMI_SETTINGS_PLAN_CARD=control|card. - The reorder, Device status, Account header and handle band ship to everyone.
Testing
flutter test test/widgets/settings test/spine/b1_settings_surface_test.dart test/widgets/semantics_baseline_test.dart: 55 pass, including both arms, the fallback-is-not-counted rule, the exposure key, the new order, the Device battery value and the Account header.scripts/check_feature_flag_registry.py: OK.- Checked on the iOS simulator (iPhone 17 Pro, iOS 26.5) on this branch.
Product invariants affected
none
🤖 Generated with Claude Code

