From 420337031451296cfe97305fdef3a78167ec1c34 Mon Sep 17 00:00:00 2001 From: Mohammad Abdul Sahil <127765312+abdulsaheel@users.noreply.github.com> Date: Sat, 15 Aug 2026 23:01:48 +0530 Subject: [PATCH 001/104] rebuild the ui MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lib/ui is gone, lib/ui2 replaces it. the old one threw away five of the six fields analytics emits, so every number rendered bare — no confidence, no tier, no reason when it was absent. real bugs this turned up, all live before now: - metric.dart matched 'AUTHORITATIVE' but analytics emits 'AUTH', so that tier always parsed to unknown - every poincare row rendered empty (heart['irregular'] is a plain map read through envValue, and the fixture had the wrong shape so it hid it) - workout avg hr was lost after 3 days, recomputed off the pruned substrate. banked to sessions.avg_hr now - crossDayArtifactUsableToday ignored its own algo_version stamp - a flat series drew pinned to the floor of the card - gen5 stepMotionCounter never reached Substrate circadian was written, tested and called by nothing. wired it on hourly hr means (raw accel is pruned at 3 days) and said so in the envelope note. things the app can't actually produce are deleted rather than explained — blood pressure, whole-day energy, power zones. docs/internal/UI_ROADMAP.md has what each would need. schema 38, algo 66. --- .gitignore | 3 + UI_AUDIT.html | 762 ----- ios/Podfile.lock | 6 +- ios/Runner.xcodeproj/project.pbxproj | 22 + .../xcshareddata/xcschemes/Runner.xcscheme | 18 + lib/app.dart | 422 +-- lib/compute/crossday_pipeline.dart | 164 + lib/compute/derivation_engine.dart | 140 +- lib/compute/derive_prepare.dart | 14 + lib/compute/manual_session.dart | 4 + lib/compute/onehz_pipeline.dart | 12 + lib/compute/substrate.dart | 94 + lib/data/db.dart | 148 +- lib/data/local_repository.dart | 15 + lib/data/local_repository_impl.dart | 50 +- lib/data/med_store.dart | 336 ++ lib/data/nutrition_store.dart | 513 ++++ lib/models/metric.dart | 8 + lib/state/app_state.dart | 22 +- lib/{ui => }/stress/breath_phases.dart | 0 lib/ui/activity/live_session_screen.dart | 2728 ----------------- lib/ui/activity/strain_detail_screen.dart | 1146 ------- lib/ui/activity/workout_share_card.dart | 622 ---- lib/ui/ai/ai_breakdown_screen.dart | 323 -- lib/ui/ai/ai_settings_screen.dart | 271 -- lib/ui/coach/ai_coach_screen.dart | 499 --- lib/ui/coach/coach_chart.dart | 192 -- lib/ui/coach/coach_render.dart | 588 ---- lib/ui/coach/coach_screen.dart | 254 -- lib/ui/coach/coach_settings_screen.dart | 261 -- lib/ui/cycle/cycle_screen.dart | 532 ---- lib/ui/design/ai_hero.dart | 207 -- lib/ui/design/app_scaffold.dart | 244 -- lib/ui/design/arc_gauge.dart | 276 -- lib/ui/design/bento.dart | 336 -- lib/ui/design/big_stat.dart | 167 - lib/ui/design/controls.dart | 322 -- lib/ui/design/design.dart | 45 - lib/ui/design/disclosure.dart | 139 - lib/ui/design/domains.dart | 70 - lib/ui/design/fake_route_fixture.dart | 94 - lib/ui/design/gallery_screen.dart | 958 ------ lib/ui/design/hypnogram.dart | 325 -- lib/ui/design/info_sheet.dart | 124 - lib/ui/design/metric_card.dart | 290 -- lib/ui/design/motion.dart | 77 - lib/ui/design/nav_pill.dart | 139 - lib/ui/design/orbit_score.dart | 172 -- lib/ui/design/pressable.dart | 90 - lib/ui/design/recap_card.dart | 131 - lib/ui/design/ring_week.dart | 78 - lib/ui/design/rows.dart | 144 - lib/ui/design/spark.dart | 247 -- lib/ui/design/state_chips.dart | 164 - lib/ui/design/surface.dart | 113 - lib/ui/design/sync_dot.dart | 101 - lib/ui/find/find_band_screen.dart | 246 -- lib/ui/heart/live_hr_tile.dart | 275 -- lib/ui/import/import_screen.dart | 348 --- lib/ui/insights/coach_cards.dart | 599 ---- .../journal/custom_journal_field_sheet.dart | 255 -- lib/ui/journal/journal_compose_screen.dart | 461 --- lib/ui/journal/journal_metric_editor.dart | 385 --- lib/ui/journal/journal_screen.dart | 711 ----- lib/ui/journey/day_nav.dart | 82 - lib/ui/journey/journey_screen.dart | 497 --- lib/ui/kit/charts.dart | 1131 ------- lib/ui/kit/kit.dart | 702 ----- lib/ui/kit/os_icons.dart | 432 --- lib/ui/kit/route_map.dart | 863 ------ lib/ui/kit/share_origin.dart | 22 - lib/ui/kit/skeleton.dart | 189 -- lib/ui/kit/state_card.dart | 97 - lib/ui/labs/lab_entry_sheet.dart | 414 --- lib/ui/labs/labs_screen.dart | 321 -- lib/ui/onboarding/welcome_screen.dart | 433 --- lib/ui/pairing_screen.dart | 591 ---- lib/ui/profile/about_screen.dart | 129 - lib/ui/profile/advanced_data_screen.dart | 419 --- lib/ui/profile/data_history_screen.dart | 393 --- lib/ui/profile/gesture_section.dart | 97 - .../profile/notification_relay_section.dart | 307 -- .../profile/notification_settings_screen.dart | 267 -- lib/ui/profile/profile_screen.dart | 2113 ------------- lib/ui/profile_setup_screen.dart | 374 --- lib/ui/recap/recap_screen.dart | 622 ---- lib/ui/records/records_screen.dart | 450 --- lib/ui/screens/detail_cards.dart | 2364 -------------- lib/ui/screens/metric_row.dart | 186 -- lib/ui/screens/metric_screen.dart | 489 --- lib/ui/screens/screens.dart | 507 --- lib/ui/screens/trend_screen.dart | 238 -- lib/ui/sleep/sleep_detail_screen.dart | 1461 --------- lib/ui/sleep/sleep_periods_screen.dart | 467 --- lib/ui/splash/boot_splash.dart | 185 -- lib/ui/spotcheck/spot_check_screen.dart | 232 -- lib/ui/stress/calm_breathing_screen.dart | 447 --- lib/ui/stress/interval_timer_screen.dart | 253 -- lib/ui/stress/stress_screen.dart | 569 ---- lib/ui/timeline/timeline_screen.dart | 825 ----- lib/ui/today/ai_summary_card.dart | 55 - lib/ui/today/step_goal_screen.dart | 179 -- lib/ui/today/today_screen.dart | 1700 ---------- lib/ui/widgets/async_guards.dart | 51 - lib/ui/widgets/screen_loader.dart | 151 - lib/ui/widgets/status_banner.dart | 289 -- lib/ui/workouts/calorie_heatmap.dart | 768 ----- lib/ui/workouts/manual_workout_screen.dart | 459 --- lib/ui/workouts/workout_filter.dart | 191 -- lib/ui/workouts/workout_filter_sheet.dart | 169 - lib/ui/workouts/workout_types.dart | 251 -- lib/ui/workouts/workouts_screen.dart | 2208 ------------- lib/ui2/README.md | 378 +++ lib/ui2/activity/catalogue.dart | 299 ++ lib/ui2/activity/live.dart | 1638 ++++++++++ lib/ui2/activity/picker.dart | 317 ++ lib/ui2/activity/setup.dart | 300 ++ lib/ui2/activity/share.dart | 530 ++++ lib/ui2/activity/summary.dart | 1299 ++++++++ lib/ui2/app_shell.dart | 199 ++ lib/ui2/charts.dart | 684 +++++ lib/ui2/grammar.dart | 1448 +++++++++ lib/ui2/onboarding/pairing.dart | 297 ++ lib/ui2/onboarding/profile_setup.dart | 339 ++ lib/ui2/onboarding/splash.dart | 106 + lib/ui2/onboarding/welcome.dart | 261 ++ lib/ui2/paint_activity.dart | 525 ++++ lib/ui2/profile/devices.dart | 430 +++ lib/ui2/profile/profile.dart | 305 ++ lib/ui2/profile/settings.dart | 320 ++ lib/ui2/screens/calm_breathing.dart | 407 +++ lib/ui2/screens/circadian_detail.dart | 279 ++ lib/ui2/screens/health_screen.dart | 851 +++++ lib/ui2/screens/home_screen.dart | 665 ++++ lib/ui2/screens/investigate.dart | 304 ++ lib/ui2/screens/journal_compose.dart | 397 +++ lib/ui2/screens/log_food.dart | 393 +++ lib/ui2/screens/metric_detail.dart | 860 ++++++ lib/ui2/screens/nutrition_screen.dart | 521 ++++ lib/ui2/screens/readiness_detail.dart | 282 ++ lib/ui2/screens/screens.dart | 14 + lib/ui2/screens/sleep_detail.dart | 627 ++++ lib/ui2/screens/wellness_screen.dart | 736 +++++ lib/ui2/screens/workout_screen.dart | 978 ++++++ lib/ui2/theme.dart | 376 +++ lib/ui2/ui2.dart | 12 + pubspec.lock | 66 +- pubspec.yaml | 39 +- test/absent_not_zero_test.dart | 547 ---- test/ai_breakdown_widget_test.dart | 154 - test/ai_screen_async_guards_test.dart | 128 - test/band_step_counter_test.dart | 194 ++ test/boot_splash_test.dart | 98 - test/bottom_gutter_test.dart | 98 - test/breath_phases_test.dart | 2 +- test/calm_breathing_view_test.dart | 175 -- test/calorie_heatmap_test.dart | 597 ---- test/core_screens_test.dart | 687 ----- test/crash_regressions_test.dart | 367 --- test/crossday_artifact_freshness_test.dart | 30 +- test/crossday_circadian_test.dart | 204 ++ test/day_nav_test.dart | 159 - test/db_strength_schema_test.dart | 142 + test/design_redesign_test.dart | 458 --- test/design_system_test.dart | 415 --- test/disclosure_test.dart | 100 - test/fake_route_fixture_test.dart | 45 - test/find_band_view_test.dart | 110 - test/flow_screens_redesign_test.dart | 274 -- test/history_screens_redesign_test.dart | 486 --- test/interactive_screens_test.dart | 393 --- test/live_session_layout_test.dart | 205 -- test/lookback_gate_test.dart | 71 - test/metric_trend_redesign_test.dart | 475 --- test/nap_credit_test.dart | 164 - test/nav_back_swipe_test.dart | 94 - test/nutrition_med_model_test.dart | 232 ++ test/nutrition_med_schema_test.dart | 149 + test/os_icons_wiring_test.dart | 195 -- test/screen_loader_test.dart | 36 - test/sleep_alarm_caption_test.dart | 82 - test/sleep_naps_visible_test.dart | 163 - test/sleep_windows_and_session_avg_test.dart | 237 ++ test/strain_bonus_test.dart | 217 -- test/sync_dot_test.dart | 61 - test/timeline_scrub_test.dart | 160 - test/ui2_activity_test.dart | 607 ++++ test/ui2_charts_test.dart | 379 +++ test/ui2_contrast_test.dart | 131 + test/ui2_golden_test.dart | 414 +++ test/ui2_home_health_golden_test.dart | 501 +++ test/ui2_onboarding_profile_golden_test.dart | 212 ++ test/ui2_router_test.dart | 288 ++ test/ui2_tokens_test.dart | 142 + test/ui_kit_new_widgets_test.dart | 82 - test/week_view_feed_test.dart | 109 - test/workout_filter_test.dart | 298 -- test/workout_health_mapping_test.dart | 229 -- test/workout_share_card_test.dart | 280 -- test/workout_sleep_redesign_test.dart | 429 --- test/workout_types_label_test.dart | 100 - test/workouts_header_actions_test.dart | 160 - 202 files changed, 22971 insertions(+), 51417 deletions(-) delete mode 100644 UI_AUDIT.html create mode 100644 lib/data/med_store.dart create mode 100644 lib/data/nutrition_store.dart rename lib/{ui => }/stress/breath_phases.dart (100%) delete mode 100644 lib/ui/activity/live_session_screen.dart delete mode 100644 lib/ui/activity/strain_detail_screen.dart delete mode 100644 lib/ui/activity/workout_share_card.dart delete mode 100644 lib/ui/ai/ai_breakdown_screen.dart delete mode 100644 lib/ui/ai/ai_settings_screen.dart delete mode 100644 lib/ui/coach/ai_coach_screen.dart delete mode 100644 lib/ui/coach/coach_chart.dart delete mode 100644 lib/ui/coach/coach_render.dart delete mode 100644 lib/ui/coach/coach_screen.dart delete mode 100644 lib/ui/coach/coach_settings_screen.dart delete mode 100644 lib/ui/cycle/cycle_screen.dart delete mode 100644 lib/ui/design/ai_hero.dart delete mode 100644 lib/ui/design/app_scaffold.dart delete mode 100644 lib/ui/design/arc_gauge.dart delete mode 100644 lib/ui/design/bento.dart delete mode 100644 lib/ui/design/big_stat.dart delete mode 100644 lib/ui/design/controls.dart delete mode 100644 lib/ui/design/design.dart delete mode 100644 lib/ui/design/disclosure.dart delete mode 100644 lib/ui/design/domains.dart delete mode 100644 lib/ui/design/fake_route_fixture.dart delete mode 100644 lib/ui/design/gallery_screen.dart delete mode 100644 lib/ui/design/hypnogram.dart delete mode 100644 lib/ui/design/info_sheet.dart delete mode 100644 lib/ui/design/metric_card.dart delete mode 100644 lib/ui/design/motion.dart delete mode 100644 lib/ui/design/nav_pill.dart delete mode 100644 lib/ui/design/orbit_score.dart delete mode 100644 lib/ui/design/pressable.dart delete mode 100644 lib/ui/design/recap_card.dart delete mode 100644 lib/ui/design/ring_week.dart delete mode 100644 lib/ui/design/rows.dart delete mode 100644 lib/ui/design/spark.dart delete mode 100644 lib/ui/design/state_chips.dart delete mode 100644 lib/ui/design/surface.dart delete mode 100644 lib/ui/design/sync_dot.dart delete mode 100644 lib/ui/find/find_band_screen.dart delete mode 100644 lib/ui/heart/live_hr_tile.dart delete mode 100644 lib/ui/import/import_screen.dart delete mode 100644 lib/ui/insights/coach_cards.dart delete mode 100644 lib/ui/journal/custom_journal_field_sheet.dart delete mode 100644 lib/ui/journal/journal_compose_screen.dart delete mode 100644 lib/ui/journal/journal_metric_editor.dart delete mode 100644 lib/ui/journal/journal_screen.dart delete mode 100644 lib/ui/journey/day_nav.dart delete mode 100644 lib/ui/journey/journey_screen.dart delete mode 100644 lib/ui/kit/charts.dart delete mode 100644 lib/ui/kit/kit.dart delete mode 100644 lib/ui/kit/os_icons.dart delete mode 100644 lib/ui/kit/route_map.dart delete mode 100644 lib/ui/kit/share_origin.dart delete mode 100644 lib/ui/kit/skeleton.dart delete mode 100644 lib/ui/kit/state_card.dart delete mode 100644 lib/ui/labs/lab_entry_sheet.dart delete mode 100644 lib/ui/labs/labs_screen.dart delete mode 100644 lib/ui/onboarding/welcome_screen.dart delete mode 100644 lib/ui/pairing_screen.dart delete mode 100644 lib/ui/profile/about_screen.dart delete mode 100644 lib/ui/profile/advanced_data_screen.dart delete mode 100644 lib/ui/profile/data_history_screen.dart delete mode 100644 lib/ui/profile/gesture_section.dart delete mode 100644 lib/ui/profile/notification_relay_section.dart delete mode 100644 lib/ui/profile/notification_settings_screen.dart delete mode 100644 lib/ui/profile/profile_screen.dart delete mode 100644 lib/ui/profile_setup_screen.dart delete mode 100644 lib/ui/recap/recap_screen.dart delete mode 100644 lib/ui/records/records_screen.dart delete mode 100644 lib/ui/screens/detail_cards.dart delete mode 100644 lib/ui/screens/metric_row.dart delete mode 100644 lib/ui/screens/metric_screen.dart delete mode 100644 lib/ui/screens/screens.dart delete mode 100644 lib/ui/screens/trend_screen.dart delete mode 100644 lib/ui/sleep/sleep_detail_screen.dart delete mode 100644 lib/ui/sleep/sleep_periods_screen.dart delete mode 100644 lib/ui/splash/boot_splash.dart delete mode 100644 lib/ui/spotcheck/spot_check_screen.dart delete mode 100644 lib/ui/stress/calm_breathing_screen.dart delete mode 100644 lib/ui/stress/interval_timer_screen.dart delete mode 100644 lib/ui/stress/stress_screen.dart delete mode 100644 lib/ui/timeline/timeline_screen.dart delete mode 100644 lib/ui/today/ai_summary_card.dart delete mode 100644 lib/ui/today/step_goal_screen.dart delete mode 100644 lib/ui/today/today_screen.dart delete mode 100644 lib/ui/widgets/async_guards.dart delete mode 100644 lib/ui/widgets/screen_loader.dart delete mode 100644 lib/ui/widgets/status_banner.dart delete mode 100644 lib/ui/workouts/calorie_heatmap.dart delete mode 100644 lib/ui/workouts/manual_workout_screen.dart delete mode 100644 lib/ui/workouts/workout_filter.dart delete mode 100644 lib/ui/workouts/workout_filter_sheet.dart delete mode 100644 lib/ui/workouts/workout_types.dart delete mode 100644 lib/ui/workouts/workouts_screen.dart create mode 100644 lib/ui2/README.md create mode 100644 lib/ui2/activity/catalogue.dart create mode 100644 lib/ui2/activity/live.dart create mode 100644 lib/ui2/activity/picker.dart create mode 100644 lib/ui2/activity/setup.dart create mode 100644 lib/ui2/activity/share.dart create mode 100644 lib/ui2/activity/summary.dart create mode 100644 lib/ui2/app_shell.dart create mode 100644 lib/ui2/charts.dart create mode 100644 lib/ui2/grammar.dart create mode 100644 lib/ui2/onboarding/pairing.dart create mode 100644 lib/ui2/onboarding/profile_setup.dart create mode 100644 lib/ui2/onboarding/splash.dart create mode 100644 lib/ui2/onboarding/welcome.dart create mode 100644 lib/ui2/paint_activity.dart create mode 100644 lib/ui2/profile/devices.dart create mode 100644 lib/ui2/profile/profile.dart create mode 100644 lib/ui2/profile/settings.dart create mode 100644 lib/ui2/screens/calm_breathing.dart create mode 100644 lib/ui2/screens/circadian_detail.dart create mode 100644 lib/ui2/screens/health_screen.dart create mode 100644 lib/ui2/screens/home_screen.dart create mode 100644 lib/ui2/screens/investigate.dart create mode 100644 lib/ui2/screens/journal_compose.dart create mode 100644 lib/ui2/screens/log_food.dart create mode 100644 lib/ui2/screens/metric_detail.dart create mode 100644 lib/ui2/screens/nutrition_screen.dart create mode 100644 lib/ui2/screens/readiness_detail.dart create mode 100644 lib/ui2/screens/screens.dart create mode 100644 lib/ui2/screens/sleep_detail.dart create mode 100644 lib/ui2/screens/wellness_screen.dart create mode 100644 lib/ui2/screens/workout_screen.dart create mode 100644 lib/ui2/theme.dart create mode 100644 lib/ui2/ui2.dart delete mode 100644 test/absent_not_zero_test.dart delete mode 100644 test/ai_breakdown_widget_test.dart delete mode 100644 test/ai_screen_async_guards_test.dart create mode 100644 test/band_step_counter_test.dart delete mode 100644 test/boot_splash_test.dart delete mode 100644 test/bottom_gutter_test.dart delete mode 100644 test/calm_breathing_view_test.dart delete mode 100644 test/calorie_heatmap_test.dart delete mode 100644 test/core_screens_test.dart delete mode 100644 test/crash_regressions_test.dart create mode 100644 test/crossday_circadian_test.dart delete mode 100644 test/day_nav_test.dart create mode 100644 test/db_strength_schema_test.dart delete mode 100644 test/design_redesign_test.dart delete mode 100644 test/design_system_test.dart delete mode 100644 test/disclosure_test.dart delete mode 100644 test/fake_route_fixture_test.dart delete mode 100644 test/find_band_view_test.dart delete mode 100644 test/flow_screens_redesign_test.dart delete mode 100644 test/history_screens_redesign_test.dart delete mode 100644 test/interactive_screens_test.dart delete mode 100644 test/live_session_layout_test.dart delete mode 100644 test/lookback_gate_test.dart delete mode 100644 test/metric_trend_redesign_test.dart delete mode 100644 test/nap_credit_test.dart delete mode 100644 test/nav_back_swipe_test.dart create mode 100644 test/nutrition_med_model_test.dart create mode 100644 test/nutrition_med_schema_test.dart delete mode 100644 test/os_icons_wiring_test.dart delete mode 100644 test/screen_loader_test.dart delete mode 100644 test/sleep_alarm_caption_test.dart delete mode 100644 test/sleep_naps_visible_test.dart create mode 100644 test/sleep_windows_and_session_avg_test.dart delete mode 100644 test/strain_bonus_test.dart delete mode 100644 test/sync_dot_test.dart delete mode 100644 test/timeline_scrub_test.dart create mode 100644 test/ui2_activity_test.dart create mode 100644 test/ui2_charts_test.dart create mode 100644 test/ui2_contrast_test.dart create mode 100644 test/ui2_golden_test.dart create mode 100644 test/ui2_home_health_golden_test.dart create mode 100644 test/ui2_onboarding_profile_golden_test.dart create mode 100644 test/ui2_router_test.dart create mode 100644 test/ui2_tokens_test.dart delete mode 100644 test/ui_kit_new_widgets_test.dart delete mode 100644 test/week_view_feed_test.dart delete mode 100644 test/workout_filter_test.dart delete mode 100644 test/workout_health_mapping_test.dart delete mode 100644 test/workout_share_card_test.dart delete mode 100644 test/workout_sleep_redesign_test.dart delete mode 100644 test/workout_types_label_test.dart delete mode 100644 test/workouts_header_actions_test.dart diff --git a/.gitignore b/.gitignore index b61a91a4..36f86e64 100644 --- a/.gitignore +++ b/.gitignore @@ -98,3 +98,6 @@ lib/firebase_options.dart # into Discord/issues, and they're ~750kb a piece. docs/*.gif docs/*.mp4 + +# golden test failure output +test/failures/ diff --git a/UI_AUDIT.html b/UI_AUDIT.html deleted file mode 100644 index d6d1382a..00000000 --- a/UI_AUDIT.html +++ /dev/null @@ -1,762 +0,0 @@ - - -
- - -OpenStrap · edge · v0.9.26+57 · UI/UX audit
-- The analytics engine returns six fields per metric. The interface renders one of them. - Every other problem in this audit is downstream of that. -
- -Verdict
-5.9 /10
-- Not bad — undifferentiated. The craft is well above the Flutter-health median: - real tokens, real null-honesty copy, a measured contrast test already in CI. What it is not - is this product's interface. It renders like a competent WHOOP clone sitting on top - of an engine that could beat WHOOP on the one axis WHOOP can't compete on: telling the truth - about what it doesn't know. -
-The finding
-
- Analytics hands the UI a Metric. It carries a value, a confidence, a tier
- (AUTH / HIGH / ESTIMATE / RELATIVE),
- the inputs it used, the drivers that moved it, and a note saying why it's absent. Five of those
- six survive the trip to a pixel almost nowhere.
-
- The band's whole claim is that it never guesses.
The interface renders that claim as a dash.
-
102
-bare — literals in lib/ui. 1 call site of needMessageFromNote, the function that turns need_baseline:have=3,need=14 into a sentence a human can act on. detail_cards.dart:818
1 of 13
-ArcGauge call sites pass confidence; it defaults to 1.0. Every sparkline, bar and time series has no confidence parameter at all. An ESTIMATE respiratory rate and an AUTH heart rate render as identical pixels. charts.dart:36
3
-reads of Metric.note across all 41,588 lines. The reason a number is missing is computed, stored, shipped to the device — and then dropped one layer before the eye.
0
-places where a drivers list is shown as an attribution. Oura's Contributors panel is the category baseline in 2026; this app computes something strictly better and never draws it.
-By discipline
-Design system — tokens, type, components
-6.5
- -UX craft — states, a11y, motion
-6.0
- -Information architecture — nav, screens, flows
-5.5
- -Data visualisation — charts, gauges, hypnogram
-5.5
- -
- The design system scores highest because it exists: a 4 pt spacing scale at 93% adoption,
- 753 AppColors and 615 AppText call sites, dark mode re-derived rather than
- inverted. The failure is not absence of a system — it is two systems, both re-exported
- from the same barrel, with 21 files importing both. design.dart:36
-
Ranked findings
-| Sev | Finding | Where | Fix |
|---|---|---|---|
| P0 | -Muted caption text fails WCAG AA — 2.71:1, 2.40:1 and 2.20:1 depending on surface. It is the app's caption and label voice, so it fails everywhere. | -theme.dart:159,165 223 uses |
- Two hex values. A contrast test harness already exists. | -
| P0 | -Zero reduced-motion support. 13 infinite .repeat() loops, including a 2.6 s breathing icon on every empty state that never stops. |
- motion.dart:32 state_card.dart:43 |
- One guard inside the DsMotion extension fixes all 34 caller files. |
-
| P0 | -A 958-line design gallery ships in release builds, routed from Settings under a "Developer" heading. | -profile_screen.dart:747 | -Wrap the route in kDebugMode. |
-
| P0 | -No downsampling anywhere. A night of 1 Hz HR is ~30,000 chart points per frame, with an O(n) linear scan on every drag pixel. | -charts.dart:381,624 | -Min/max bucketing to 2× pixel width. _bucketAvg already exists — promote it. |
-
| P0 | -The Insights screen returns SizedBox.shrink() while loading and when empty. Its content silently does not exist. |
- coach_cards.dart :176,365,436,534 |
- Skeleton, then StateCard with the have/need line. |
-
| P1 | -Today's tiles push Sleep/Heart/Body — the same classes that are already tabs 1/2/3. Two live instances with divergent scroll and drill state; "back" means two different things depending on how you arrived. | -today_screen.dart:441 vs app.dart:236 |
- Tiles switch tabs. Never push a destination that is also a tab. | -
| P1 | -The onboarding validator contradicts its own copy: the screen promises "leave a field blank and only that metric stays unknown — never guessed", then requires all four fields. | -profile_setup_screen .dart:139 vs :193 |
- Allow blanks. Gate Continue on sex alone. | -
| P1 | -No escape from pairing. Bond-refused and picker-cancelled both fall through to one generic "Couldn't pair". This is the drop-off cliff of a hardware app. | -pairing_screen.dart :178,190 |
- Distinct refused/cancelled states, plus a skip-to-shell path. | -
| P1 | -The app's most visible element ignores the type scale. BigStat and MetricCard override AppText.hero/display/metric; hero numbers render at 26, 27, 36, 40, 44, 46, 48, 52, 54, 64 and 72 px across screens, at 14 different letter-spacings. |
- big_stat.dart:66 metric_card.dart:112 |
- Promote metricXl…metricSm into AppText; delete the overrides. |
-
| P1 | -A shadow chart library. Five line-chart implementations, three gauges, two hypnograms, two sparklines — and coach_render.dart's copies interpolate straight across null gaps, which is a lie in a null-honest app. |
- coach_render.dart coach_chart.dart |
- One engine each. ~400 lines net deletion. | -
| P1 | -A dual-axis chart normalises each series to its own min/max on a shared grid. Every crossing and convergence it draws is an artifact. | -coach_render.dart:234 | -Delete. Two stacked charts, or index to a common base. | -
| P1 | -Journey — the richest view in the app at 1,300+ lines — is reachable through exactly one card that hides itself for the first 24 hours. New users never learn it exists. | -today_screen.dart:416 | -Promote to a tab. | -
| P2 | -23 Semantics against 236 onTap:. Charts are blanket-ExcludeSemantics. Confidence is a 7 px hue-only dot with no label. |
- kit.dart:498 charts.dart:437 |
- Semantic labels on every interactive surface; colour + word + shape for status. | -
| P2 | -Sub-44 pt tap targets on primary controls (34 pt segmented controls, 40 pt back button); 68 candidate sites. | -controls.dart:70 app_scaffold.dart:43 |
- Enforce the floor inside Pressable so no call site can opt out. |
-
| P2 | -Unbounded workout history built into a non-lazy ListView(children:), each row wrapped in its own animation ticker. One ListView.builder exists in the entire UI. |
- workouts_screen.dart :354,461 |
- ListView.builder; stagger only the first viewport. |
-
| P2 | -Hypnogram — the signature visual of any sleep app — has no minimum segment width (a 30 s wake epoch is sub-pixel on an 8 h night), no time axis, no scrub, and shouldRepaint => true. |
- hypnogram.dart:99,123 | -Clamp to ≥1.5 px, add hour ticks and scrub-to-read. | -
| P2 | -Four god-screens: 2,728 / 2,208 / 2,113 / 1,700 lines. Meanwhile ~5,300 bespoke day-view lines sit beside a 489-line generic one that already works. | -live_session · workouts profile · today |
- Split by responsibility; route the rest through MetricScreen. |
-
| P2 | -The app calls itself "OpenStrap" during onboarding and "Edge" once you're inside. Jargon leaks as primary labels: MS RMSSD, pNN50, SD1/SD2. |
- welcome_screen.dart:365 spot_check_screen.dart:186 |
- One name. Method notes hold the method names, not stat captions. | -
Ship blockers
-2.20:1 at worst against a 4.5:1 requirement, across 223 call sites. Two hex values in theme.dart.
Users with vestibular disorders get a permanently pulsing empty state and a shimmer that never stops. Also a ticker burning battery on an idle screen.
-A design gallery and a fake-route fixture reachable from user-facing Settings.
-Every sleep detail view rebuilds a 30k-segment path per frame and scans it linearly on each drag pixel. This is the jank users will describe as "the app feels cheap".
-Target
-- Not "more polish". The target is an interface whose structure is the honesty guarantee, - where a fabricated number is unrepresentable rather than merely avoided. -
- -1
-A dash is a bug. One MetricValue widget: value → note-derived reason ("4 of 14 nights") → device reason ("not synced since 2 pm") → a dash only as an assertion failure. Target: ≤5 literal dashes in lib/ui, down from 102.
2
-Tier is an encoding, not a badge. ESTIMATE renders in lighter weight with a hatched band; RELATIVE plots against a zero-line that means "your normal"; confidence below 0.4 dashes the stroke. Enforced in the one chart engine so no call site can regress it. No yellow, no warning triangle — the way Apple Weather greys distant forecast days.
3
-Drivers are the hero, not a tap-through. Three signed attributions under the score, always expanded — "HRV −18% vs baseline · sleep debt 1 h 40 m · late alcohol logged". This is Oura's Contributors panel, except causal, and the engine already computes it.
-4
-Day 0 shows a contract, not a skeleton. No zeros, no shimmer implying a number is imminent: "Readiness needs 3 nights. 1 done. First score Thursday morning" — alongside live raw HR, the only thing that is real on day 0 and the proof the band is talking.
-5
-One hero, not three dials. WHOOP's three-dial home forces the user to arbitrate. Readiness alone above the fold in the morning; strain-so-far against today's ceiling in the evening; everything else earns its way below. The AI briefing is not a co-hero — an LLM paragraph at the top makes a local-first app look like a wrapper.
-6
-The receipt. Every score links to its inputs: which records, which gaps, coverage in real units ("6 h 12 m of 7 h 40 m in bed"), which algo version. When a version change re-scores a past day, say so on the card instead of silently mutating history.
-7
-Lead with what WHOOP cannot do. A smart alarm that wakes you in light sleep, using live per-second staging, with a morning receipt — "woke you at 06:31, 4 minutes into light sleep". Plus a raw 1 Hz viewer and one-tap export. No subscription product can offer either.
-Sequence
-1
-Roughly a day. Almost entirely mechanical.
-theme.dart fix contrast across 223 call sites — then assert it in CI, which the palette already supports.DsMotion extension gives 34 files reduced-motion; kill the 13 infinite loops.kDebugMode wrapper removes the gallery from release.Pressable, so no future call site can opt out.timeline_screen.2
-The phase that changes what the product is.
-MetricValue replaces all 102 dashes with reasons.confidence and tier become required parameters on every series-bearing widget.3
-Net deletion. The diff should be negative.
-kit/design pair, codemod, delete the loser, drop the re-export.fontSize:, Color(0x, Colors.white and literal dashes — the harness already exists in dart_source_test.dart, and it is the only fix in this document that is permanent.4
-Where it stops looking like a hobby build.
-- The uncomfortable summary: the hard part is already built. On-device staging, corrected RR - intervals, personal baselines, per-metric confidence, honest nulls — years of work that no - competitor ships. The interface throws away five of the six fields that work produces, and - then looks like every other recovery app. -
-- Phase 1 is a day. Phase 2 is what makes it this app. -
-