# Sentry FE wire-up — 2026-05-11 (Task 2.5 FE)

**Status: DONE.** Single commit `b7f1614` on `master`.

## Package

| Field | Value |
|-------|-------|
| Package | `sentry_flutter` |
| Constraint in `pubspec.yaml` | `^8.0.0` (under `dependencies:`, not dev) |
| Resolved version | `sentry_flutter 8.14.2` (transitive: `sentry 8.14.2`) |

Brief said `dev_dependencies` — corrected to `dependencies`. `SentryFlutter`
is imported from `lib/main.dart` at runtime, so it must be a production
dep. Keeping it under `dev_dependencies` would break `flutter build`.

## `SentryFlutter.init` wrap location

`lib/main.dart`:

| Line | Purpose |
|------|---------|
| 2    | Added `kDebugMode` to the existing `flutter/foundation.dart` `show` clause |
| 5    | New import: `package:sentry_flutter/sentry_flutter.dart` |
| 15–17 | Top-level `const String kSentryDsn = '…'` with a comment explaining why a Flutter DSN isn't a secret |
| 36   | `await SentryFlutter.init(` opens |
| 37–41 | Options closure: `dsn = kSentryDsn`, `tracesSampleRate = 0.2`, `debug = kDebugMode` |
| 42   | `appRunner: () => runApp(const MyApp())` — replaces the original `runApp(const MyApp())` at the bottom of `main()` |
| 43   | `);` close |

`main()` was already `async` (it awaits `PackageInfo.fromPlatform`), so no
signature change was needed. Stripe init and `AppConfig.clientVersion`
setup still run before Sentry init, preserving their existing ordering.

## Test button placement

Two-file change:

1. **New widget** `lib/widgets/sentry_test_button.dart` — a `StatelessWidget`
   that returns `SizedBox.shrink()` when `!kDebugMode`, otherwise renders
   an `IconButton` (red `Icons.bug_report`, tooltip `Throw test exception
   (Sentry)`) whose `onPressed` throws a `StateError`.
2. **Wired into** `lib/widgets/common_header.dart`, the shared top bar
   imported by every full-screen page in the app via `CommonHeader`. The
   button sits in a `const Row(mainAxisSize: MainAxisSize.min, …)` next
   to the existing `DevEnvironmentChip`, so both dev affordances render
   together on the right edge of the header.

Why `CommonHeader` rather than the home AppBar: home screen doesn't use
an `AppBar` (it builds a custom `Scaffold` body), but every other major
screen uses `CommonHeader` either directly or via a `Scaffold.appBar`
slot containing `DevEnvironmentChip`. Putting the button in
`CommonHeader` matches the existing dev-affordance pattern (CLAUDE.md's
"dev banner") and surfaces the trigger on every screen during dev — which
is what you want when verifying Sentry captures route-specific context.
In production release builds, `kDebugMode` is `false` and the widget
returns nothing, so there's no risk of end-users seeing or tapping it.

## Verification steps (for Pam)

```powershell
# 1. Build & run a debug build against any environment (DSN is hardcoded):
flutter run -d chrome
#    or:
flutter run -d windows

# 2. Once the app loads, look at the top-right of any screen that uses
#    CommonHeader. You should see a red bug icon to the LEFT of the
#    "DEV: <host>" chip. The bug icon appears in EVERY debug build
#    regardless of API_BASE_URL (the chip only appears in non-prod).

# 3. Tap the bug icon. The app will throw an uncaught
#    `StateError('Sentry test exception — fired from SentryTestButton')`.
#    In debug mode you'll see the standard red error screen / DevTools
#    exception breakpoint — that's expected. Hot-restart (`R` in flutter
#    run) to recover.

# 4. Open the Sentry project dashboard:
#       https://o4511358339645440.ingest.us.sentry.io/  (org)
#    Project DSN ends in /4511369366929408 — find that project, open the
#    Issues feed. Within ~10–30 seconds you should see a new issue:
#       StateError: Sentry test exception — fired from SentryTestButton
#    Click in: the stack trace should point at sentry_test_button.dart.

# 5. Confirm release build hides the button:
flutter run --release -d chrome
#    The bug icon should be ABSENT. Only the DEV chip may still show if
#    you're pointed at a non-prod API host.
```

If step 4 doesn't show the event:
- Confirm network egress to `*.ingest.us.sentry.io` isn't blocked.
- Increase verbosity: in debug mode, `options.debug = kDebugMode` makes
  Sentry print its lifecycle to console — look for `Sentry: …` lines.
- DSN typo would surface as a 4xx in those debug logs.

## `flutter analyze`

```
152 issues found. (ran in 2.4s)
```

Error count: **0**. All 152 are pre-existing info/warning lints carried
over from before this change (mostly `avoid_print` in `lib/services/`,
plus a handful of `use_build_context_synchronously` and unused-element
warnings). My changes did not introduce any new error or warning. They
briefly introduced one `prefer_const_constructors` info on the new
`Row` in `common_header.dart`, which I fixed by promoting the `Row` to
`const` — that drop from 153 → 152 is visible in the diff.

## `flutter test`

```
Test directory "test" not found.
```

This matches the post-`cd0f685` ("drop boilerplate counter test") state:
the repo currently has no committed tests under `test/`. The autostash
captured the untracked halt-report markdown files that had previously
been written under `test/`, so the directory itself is now absent.
"No test directory" is the same state as before this change — no NEW
failures.

## Auto-stash

Per the authorization in the third re-invocation, dirty pre-flight state
was captured into a single stash entry before any work began:

```
stash@{0}: On master: pre-sentry-fe-2026-05-11-autostash
```

Contents (7 paths, 505 insertions / 159 deletions):

| Path | Type | One-line summary |
|------|------|------------------|
| `30` | untracked (0-byte file) | Stray file at repo root — most likely an accidental shell redirect (e.g. `git log -30 > 30`). Safe to drop. |
| `lib/screens/diagnostic/diagnostic_result_screen.dart` | modified | Mid-flight edits to diagnostic result screen (+68 lines, presumably WIP). Predates today's session. |
| `lib/screens/diagnostic/diagnostic_screen.dart` | modified | Large refactor in progress on diagnostic screen (~284 lines touched, +/- roughly balanced). Predates today's session. |
| `lib/screens/home_screen.dart` | modified | Small edit on home screen (+4 lines). Predates today's session. |
| `ops/sentry-fe-2026-05-11.md` | untracked | Halt report from attempt #2 of this task. Now superseded by this report. Safe to drop. |
| `test/lunch-run-fe-2026-05-11.md` | untracked | Halt report from morning's Task 2.4 FE smoke-tests attempt. Documents architecture findings (HTTP client = `http`, no Riverpod/bloc seen; service classes under `lib/services/*Service.dart`). Worth keeping until Task 2.4 FE is actually completed. |
| `test/sentry-fe-2026-05-11.md` | untracked | Halt report from attempt #3 pre-flight, written *before* the auto-stash auth was granted. Superseded by this file (same path). Safe to drop. |

To inspect and decide what to recover:

```powershell
git stash show -p --include-untracked stash@{0}   # full patch incl. untracked
git checkout stash@{0} -- lib/screens/diagnostic   # restore just those dirs
git checkout stash@{0} -- test/lunch-run-fe-2026-05-11.md   # recover the morning halt note if you want it
git stash drop stash@{0}                          # discard everything in the stash
```

Recommended: restore the morning halt note for Task 2.4 FE context, then
triage the three `lib/` files (they look like in-progress diagnostic
work that someone forgot to commit), then drop the rest.

## Commit

```
b7f1614 feat(observability): wire Sentry for FE (Task 2.5 FE)
```

Files: `lib/main.dart`, `lib/widgets/common_header.dart`,
`lib/widgets/sentry_test_button.dart` (new), `pubspec.yaml`,
`pubspec.lock`, plus auto-generated platform plugin registrants for
linux / macos / windows (the standard `flutter pub get` side-effect of
adding a plugin with native platform code).

## Next steps not in this commit

- Source-map upload to Sentry on release builds (`sentry-cli` in CI).
- User-context enrichment: call `Sentry.configureScope` after login to
  attach `user.id` / role so issues are queryable per-user.
- Filter out noisy expected errors (e.g. 401s from background polling)
  via `options.beforeSend` so the quota goes to real bugs.
- Add a real-device verification test once a tester device is to hand.
