Skip to content

feat(app): Settings puts the most-opened groups first; plan card A/B (settings-plan-card) - #21763

Open
YinkaAdepoju wants to merge 3 commits into
BasedHardware:mainfrom
YinkaAdepoju:yinka/settings-plan-card
Open

YinkaAdepoju wants to merge 3 commits into
BasedHardware:mainfrom
YinkaAdepoju:yinka/settings-plan-card

Conversation

@YinkaAdepoju

@YinkaAdepoju YinkaAdepoju commented Oct 15, 2026 •

Copy link
Copy Markdown

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.md notes 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 (source usage_page), so paywall-variant still 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 After

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 (via recordFeatureFlagExposure, 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 Succeeded within 7 days of exposure. Secondary: Upgrade Plan Selected. Guardrail: Product Error per 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 to config/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

View guided diff

…(settings-plan-card)

Settings sheet:
- Device, Recording & Transcription, Notifications & Display and Data &
  Privacy (most Settings visits) sit right under Account and Plan; Voice,
  Integrations and Connect an Agent follow; Referral moves next to Help and
  loses its NEW tag.
- The Device row shows the battery, or Connected / Disconnected.
- Account is the name (large) and email, without a placeholder picture.
- No handle band: the header with the close X is the top of the sheet,
  which still swipes down (noted in the UX contract).

Plan & Usage for Free and Plus, A/B tested with PostHog flag
settings-plan-card (experiment 486057, draft): control is the row with the
minutes meter and a full-width Upgrade; card is the plan name large, the
meter and a full-width Upgrade. Upgrade opens the paywall in place
(openUpgradePaywall, source usage_page; same files as BasedHardware#21711). No identity,
an unread flag or an unknown variant is control and not counted.
Pre-registration in product/experiments/settings-plan-card.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Git-on-my-level

Copy link
Copy Markdown
Collaborator

Thanks @YinkaAdepoju — this is a genuinely well-put-together change. I reviewed the full diff statically (no build CI ran on this PR beyond the AI reviewer, so nothing was executed) and verified the API surface it relies on against current main. File-by-file notes:

  • app/lib/pages/settings/settings_drawer.dart — reorder is coherent and the test asserts the full new key order; _deviceValue() correctly guards batteryLevel >= 0 (DeviceProvider defaults to -1, so a non-reporting device falls back to Connected). _planValue() now surfaces Free/Plus display names only when showSubscriptionUI, matching the section widget's gate.
  • app/lib/pages/settings/widgets/settings_plan_section.dart — clean arm split (_ControlSection keeps the row + meter; _PlanCard is the new layout). _expose() records via addPostFrameCallback with a once-guard, and widget.arm != null (tests) skips exposure — correct given the pre-registration only counts PostHog-assigned readers. Minor nit: openSettingsUpgrade(context, plan) accepts plan and ignores it.
  • app/lib/services/experiments/settings_plan_card_experiment.dart — fail-closed everywhere: no identity / timeout (2s) / unknown variant → control, and _cachedFromFlag ensures a fallback arm is never recorded as an exposure. Identity-keyed cache invalidates on distinct-id change. Matches the pre-registration exactly.
  • app/lib/pages/settings/widgets/upgrade_paywall.dart / standalone_plans_sheet.dart — delegation to PaywallRouter.openVariant verified against main's signature; trackOpen once-semantics and the context.mounted guard after awaits are right. Note these two files are intentionally byte-identical to open PR feat(app): redesigned Home behind the home-redesign A/B flag (recording pill, upgrade in place) #21711 — whichever merges second will need a trivial rebase; worth tracking so it doesn't rot.
  • app/docs/ux-contract.md — the no-handle-band exception is documented next to the sheet rules, which is the right place.
  • product/experiments/settings-plan-card.md, config/feature-flags.yaml, backend/docs/feature-flag-registry.md — pre-registration (question, arms, unit, primary metric, guardrails, sample-size reasoning, decision rule, kill switch) plus registry/flag rows are all consistent with each other and with the code.
  • Tests (settings_plan_section_test.dart, settings_structure_test.dart, settings_search_header_stability_test.dart, settings_sheet_appearance_test.dart) — good coverage: both arms, tap-outside-Upgrade opens Plan & Usage, per-plan matrix, hidden subscription UI, no-subscription, unknown/failed variant not counted, battery on the Device row, and the full sheet order.
  • .agent-artifacts/ui-evidence/ — follows the existing repo convention; before (widget-test render) vs after (simulator) honestly labeled.

Nothing blocking found technically. Because this reorders the Settings information architecture and adds a new monetization surface (the plan card A/B), it needs a product-direction decision from a maintainer before merge — leaving for human maintainer review with a positive signal.


This review is AI-generated feedback on behalf of the maintainers.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@Git-on-my-level Git-on-my-level added needs-maintainer-review Needs a human maintainer to sign off before merge positive-signal Automation verified a genuine fix/quality contribution flutter flutter work labels Oct 15, 2026
YinkaAdepoju and others added 2 commits October 16, 2026 18:08
# Conflicts:
#	config/feature-flags.yaml
Review nit on BasedHardware#21763: the Settings Upgrade button passed the plan but the
helper never used it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

flutter flutter work needs-maintainer-review Needs a human maintainer to sign off before merge positive-signal Automation verified a genuine fix/quality contribution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants