critical priority medium complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

FeatureFlagProvider is implemented as a Riverpod AsyncNotifier<Map<String, bool>> where keys are flag constant strings from 695-feature-flag-constants
On build, the provider reads the active organization from the auth session provider and calls FeatureFlagInitializer.initialize() before constructing the flag map
The flag map is constructed by evaluating each raw flag through RolloutEvaluator — the map contains resolved boolean values, not raw flag objects
When the active organization changes (org switch), the provider invalidates itself and rebuilds the full flag map for the new organization
Consumers calling `ref.watch(featureFlagProvider).value?['some_flag_key'] ?? false` receive the correct resolved boolean without additional evaluation logic
A convenience method or extension `isEnabled(String flagKey)` is available so consumer widgets do not need to handle nullable map access
The provider's AsyncValue.loading state is propagated correctly — consumers can check `isLoading` before rendering gated content
Provider is tested in isolation with mocked FeatureFlagInitializer and RolloutEvaluator using Riverpod's ProviderContainer test utilities

Technical Requirements

frameworks
Flutter
Riverpod
Dart
apis
Supabase Auth (organization context)
data models
activity_type
performance requirements
Flag map construction (evaluating all flags) must complete in under 10ms synchronously after data is available
Provider rebuild on org switch must not cause visible frame drops — evaluation is CPU-light, not async
security requirements
Active organization context must be sourced from the auth JWT provider — never from mutable local state that could be tampered with
The resolved flag map is read-only from consumers — no public mutation methods on the notifier
Flag evaluation must not log flag values to crash reporters — flag states may reveal unreleased features
ui components
FeatureGate widget (downstream consumer of this provider — not built in this task but must be compatible)

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Declare the provider with `@riverpod` annotation if using riverpod_generator, or as a top-level `final featureFlagProvider = AsyncNotifierProvider>(() => FeatureFlagProvider())`. To trigger rebuild on org switch, watch the active organization provider inside `build()` — Riverpod will automatically re-run `build()` when that dependency changes. Build the flag map using `Map.fromEntries(flags.map((f) => MapEntry(f.key, rolloutEvaluator.evaluate(f, context))))` for readability. Add the `isEnabled(String key)` convenience method as an extension on `AsyncValue>` rather than on the notifier itself, so widget code reads as `ref.watch(featureFlagProvider).isEnabled('my_flag')`.

This pattern composes cleanly with AsyncValue's `whenData` and `value` accessors.

Testing Requirements

Unit tests using ProviderContainer with overrideWithValue: (1) happy path — mock initializer completes, mock repository returns 3 flags, assert all 3 keys present in map with correct boolean values; (2) org switch — override activeOrgProvider with a different orgId after initial build, assert provider rebuilds and map reflects new org's flags; (3) empty flags — mock returns empty list, assert map is empty and isEnabled returns false for any key; (4) loading state — mock initializer that never completes, assert provider is in loading state. All tests use flutter_test with no real Riverpod internals replaced.

Component
Feature Flag Provider
infrastructure low
Epic Risks (2)
high impact medium prob technical

The feature-flag-initializer must complete before any screen that checks feature flags renders. If the navigation shell is pushed concurrently with initialization (e.g., via a parallel Riverpod provider chain), some screens may query flags before they are loaded and incorrectly receive all-disabled defaults.

Mitigation & Contingency

Mitigation: Gate the main navigation shell render behind the feature-flag-initializer's Future by using a splash/loading screen that awaits the initialization provider. Use Riverpod's ref.watch on an initialization state enum (loading/ready/error) to block rendering.

Contingency: If race conditions are observed in testing, introduce an explicit initialization barrier using a ChangeNotifier or a dedicated `featureFlagsReadyProvider` that the router guard checks before allowing navigation.

medium impact low prob technical

If feature-flag-provider is watched by many widgets simultaneously and a flag map refresh triggers all of them to rebuild at once (e.g., after an organization switch), the app could experience a significant UI jank or dropped frames.

Mitigation & Contingency

Mitigation: Use select() on the provider to have each widget watch only the specific flag key it needs rather than the entire map. Ensure the provider uses equality checks so rebuilds only propagate when the specific flag value changes.

Contingency: If rebuild storms are measured via Flutter DevTools, refactor to a family provider keyed by flag key, so each widget subscribes only to its own flag's changes.