fix(onboarding): Watch instructions layout, specific pairing errors, find-us fits on screen
Android pairs after opening Connect at 26%, versus 54% on iOS (new mobile signups observed September 13–October 12). The current failure copy offers no actionable cause; the Apple Watch paragraph uses title weight with its action mid-screen, and the acquisition survey hides its final choices below Continue.
This change pins Watch's primary action using the shared first-capture layout, presents body-weight recording steps while preserving paired/installed guidance, and fits all 12 acquisition choices in an ordered two-column layout at 393×852. Large text uses scrollable rows. Option labels, values, and order stay unchanged.
Connect displays an inline failure card with a visible dismiss, Try Again, and How to pair. Exposed Bluetooth power, Bluetooth/Nearby devices/location permissions, stale-bond, combined reachability, and unknown cases have actionable localized messages. Device Connect Failure Shown is a new registered event with failure_reason and platform, emitted after the card's first frame. Device Connect Failed and the registry contract stay unchanged. A 10-second empty scan retains No Omi Found, its instructions and Contact Support, and emits no Device Connect Failure Shown. A visible saved or discovered device clears the reachability card. Bluetooth-off and unauthorized exception types take precedence over stale-bond advice.
The connection service catches transport errors and returns null: not found, timed out, and connected-then-dropped cannot be separated here. They use not_found_or_dropped and shared reachability guidance. Follow-up: preserve typed transport causes at the service boundary in a separately reviewed connection change; this PR keeps transport and connection/launch-timeout behavior unchanged.
Validation:
bash app/test.sh test/widgets/onboarding_polish_test.dart: bootstrap and all 138 protected contract tests passed. The focused fixture was then corrected to use an inert provider rather than a live service singleton.ONB_POLISH_RENDER=1 flutter test test/widgets/onboarding_polish_test.dart: all 6 tests passed; Watch bottom inset/paired/installed guidance, every reason→copy, event properties and one event per presentation, connect dismiss, all 12 survey options visible at 393×852, 2.0-scale scrolling to Other and its editor, plus 21 original renders; the two provider-driven empty-scan renders are covered below.flutter test test/widgets/first_capture_test.dart test/widgets/onboarding_survey_sync_test.dart test/widgets/onboarding_bottom_inset_test.dart test/providers/onboarding_provider_connect_failure_test.dart test/services/devices/pairing_failure_test.dart test/utils/analytics/onboarding_funnel_analytics_test.dart: all 31 neighboring regressions passed. Updated only the first-capture pairing assertion/fixture for the new inline card; the sibling lane's five red test files are untouched.- Review regression coverage:
ONB_POLISH_RENDER=1 flutter test test/providers/onboarding_provider_pairing_presentation_test.dart test/widgets/onboarding_polish_test.dart test/widgets/first_capture_test.dart test/services/devices/pairing_failure_test.dart test/providers/onboarding_provider_connect_failure_test.dartpassed all 37 tests. This drives the real OnboardingProvider timer and onDevices paths, verifies telemetry via a recording AnalyticsAdapter, retains stale-bond guidance, and tests exception precedence. Connect-failure and empty-scan renders were regenerated on iOS/Android and opened; the empty-state controls inherit the themed real font. - Focused
flutter analyze: no issues.python3 app/scripts/l10n.py check: 49 locales, 3,877 consistent keys.python3 scripts/check_event_registry.py --write: 183 events and executable consumers pass.make preflight: all 24 selected checks passed on the final commit; metadata-only preflight also passed all 5 selected checks. - Screenshots: all 23 inspected PNGs and reproduction details. Watch, iOS reachability failure, Android reachability failure, iOS find-us, Android find-us, large text scrolled to final choices.
CI at a269e7046334eb49daeac059b3640325b8de592e: Dart Analyze & Tests ran under ci:full and completed failure. Analyzer passed. The only five failing cases are the known main failures tracked by #21425: launch-timeout connection preservation (1), category large-text layout (2), AI app generator large-text layout (1), and settings search header stability (1). No onboarding tests failed. No physical device pairing or full-suite acceptance is claimed.
Known follow-up: unify the global Bluetooth guidance sheet and inline failure card so they cannot stack. This PR preserves the existing guidance flow.
Risks: new messages use English fallback for other locales; no physical-device BLE acceptance is claimed; the existing global Bluetooth guidance can appear alongside the inline card; acquisition labels remain localized legacy analytics values; large text deliberately needs scrolling.




Failure-Class: none
🤖 Generated with Claude Code