feat(app): redesigned Home behind the home-redesign A/B flag (recording pill, upgrade in place)
Summary
A redesigned mobile Home, behind a PostHog A/B flag so it can be measured against today's Home before it ships to everyone.
- Flag:
home-redesign(test= this redesign,control/ unread = today's Home). Not created in PostHog yet, so until it is, every reader sees today's Home and this PR changes nothing for them. - Pre-registration:
product/experiments/home-redesign.md: question, arms, exposure, primary metric (readers with ≥1 new conversation in 7 days), guardrails, decision rule, kill switch. - Split out of this PR to keep it reviewable: the sign-in / Data & Privacy launch film (#21761) and the paywall re-ask on app open (#21754).
- Builds on #21689 (David's Home declutter): the Activity bell and the "Conversations on the way" row are kept and shared by both arms.
What the redesigned Home changes (test arm)
One place for a live recording: the bottom pill. There is no recording card above the list anymore. The bottom bar is Ask Omi beside one pill, and the pill always says three things:
| What is happening | Listening · Reconnecting… · Paused · Recording (Transcribe Later) · Disconnected (Omi dropped) · the problem in a few words (mic taken by a call, transcription failed, storage full) |
| How it is going (dot) | red, breathing = capturing fine · grey = paused or waiting · orange = needs a look |
| The one next step | Stop while recording · Resume while paused |
Tapping a healthy pill opens the live transcript. Otherwise it opens a sheet with why, the capture's own controls (Pause/Resume, Transcribe Later's New recording and Stop, retry transcription), the live transcript, Stop, and, for an Omi that dropped, "Record with phone instead". The pill and the sheet read one shared summary (conversations/live_capture_summary.dart), so they never disagree.
Also in the test arm
- Text-only empty states on Home and Tasks; Omi pull-to-refresh; Home fades in after onboarding.
- Page actions sit beside the Home/Tasks switcher (calendar on Home); the top bar (device pill · Search · Activity · Settings) is identical on Home and Tasks.
- Locked conversations and memories say Upgrade and open the paywall in place (via the
paywall-variantrouter), instead of View Usage → the Usage page. - Tasks: a ticked (or reopened) task shows its new state, then folds away. Closable "Teach Omi your voice" card. Omi's first chat message in an empty chat. After onboarding: a dot on Tasks, and three starter to-dos when onboarding found none.
Today's Home (control) is unchanged: the Ask Omi chat bar with the round record button, the recording card above the list, empty states with icons, the calendar in the header, locked rows → Usage. Its widgets are kept in app/lib/pages/home/legacy/ until the experiment is decided; the losing path gets deleted then.
Shared by both arms (not part of the comparison): the Activity bell and pending row (#21689), the Tasks menu in the header (select, group, completed, export), the Settings plan card with Upgrade, and the end-of-onboarding control exposure for paywall-variant.
How the flag behaves
- Client-evaluated, read once the analytics identity has settled (a listener, no polling), and also during onboarding, so a new reader's Home is decided before they first see it.
- The arm is saved per account: a reader never switches Home mid-session; a new assignment applies from the next launch if Home was already on screen. Fail-closed: no identity, an unread flag or an unknown variant = today's Home.
- Exposure:
$feature_flag_calledforhome-redesign, once per account and arm, when Home is first shown after PostHog assigned an arm.$feature/home-redesignthen tags later events. - Kill switch: set the flag to
control100% (or turn it off); every build shows today's Home without a release. - Try it locally (debug builds):
--dart-define=OMI_HOME_REDESIGN=onor=off.
Review notes
- Processing on Home follows main (#21850): both arms show one "on the way" row for syncing and processing work, with no per-conversation processing cards. The redesign's own processing row and the stage split were dropped when merging main (2026-10-16).
- Fixes found in review, all covered by tests: Data & Privacy keeps Agree & Continue + Use a Different Account; an Omi that dropped no longer blocks recording with the phone; the live transcript can't open twice while the mic starts; the sheet's Stop never starts a new recording; starter to-dos refresh the Tasks list; the deferred firmware prompt checks the device is still connected.
app/lib/pages/home/legacy/legacy_home.dartis a copy of main's widgets, not new design: please skim, don't review line by line.
Screenshots
test arm on the iOS simulator against the local dev server (001–004 today; 005–007 from the earlier #21445 branch).
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() |
Testing
- Full app suite on this branch: 5,904 passed, 20 skipped, 0 failed.
make preflight: 30/30 checks pass.- New tests:
test/unit/home_redesign_test.dart(unread = control, per-account memory, never switches a Home on screen, exposure only after assignment, no read before an identity); off-path tests for locked rows (View Usage → Usage page); pill tests for every recording state (no card on Home, Paused + Resume, failure opens the sheet, Omi paused / dropped / Transcribe Later, Record with phone). - Manual: simulator walk-through of the pill (record → Listening → Paused → sheet → Resume → Stop) and Home with no top card.
- Not yet done: a run on a physical device from this branch.
Line-count exceptions
Line-Count-Exception: app/lib/backend/preferences.dart | 1538 -> 1543 | one setting: the closed "Teach Omi your voice" card
Line-Count-Exception: app/lib/pages/settings/transcription_settings_page.dart | 1572 -> 1575 | upgrade opens the paywall in place instead of the Usage page
Line-Count-Exception: app/lib/providers/device_provider.dart | 1598 -> 1614 | a firmware prompt found during onboarding waits until Home is up
Product invariants affected
- INV-MEM-1 — no change to how memories are stored or reviewed; in the
testarm a locked memory row opens the paywall in place instead of the Usage page.
Failure-Class: none
🤖 Generated with Claude Code






