Skip to main content

Observability — analytics & error tracking

Two vendor integrations, both real SDKs but config-gated off by default (no token/DSN dart-define → the provider never initializes; a clean no-op in dev and tests), plus a debug-only DevTools bridge. All three sit in the outside layer.

Mixpanel — product analytics

  • mixpanel_flutter: ^2.8.0, behind MixpanelEffect (app/lib/outside/effect_providers/mixpanel/) with a MixpanelEffectFake for tests and a route_observer.dart that auto-tracks page views.
  • Config-gated: with no MIXPANEL_TOKEN dart-define the provider never initializes the SDK — events are dropped, not sent.
  • Privacy by construction: a per-install hashing salt anonymizes the distinct id, and the JTBD funnels emit bucketed values (e.g. token_value_bucket: '1-3' | '4-9' | '10+'), never raw token amounts.
  • The JTBD funnels (fired from TodayChoresBloc): today_section_shown, bounty_claimed, chore_completion_submitted, chore_completion_approved, approval_resolved. See Today JTBD funnels.

Sentry — crash / error observability

  • sentry_flutter: ^9.21.0 + sentry_logging: ^9.21.0, behind SentryClientProvider (app/lib/outside/client_providers/sentry/) with a nullable Configuration.
  • Config-gated: no SENTRY_DSN dart-define → never initialized.
  • Capture path: the LoggingIntegration bridges package:logging into Sentry — a log.severe(...) becomes a Sentry event. (This replaced an earlier "Sentry Effect"; manual capture now goes through logging.)

Bloc DevTools extension

  • packages/bloc_devtools_extension/ — a DevTools plugin. The app posts bloc events via DevtoolsBlocObserver (debug-only), serializing each @JsonSerializable bloc state. This is why the bloc-state contract is non-negotiable (see Architecture and Add a bloc & page).