Skip to content

feat(app): UI redesign — Convos-first nav, Chat tab, Brain tab, consolidated Todos - #11265

Draft
jacobtrebil wants to merge 19 commits into
BasedHardware:mainfrom
jacobtrebil:feat/app-ui-redesign
Draft

feat(app): UI redesign — Convos-first nav, Chat tab, Brain tab, consolidated Todos#11265
jacobtrebil wants to merge 19 commits into
BasedHardware:mainfrom
jacobtrebil:feat/app-ui-redesign

Conversation

@jacobtrebil

@jacobtrebil jacobtrebil commented Aug 8, 2026

Copy link
Copy Markdown

What changed and why

A UI redesign pass over the Flutter app. The central change is that chat
becomes a first-class destination
rather than a route pushed from Home's "Ask
Omi" bar — that push was residue from when Chat was its own nav tab (the tap
still fired bottomNavigationTabClicked('Chat')).

  • Nav is now Convos / Chat / Brain / Todos / Apps. Conversations lead; the
    old Home tab is renamed Chat and holds the conversation with Omi.
  • Brain tab — memories and the knowledge graph move out of the settings
    drawer into a nav tab, and the Mind Map card comes off Home where it rendered
    unlabeled dots across a third of the screen.
  • Todos collapse from five headings (Today, Tomorrow, Later, No deadline,
    Overdue) to one list plus Completed. Due dates already appear on the rows.
  • Typography moves to Space Grotesk with a shared ramp in
    lib/utils/app_typography.dart.
  • Neutral accents — the completed-task check and several controls drop their
    colour, continuing the INV-UI-1 direction.
  • Daily recaps carousel, starter prompts under the composer, task-completion
    feedback, and bottom-bar spacing work.

Product invariants affected

  • INV-MEM-1 — memories move to a dedicated Brain tab. Storage, promotion and
    tiering are untouched; this is presentation only.

INV-UI-1 is not cited: per its own PR rule, routine UI work relies on the brand
ratchet. This branch reduces purple (Record button, task FAB, completed check).

How it was verified

Exercised on the iOS simulator (iPhone 17 Pro, iOS 26.5) — every tab, the chat
empty state and composer focus behaviour, task completion, and the recaps
carousel. Spacing changes were measured against reference screenshots rather
than eyeballed.

Three limits worth stating plainly:

  1. Not verified against real data. A local build cannot authenticate:
    app/setup.sh copies based-hardware-dev Firebase config while setting
    API_BASE_URL=https://api.omiapi.com/, so signInWithCustomToken fails with
    custom-token-mismatch. Every screen here was exercised against fabricated
    local data. That looks like a bug in the documented setup path rather than
    anything specific to this branch, and deserves its own issue.
  2. Not verified on a physical device. Device signing is blocked on an Apple
    account permission, not on anything in the project. BLE, the widget and the
    watch app are therefore unexercised — none are touched here, but the
    app-group entitlement path is unproven.
  3. Tab indices are not compiler-checked. Tab position lives as bare integer
    literals across several files (page list, scroll-to-top, merge action bar,
    sync/calendar buttons, app-bar chrome gate, BatteryInfoWidget). The reorder
    updated all known sites and each was checked on screen, but a future reorder
    has no guard. Converting these to a named enum is the obvious follow-up.

Tests

flutter test test/widgets/148 pass, 0 fail, 27s.

Run in a fresh checkout of this branch with no local index state, after
flutter pub get, dart run build_runner build and the setup.sh-generated
Firebase config — i.e. what a maintainer gets, not my working tree. Same
checkout: flutter analyze lib/ test/ reports 0 errors.

New coverage lands with the behaviour it asserts: action_item_completion_test,
action_item_completed_view_test, action_items_page_scroll_test, and
home_hero_entrance_test.

Two problems the first push had, both fixed here and both invisible from my
working tree, since the files carrying them were marked skip-worktree:

  • users.dart imported package:omi/dev_demo_data.dart, a local-only file
    excluded from git, so no clean clone could build. Reported in review.
  • Three action-item tests called ActionItemsProvider.seedDemoData, which also
    existed only locally, so the suite did not compile either. They now use the
    constructor injection this provider already had, plus a fake update request in
    test/widgets/support/.

An earlier home_hero_pixel_test has been removed. It awaited toByteData()
outside tester.runAsync, which never completes under fake async — the runner
wedged and only failed at the 10-minute default timeout. It was a debugging
probe, not a guard.

Failure class (fixes)

Failure-Class: none

The one fix: commit (995cd71) corrects a build-configuration typo: the dev
BatteryWidget derived $(APP_BUNDLE_IDENTIFIER).development.widget while
APP_BUNDLE_IDENTIFIER already ended in .development, producing
…ios12.development.development.widget. That identifier does not exist on the
Apple team, so any dev-flavour device build failed at signing. The prod widget
and the watch app already used the correct single-suffix pattern; this brings the
three dev configs in line. No existing class covers a per-flavour identifier
derivation typo, and one instance does not warrant a new one.

Scoped cleanups

  • Removed ChatPage's second BottomNavBar, which rendered inside the page and
    appeared/disappeared on focus — the cause of the composer shifting ~100pt.
  • The reply-reserve spacer now sizes off the message list's viewport rather than
    the whole screen; as a tab body the list is far shorter, so the reserve pushed
    the newest message off the top.
  • .gitignore exception for app/assets/sounds/*.wav. The repo-wide *.wav
    rule hid the completion chime while pubspec.yaml declared it, so every other
    clone would have built with the asset missing.

Known gaps

Embedding ChatPage removed its app bar, which was the only caller of
openEndDrawer()Clear chat and the chat-app selector are unreachable from
the Chat tab.
They need a new entry point (the composer's + menu is the
natural place) before this is user-ready.

Replaces the SF Pro Display family with Space Grotesk (SIL OFL 1.1) and
sets it as ThemeData.fontFamily, which is what reaches the inline
TextStyles throughout the app — most of them name a size and weight but
no family, so they inherited SF Pro implicitly.

Adds lib/utils/app_typography.dart as the single place the type ramp is
defined, so screens stop hardcoding sizes independently.

Verification: flutter analyze on the changed files reports 4 info-level
lints, all pre-existing (verified none appear in the added lines).
Rendered on the iOS simulator (iPhone 17 Pro, iOS 26.5).
… card

Memories were reachable only through the settings drawer, while a
preview of the same graph occupied roughly a third of the Home screen —
rendering five unlabeled dots that could not be read or acted on. That
inverted the importance of the two surfaces.

- Adds a Brain tab to the bottom nav (memories + the knowledge graph).
  FontAwesome brain icon; the graph page's own analytics event was
  already named brainMapShareClicked, so the name matches what the code
  already called it.
- Removes the Mind Map card from Home. The graph keeps its existing
  full-screen entry point from within memories, where it has room for
  labels.
- Localizes the nav labels (navHome/navConvos/navTodos/navBrain) across
  all 49 supported locales.

Analytics event names stay English and unchanged ("Conversations",
"Tasks") even though the visible labels differ, so the existing funnel
does not break.

Verification: flutter analyze on the changed files reports only
pre-existing info-level lints. Navigated all five tabs on the iOS
simulator (iPhone 17 Pro, iOS 26.5).
The Brain tab rendered the knowledge graph full-bleed, so nodes ran off
every edge with no boundary showing where the graph ended — it read as
the page having escaped rather than as something to look at.

BrainPage now frames it: a grey (#1A1A1A) card on the page's deep black
(#0F0F0F), 28pt corners, with a "Mind Map" header and an Expand button
to the full-screen graph. The page colour matches what the nav bar's
gradient already resolves to, so page and bar read as one surface with
the card floating on it.

Two details that are load-bearing:
- The bottom nav bar is Align(bottomCenter) inside a Stack and takes no
  layout space, so the card reserves 116pt of bottom clearance by hand.
  Without it the lower corners sit behind the bar and never show.
- MemoryGraphPage's embedded mode hardcoded ColoredBox(Colors.black),
  which painted over any parent surface. Embedded mode now draws no
  background and lets the embedding parent own the colour. BrainPage is
  its only embedded consumer.

Verification: flutter analyze clean on both files (2 pre-existing
deprecation infos in the share path). Built and run on the iOS simulator
(iPhone 17 Pro, iOS 26.5).
Home led with a stack of previews and buried the query box at the bottom,
below a decorative graph. The query box is the product — Omi's value is
that it already knows your context — so it becomes the Home surface:
HomeHero renders the headline and chat bar, with its entrance animation
playing once per launch rather than on every tab return.

The app bar also reserved kToolbarHeight on tabs that render no chrome
(Conversations, Brain, Apps), leaving an empty band above each page's own
headline. It now collapses to zero when empty. The collapse is
conditional rather than blanket because the Conversations bar still holds
two buttons — sync (device paired / files pending) and calendar (date
filter active); blanket-collapsing would have hidden the sync button for
anyone with a device.

Also reworks the Apps explore/install page and the conversations folder
tabs to match, moving the Apps create menu into an overflow beside its
search field.

Verification: flutter analyze clean across all changed files (4
pre-existing info lints). Exercised every tab on the iOS simulator
(iPhone 17 Pro, iOS 26.5); confirmed by screenshot that the gap above the
Conversations headline is gone.
The card version stacked three near-blacks in one column — page #0F0F0F,
card #1A1A1A, button #252525 — which reads as banding rather than depth.
The graph now draws straight onto the page background and gets the whole
width, with a ClipRect so node glows can't paint up into the header, and
100pt of bottom clearance so nodes don't settle under the floating nav
bar where they can't be read or tapped.

Verification: flutter analyze clean (2 pre-existing deprecation infos in
the share path). Rendered on the iOS simulator (iPhone 17 Pro, iOS 26.5).
Tapping "Ask Omi" pushed ChatPage as a full route. That was residue, not
design: the bar was a Container styled to look like a field, and its tap
fired bottomNavigationTabClicked('Chat') — Chat used to be a nav tab, and
when it was removed the bar became a shortcut to the orphaned page.

Home now renders ChatPage in place, so asking Omi something never leaves
the tab. The header (Connect / Record / settings) stays. ChatPage gains:

- `embedded`: no app bar of its own, and it keeps its Scaffold so the
  chat-apps end drawer still works.
- `emptyState`: Home passes its hero, so an empty chat *is* the Home
  screen rather than a bare "no messages yet".
- HomeProvider's chat focus node instead of its own, and dispose() leaves
  it alone since the provider owns it.
- No auto-focus when embedded — as a tab body that fired at launch and
  popped the keyboard over a screen nobody asked to type on.

Three layout fixes fell out of embedding it:

- The composer reserves the nav bar's 100pt unconditionally. Making it
  depend on focus put the composer back under the bar the moment it was
  tapped, so only ever one of the two was visible.
- The nav bar no longer hides on chat focus. That branch had no live
  callers before this change (nothing used chatFieldFocusNode), and Home
  being the chat makes focusing the composer the most ordinary action on
  the tab — dropping the nav made it feel like the app had navigated away.
- The dynamic spacer that reserves room for a streaming reply now sizes
  off the list's own viewport via LayoutBuilder, not the whole screen.
  Screen-height was fine for a full-page route; as a tab body the list is
  ~276pt shorter, so the reserve overshot and scrolled the just-sent
  message off the top. Pre-existing latent bug, exposed by embedding.

Also swaps the nav order to Home / Convos / Brain / Todos / Apps. That
touched every index-keyed branch: the page list, tap-again-to-scroll, the
brainMapOpened event, the task selection bar, the Tasks app-bar buttons,
_hidesAppBarChrome, the Today card's "View All", and the iOS quick action
that jumps to Tasks — the last two would otherwise have silently landed
the user on the graph.

Verification: flutter analyze clean across all changed files (2
pre-existing print lints). Exercised on the iOS simulator (iPhone 17 Pro,
iOS 26.5): every tab, the chat empty state, and composer + nav bar
visible together in both focus states.
Checking off a task now fills the circle green, strikes the text through,
and plays a short confirmation sound before the change is reported.

The asset needed a .gitignore exception. `*.wav` is ignored repo-wide, so
assets/sounds/task_complete.wav was invisible to git while pubspec.yaml
declared assets/sounds/ and ui_sounds.dart played it — every other clone
would have built with the asset missing. Added a negation next to the
existing one for the backend transcription probe, so future UI sounds do
not hit the same trap.

Verification: flutter test test/widgets/action_item_completion_test.dart
— 3/3 pass, covering the open state, the completion transition, and that
un-checking reports immediately with no animation. flutter analyze clean.
Tapping "Ask anything" shifted the whole screen — the composer dropped
roughly 100pt onto the bottom nav and the hero slid down with it. Two
mechanisms were fighting: ChatPage's Scaffold resizes itself for the
keyboard inset, while the composer separately reserved a flat 100pt for
the floating nav bar, so the reservation was counted twice the moment the
inset appeared.

Netting the two against each other was still fragile, so the embedded
case now opts out of inset resizing entirely and keeps a fixed 88pt
clearance — the bar's opaque 80pt strip plus a small gap, which is where
the composer landed when focused and is the position that reads right.
The layout is then identical focused or not.

Auto-focus is restored for the embedded case too, so Home opens ready to
type. It was disabled earlier only because focus used to hide the nav bar
and pop the keyboard over a screen nobody had asked to type on; neither
is true now, and with the layout pinned there is nothing left to jump.

Known limitation: with inset resizing off, a real software keyboard will
cover the composer on a physical device. The simulator's hardware
keyboard hides this. The fix there is to lift the chat and the nav bar
above the keyboard together rather than re-enabling resize — deferred
until it can be built against a real keyboard.

Verification: flutter analyze clean on both files (2 pre-existing print
lints). Built and run on the iOS simulator (iPhone 17 Pro, iOS 26.5);
composer and nav bar hold position, confirmed by screenshot.
Adds a daily recaps carousel to Conversations with its own full page,
alongside refinements to the action items list (completed view, tile
treatment, scrolling) and the shared UI guidelines they draw on.

Verification: flutter analyze clean across the changed files (8
pre-existing info lints, none new). flutter test on the three action-item
widget suites — 5/5 pass, covering the open state, the completion
transition, un-checking, the completed view, and list scrolling on
overflow.
Five headings for eight tasks read as filing rather than as a list of
what to do. Overdue, Tomorrow and No deadline are gone:

- Overdue folds into Today. A past-due task is the most actionable thing
  on the page, and its own heading sat below everything else — the most
  urgent work was the last thing you saw.
- Tomorrow and No deadline fold into Later, except undated items older
  than a week, which go to Today so they don't drift indefinitely.

Removes what became dead weight with them: the whole overdue section
builder, the collapsible no-deadline header and its expansion state, and
a duplicate clear-completed control that only existed on that path.

Verification: flutter analyze clean on the file. flutter test
test/widgets/ — 147 pass; the single failure is the local-only demo auth
override short-circuiting the reauth shell, verified passing with that
flag off and unrelated to this change.
Tapping the composer moved the whole screen. The cause was a second
BottomNavBar rendered inside ChatPage itself, conditioned on !hasFocus —
a leftover from when it was a pushed route that needed its own. Embedded,
Home already supplies one, so the duplicate appeared and disappeared on
focus and took ~100pt of layout with it. Three earlier attempts at
clearance values and keyboard-inset arithmetic were treating the symptom;
none could have worked while a widget was entering and leaving the tree.

Before the first message the composer now renders inside the empty state,
under the headline it answers, rather than stranded at the foot of a
blank screen; it drops to the bottom once there is a conversation. That
needed the composer extracted from the body Column into _buildComposer so
it could render in either place — no behaviour change, just reachable
from two call sites.

Centring took three passes worth recording, because the arithmetic and
the perception disagree. The bottom nav floats over the body rather than
occupying layout, so centring in the full body sits half a bar-height
low; that part is a genuine correction. But an invisible counterweight
that centred the headline/composer pair exactly left every visible
element below the midpoint under a large void, which reads as low however
correct it is. Centring the whole group — headline, composer, prompts —
is what actually looks centred.

Also restyles the starter prompts: filled #131317 rather than a 12%
hairline, with quieter text. A hairline is harder to pick out than a soft
surface and gave them the look of controls; the fill sits between the
page and the composer so they read as subordinate to the input above
them. Drops the "what did I say I would follow up on?" suggestion.

Known gap: embedding removed ChatPage's app bar, which was the only
caller of openEndDrawer() — Clear chat and the chat-app selector are
currently unreachable from Home. Tracked for a follow-up entry point.

Verification: flutter analyze clean across lib/ (0 errors, 0 warnings).
Exercised on the iOS simulator (iPhone 17 Pro): composer holds position
through focus changes, the empty state centres, tapping a prompt sends it
and the composer drops to the bottom.
Continues the redesign pass across the shared surfaces: the Home hero's
entrance animation (with a test covering that it plays once per launch),
the daily recaps carousel and its detail page, goals, folder tabs,
conversation list items and bottom bar, the battery/status chip, and the
shared UI guidelines they draw on.

Verification: flutter analyze clean across lib/ (0 errors, 0 warnings;
148 pre-existing info lints). flutter test test/widgets/ — 147 pass. The
one failure is the local-only demo auth override short-circuiting the
reauth shell, verified passing with that flag off.
- Tasks collapse to a single Todos section with Completed below it. Due
  dates already show on the rows, so the headings were splitting a short
  list without adding anything. TaskCategory stays as a single-value enum
  because ordering, drag-and-drop and the drop zones are keyed by it.
- Starter prompts lose their pills entirely and render as plain text. The
  rounded capsule is the composer's shape and should stay unique to it;
  repeating it made the suggestions read as three peers of the input.
  Padding remains as the tap target.
- The completed check goes white (AppStyles.completedAccent), removing
  the last chromatic accent on the page. Changed at the token so both the
  list checkbox and the animated tile checkbox move together.

Also carries the other agent's in-flight typography, recaps carousel,
conversation list and hero work from the same tree.

Verification: flutter analyze lib/ — 0 errors, 0 warnings (148
pre-existing info lints). flutter test on the three action-item suites —
6/6 pass. Exercised on the iOS simulator (iPhone 17 Pro).
…eaders

The + sat beside the input as its own bordered circle, with a 56pt
spacer reserving space for it. It now sits on the pill's left edge,
mirroring the mic on the right, and the composer runs full width.

Its own fill, border and shadows are gone: a bordered circle inside a
bordered pill reads as a control stacked on another control, and the
shadows only existed to lift it off the page background. The recording
stop button shares that slot and moves with it. The pill's left padding
grows to 52 while a button is showing so placeholder text can't run
underneath.

Also matches the Daily Recaps header's bottom padding to the
Conversations header (10 -> 16), so both section titles sit the same
distance above their content.

Carries the other agent's in-flight conversation list, grouping and hero
work from the same tree.

Verification: flutter analyze lib/ — 0 errors, 0 warnings. Built and run
on the iOS simulator (iPhone 17 Pro); + renders inside the pill and the
menu still opens.
The dev-flavour BatteryWidget set PRODUCT_BUNDLE_IDENTIFIER to
"$(APP_BUNDLE_IDENTIFIER).development.widget", but for dev builds
APP_BUNDLE_IDENTIFIER already ends in ".development" — producing
com.friend-app-with-wearable.ios12.development.development.widget.

No such identifier exists on the Apple team, so signing a dev build to a
device fails with "Failed Registering Bundle Identifier ... cannot be
registered to your development team". The prod widget config and the
watch app both use the correct single-suffix pattern; this brings the
three dev configs (Debug/Profile/Release) in line.

Verification: reproduced the failure building the dev flavour to a
physical iPhone, applied the fix, and the identifier error is gone —
device signing then fails only on provisioning-profile membership, which
is an Apple account permission issue rather than a project one.
…lish

Reorders the tabs to Convos / Chat / Brain / Todos / Apps and renames the
old Home tab to Chat, using the existing `chat` l10n string so all 49
locales come along. The icon moves from a house to comment-dots, since a
house no longer describes it. The analytics event stays 'Home' — the
destination is unchanged, only its name and position — matching how
Convos and Todos kept their original event names.

Tab position is duplicated as bare integers across several files, so the
reorder had to move with it: the page list, tap-again-to-scroll, the
merge action bar, the sync and calendar buttons, and the app-bar chrome
gate. Worth noting for whoever reorders next — nothing here is
compiler-checked, and a missed site fails silently in one screen.

Bottom-bar changes:

- Drops the 20pt scrim above the nav bar. It fades transparent to the
  page colour, which reads as haze over dark content rather than as a
  soft edge; the hairline alone is a cleaner boundary. Clearances are
  unaffected — they were always measured against the opaque 80pt strip.
- Lifts the chat composer to 112pt and lightens its fill to #26262E.
  Sitting tight above the bar in near-identical greys, the two read as a
  double-decker toolbar; the composer should be plainly the primary
  control with navigation beneath it.
- Trims the Conversations header's bottom padding to 4. Each conversation
  card carries its own 12pt top padding, so 16 there put the first card
  28 below the heading while the recaps cards sat at 16.

Verification: flutter analyze lib/ — 0 errors, 0 warnings (148
pre-existing info lints). Exercised every tab on the iOS simulator
(iPhone 17 Pro); measured the header spacing against the recaps carousel
by screenshot rather than by eye.
@Git-on-my-level Git-on-my-level added needs-tests PR introduces logic that should be covered by tests needs-maintainer-review Needs a human maintainer to sign off before merge flutter flutter work labels Aug 9, 2026

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pushing this redesign work — the direction is cohesive around the Flutter app shell/Home/chat/Todos experience, and I like that you included widget coverage for the new home hero and task-completion behavior.

I need to request changes before this can be reviewable/mergeable because the current head appears to leave local demo scaffolding wired into production code:

  • app/lib/backend/http/api/users.dart imports package:omi/dev_demo_data.dart and calls kDemoDataEnabled / demoDailySummaries() inside getDailySummaries(), but app/lib/dev_demo_data.dart is not present in the PR or repository at this head. A fresh checkout therefore cannot resolve this import and the Flutter app will fail analysis/build before runtime.
  • app/lib/providers/goals_provider.dart and app/lib/providers/message_provider.dart add seedDemoData(...) APIs and comments that also point at lib/dev_demo_data.dart. If these hooks are only for local prototype data, they should be removed before merging; if they are intended as a real fixture/demo mode, please add the missing implementation behind an explicit non-production/test boundary.
  • app/lib/pages/conversations/widgets/daily_recaps_carousel.dart now calls getDailySummaries() directly, so the unresolved demo-data branch in users.dart affects the new carousel path too, not just an unused helper.
  • app/pubspec.yaml and .gitignore correctly add assets/sounds/ / allow committed WAV assets for app/lib/utils/audio/ui_sounds.dart, but I could not complete a local Flutter analyzer run in this environment because Flutter/Dart are not installed here; the unresolved import above is from static inspection of the checked-out PR head.
  • app/lib/pages/home/page.dart, app/lib/pages/chat/page.dart, app/lib/widgets/bottom_nav_bar.dart, and app/lib/pages/action_items/action_items_page.dart make broad navigation/product-surface changes. Once the build issue is fixed, this still needs maintainer product/UX sign-off because Home becoming the embedded chat, adding Brain, and moving goals/Todos are substantial app-direction choices.

Suggested next step: remove the dev_demo_data import/branch and any local-only seed hooks that are not meant to ship, or include a properly scoped test/demo fixture that is available in clean clones and cannot affect production users. After that, please rerun Flutter analysis/tests for the app/widget suite so maintainers can review the UX changes on a buildable head.

Leaving this for human maintainer review as a product-direction decision for the app navigation/Home redesign after the build blocker is fixed.

— Reviewed by gpt-5.5 for the Omi maintainer automation.


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

jacobtrebil and others added 3 commits August 8, 2026 20:54
A clean clone of this branch could not build. `users.dart` imported
`package:omi/dev_demo_data.dart` — a local-only file deliberately kept
out of git — and branched on it inside `getDailySummaries()`. Three
committed widget tests also called `ActionItemsProvider.seedDemoData`,
which existed only in my working tree, so the test suite did not compile
either. Both were reported by the PR reviewer and by a clean-tree
analyze; the test breakage was not visible locally because the file
carrying it is marked skip-worktree.

Root cause: demo scaffolding was protected by a two-part scheme —
`.git/info/exclude` for the data file, `skip-worktree` for its callers.
`mobile_app.dart`, `conversation_provider.dart` and `memories_provider.dart`
were flagged; `users.dart`, `goals_provider.dart` and `message_provider.dart`
never were, so their hooks committed silently. An unflagged file gives no
signal that it is carrying something that must not ship.

- `users.dart`: import and demo branch removed. Production request code
  should not have carried a fixture seam at all.
- `ActionItemsProvider`: `_demoMode` is gone. It suppressed the server
  write and kept the optimistic update — reachable in a production build
  and silently dropping task updates if ever set. Replaced with
  constructor injection of the update request, matching the injection
  this class already used for `getActionItems`/`deleteActionItem`, plus a
  `@visibleForTesting seedItems()` that only fills the list. The default
  remains the real API, so production behaviour is unchanged.
- `goals_provider` / `message_provider`: seed hooks removed. Nothing in a
  clean clone referenced them.

The three tests now inject `fakeUpdate` (`test/widgets/support/`), which
confirms the write without a network call. That is what keeps the
completion from rolling back — previously supplied by the demo flag.

Verification: `flutter analyze lib/ test/` — 0 errors in committed code.
The 3 remaining errors are in `lib/mobile/mobile_app.dart`, which is
skip-worktree'd local-only scaffolding and is not part of this branch.
Full widget-suite results follow in a separate message; this commit is
staged from an analyze-clean tree.
A clean clone of this branch crashed on launch. `ios/Flutter/Custom.xcconfig`
is generated per machine by `setup.sh` — the bundle identifier is derived from
the developer's hostname (`com.friend-app-with-wearable.ios12-${hostname}`) so
each machine installs a distinct app. The file says so on line 1: "This is a
generated file; do not edit or check into version control."

It was tracked anyway, and the committed copy carried a bundle id with no
hostname suffix. That identifier no longer matches the one in
GoogleService-Info.plist, so FirebaseApp configuration fails during startup —
the app builds fine and then dies every time it opens.

Two things kept this invisible to the file's owner:

- `.gitignore:78` already lists `**/ios/Flutter/Custom.xcconfig`, but gitignore
  does not apply to files that are already tracked, so the entry did nothing.
- The file was flagged `skip-worktree`, which suppresses the diff between the
  local machine-specific copy and the committed one. Locally everything looked
  clean; only a fresh clone saw the stale value.

`git rm --cached` untracks it while leaving the working copy in place. The
existing gitignore entry now takes effect, and `bash setup.sh ios` regenerates
it — `scripts/generate_ios_custom_config.sh` truncates and rewrites the file, so
a clone with no copy at all gets a complete one.

Also clears `skip-worktree` on the seven other flagged files (mobile_app.dart,
knowledge_graph_api.dart and the five providers). Those carry local demo
scaffolding that must not ship; the flag hid their divergence from HEAD, which
is the same mechanism that let demo hooks commit silently in f9c5c39. They
now show in `git status`, where they can be seen and deliberately left unstaged.

Verification: `git ls-files -v` reports zero skip-worktree entries;
`git check-ignore` now resolves Custom.xcconfig to .gitignore:78; the local file
retains its `-macbookpro` suffix; `git grep` over HEAD finds no dev_demo_data,
seedDemoData, kDemoDataEnabled or _kForceSignedInShellForUiWork.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`home_hero_pixel_test` hangs and is only recorded as a failure ten
minutes later, when the framework's default timeout fires. It wedges the
runner rather than failing: a `flutter test test/widgets/` run reaches
+148 in 22 seconds and then sits on this one test until the timeout. A
`--timeout 120s` on the command line does not shorten it.

The defect is in the test. `boundary.toImage()` runs inside
`tester.runAsync`, but `image.toByteData()` on the next line runs outside
it. Under the widget tester's fake async that future never completes, so
the await blocks forever.

Deleting rather than repairing it. This was a diagnostic I wrote while
tracking down whether the headline painted white, and its own doc comment
says so — "this establishes whether the widget itself paints white, so the
search can move to the app tree instead of going in circles". It prints
its finding and asserts only that some pixel is near-white. That question
was answered; what is left is a brittle probe that depends on the test
font renderer, and it never belonged in the committed suite.

The hero's committed coverage stays: `home_hero_entrance_test` exercises
the widget's real behaviour, and headline colour is covered by the
INV-UI-1 brand ratchet.

Verification: `flutter test test/widgets/` in a clean checkout of this
branch (no skip-worktree flags) — 148 pass, 1 fail before this commit,
the failure being this test's timeout. Re-run after removal reported
below.
@jacobtrebil jacobtrebil changed the title feat(app): UI redesign — Home is the chat, Brain tab, consolidated Todos feat(app): UI redesign — Convos-first nav, Chat tab, Brain tab, consolidated Todos Aug 9, 2026
@jacobtrebil

Copy link
Copy Markdown
Author

Fixed in f9c5c39 and b099e01.

The dev_demo_data import is gone from users.dart. You were right that it broke clean clones — the file is local-only and excluded from git, and the skip-worktree flag on the files around it meant this was invisible from my working tree.

There was a second instance you couldn't have seen without running Flutter: three action-item tests called ActionItemsProvider.seedDemoData, which also existed only locally, so the test suite didn't compile either. They now use the constructor injection this provider already had for getActionItems/deleteActionItem, with a fake update request in test/widgets/support/. The _demoMode flag those hooks relied on is removed — it suppressed the server write in production code, which was the more serious half of the problem.

Also removed home_hero_pixel_test, which awaited toByteData() outside tester.runAsync and wedged the runner until the 10-minute timeout.

Verified on a fresh checkout with no local index state: flutter analyze lib/ test/ 0 errors, flutter test test/widgets/ 148 pass / 0 fail. Note that no CI has run on this branch yet — the checks are still pending workflow approval — so those numbers are from my machine, not from Omi's CI.

The nav and Home/Chat changes are still product-direction calls and need a maintainer's view.

@undivisible

Copy link
Copy Markdown
Collaborator

yo can we get screenshots pls :))

@Git-on-my-level
Git-on-my-level dismissed their stale review August 10, 2026 00:16

Resolved on current head: the dev_demo_data import/branch and local-only seedDemoData hooks from the prior automation review are no longer present.

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the follow-up. I re-reviewed the current head and confirmed the previous build blocker around local demo scaffolding has been addressed: app/lib/backend/http/api/users.dart no longer imports dev_demo_data, and the prior seedDemoData references are gone from the providers.

I still need to request changes for a remaining navigation regression introduced by the tab reorder:

  • app/lib/pages/home/page.dart now defines the tab order as Conversations(0), embedded Chat/Home(1), Brain(2), Tasks(3), Apps(4), and app/lib/widgets/bottom_nav_bar.dart renders the same five-tab order. However the deep-link initialization still maps /action-items to homePageIdx = 2 at lines 307-310, and the later switch still says the action-items tab has already been selected. With the new order, index 2 is BrainPage, so notification/deep-link entry points for action items open the Brain tab instead of Todos. Please update that mapping to the new Tasks index and add/adjust a regression test for the route/initial-index mapping.

Other reviewed areas:

  • app/lib/pages/chat/page.dart correctly avoids disposing the shared HomeProvider.chatFieldFocusNode when embedded, and the empty-state composer/prompt structure is coherent with Home-as-chat.
  • app/lib/pages/action_items/action_items_page.dart now consolidates active tasks into one Todos bucket and delays provider completion until after the tap feedback; the provider seam in app/lib/providers/action_items_provider.dart is a reasonable way to keep widget tests hermetic.
  • app/lib/pages/conversations/widgets/daily_recaps_carousel.dart uses the existing getDailySummaries(limit: 7) path directly and stays out of the page when there are no summaries; I did not see the prior missing local fixture issue on this head.
  • app/lib/pages/memories/brain_page.dart embeds MemoryGraphPage with bottom-nav clearance, which matches the new tab-shell architecture.
  • app/pubspec.yaml, .gitignore, app/lib/gen/assets.gen.dart, and app/lib/gen/fonts.gen.dart are consistent with shipping Space Grotesk and assets/sounds/; this is product/brand-sensitive but not a supply-chain concern from the diff I inspected.
  • The new widget tests in app/test/widgets/ cover the action-item completion and home-hero behavior, but I could not run them in this review environment because Flutter is not installed here, and GitHub still has no passing check summary for this draft.

This PR is still a substantial app navigation/Home redesign, so after the routing bug is fixed it should remain with a human maintainer for product/UX sign-off before merge.


by AI on behalf of David; human maintainer review is needed for the app navigation and Home/Chat product direction.

Reviewed by glm-5.2 for the Omi maintainer automation.

@undivisible undivisible added the human Human-authored pull request label Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app flutter flutter work human Human-authored pull request mobile needs-maintainer-review Needs a human maintainer to sign off before merge needs-tests PR introduces logic that should be covered by tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants