critical priority low complexity infrastructure pending backend specialist Tier 1

Acceptance Criteria

When the Supabase fetch throws a SocketException or TimeoutException, the initializer catches the error, logs a structured warning with the error type and orgId, and returns normally (does not rethrow)
When fallback is triggered, the in-memory flag state is populated from the persistent cache if a cached entry exists for the organization
When fallback is triggered and no cached entry exists, safe defaults (all flags disabled) are used and a warning is logged indicating cold-start offline mode
The initializer never throws from its `initialize()` method regardless of network or cache state
A public `InitializationSource` enum or equivalent communicates to callers whether the current state came from: network, cache, or defaults — without requiring callers to handle exceptions
Fallback behavior is covered by at least three distinct test cases: network error with warm cache, timeout with warm cache, network error with cold cache

Technical Requirements

frameworks
Flutter
Riverpod
Dart
apis
Supabase PostgreSQL 15 via FeatureFlagRepository
data models
activity_type
performance requirements
Cache read on fallback must complete in under 50ms
Fallback path must not trigger any additional network requests
security requirements
Fallback state must never blend flags from different organizations — cache keys must be org-scoped
Error logs must not include PII or session tokens — only orgId and error type

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Use a try/catch wrapping the repository fetch, catching `Exception` broadly but logging the runtime type for diagnostics. Avoid catching `Error` (stack overflow, assertion errors) — only catch recoverable `Exception` subclasses. For the persistent cache key, use `'feature_flags_${orgId}'` to ensure strict org isolation. Consider a short connection timeout (e.g., 5 seconds via Supabase client config) so the fallback triggers quickly on mobile networks rather than blocking for 30+ seconds.

The `InitializationSource` value should be stored as a field on the initializer and exposed as a getter so FeatureFlagProvider can surface it via a diagnostic stream for QA builds.

Testing Requirements

Unit tests using mocktail to simulate: (1) SocketException on fetch with pre-populated cache — assert flags loaded from cache, no throw; (2) TimeoutException on fetch with pre-populated cache — same assertions; (3) SocketException with empty cache — assert safe defaults applied, no throw; (4) Successful fetch after previous fallback — assert cache is updated with fresh data. Verify InitializationSource value in each scenario.

Component
Feature Flag Initializer
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.