high priority medium complexity frontend pending frontend specialist Tier 5

Acceptance Criteria

When an admin toggles a feature flag in the admin screen, all widgets watching FeatureFlagProvider rebuild within 500ms without an app restart
FeatureFlagProvider subscribes to a repository stream or change signal that fires when flag state changes in Supabase
On receiving a change signal, the provider atomically updates its resolved in-memory map and calls Riverpod's state invalidation/notification
Widgets watching only a single flag key do not rebuild when unrelated flags change (selective rebuild via provider.select or equivalent)
Organization context switch (switching active org) triggers full re-initialization of the flag map, clearing the previous org's flags
If the change signal arrives while the provider is mid-initialization, it is queued and applied after initialization completes — no lost updates
Provider correctly handles rapid consecutive toggles (debounce or last-write-wins strategy applied)
Error during reactive update (network failure) does not crash the provider — stale cached values are retained and a stale-cache indicator is signaled

Technical Requirements

frameworks
Flutter
Riverpod
apis
Supabase Realtime (postgres_changes subscription) or Supabase REST polling
data models
FeatureFlag
FeatureFlagKeys (enum)
ResolvedFlagState
performance requirements
Reactive update propagated to UI within 500ms of Supabase change event
Only widgets watching changed flags rebuild — not all widgets
Debounce rapid consecutive toggles with a 200ms window to prevent widget rebuild storms
security requirements
Supabase Realtime subscription must use the authenticated session token — no anonymous listening on flag changes
Row Level Security on feature_flags table must ensure only org-scoped flags are streamed to each client

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Implement using Riverpod's `StateNotifier` or `Notifier` (Riverpod 2.x). The provider watches a `StreamProvider` backed by the Supabase Realtime channel for the `feature_flags` table scoped to the current organization. On stream event, parse the changed flag, recompute only the affected entry in the resolved map (immutable copy with updated entry using `Map.from(...)..update(...)`), and set state to the new map. For selective rebuilds, expose individual flag providers via `provider.select((map) => map[flagKey])` so each FeatureGate widget only re-renders when its specific flag changes.

For org switching, listen to an `activeOrganizationProvider` and use `ref.watch` to auto-dispose and re-create the flag stream when the org changes. Add a `StaleCacheNotifier` bool to signal UI when a reactive update failed.

Testing Requirements

Unit and integration tests using flutter_test. Test cases: (1) Mock repository emits a change event → verify provider rebuilds and isEnabled returns updated value; (2) Mock org switch event → verify map is cleared and re-initialized with new org's flags; (3) Simulate rapid 5 consecutive toggles within 100ms → verify only 1 or 2 rebuilds occur (debounce working); (4) Mock network failure during reactive update → verify stale values retained and no exception thrown; (5) Widget test: wrap a FeatureGate widget in a ProviderScope, simulate flag toggle via mock repository, assert widget content changes without app restart. Use flutter_test WidgetTester for widget-level tests.

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.