critical priority low complexity infrastructure pending backend specialist Tier 0

Acceptance Criteria

FeatureFlagInitializer exposes a single async `initialize(organizationId: String)` method that completes without throwing under normal network conditions
On successful fetch, all flags for the organization are written to the persistent cache before the method returns
The initializer reads organizationId from the authenticated session context — it does not accept unchecked caller-supplied IDs
If the repository fetch succeeds but returns zero flags, the cache is still updated (empty map) and the method completes without error
Initialization completes within 3 seconds on a 3G connection (200ms RTT, 1Mbps download)
The service is injectable via Riverpod and does not hold any global singletons
Initializer can be called multiple times (e.g., on org switch) without memory leaks or duplicate cache entries

Technical Requirements

frameworks
Flutter
Riverpod
Dart
apis
Supabase PostgreSQL 15 via FeatureFlagRepository
data models
activity_type
performance requirements
Total initialization time under 3 seconds on simulated 3G network
Single Supabase query per initialization call — no N+1 fetches
security requirements
OrganizationId must be sourced from the verified JWT claims, not from UI state or route parameters
No feature flag data from other organizations may be written to or read from the local cache
Supabase RLS enforced at query level — application layer must not attempt to filter by org after the fact

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Design FeatureFlagInitializer as a plain Dart class with a constructor that accepts FeatureFlagRepository and a Cache abstraction — avoid making it a Riverpod provider itself; let the provider wrap it. This keeps the core logic testable without Riverpod test setup overhead. Use a single `await repository.fetchAllForOrganization(orgId)` call to load all flags at once rather than fetching per-flag-key, which prevents N+1 latency on startup. Expose an `isInitialized` boolean getter so the caller can check state without re-triggering initialization.

Log a structured info message on successful initialization including orgId (not personal data) and flag count for observability.

Testing Requirements

Unit tests for the happy path (flags fetched and cached), the empty-flags path (zero flags returned), and the re-initialization path (calling initialize twice for the same org produces a single cache entry, not duplicates). Integration test verifying the initializer's Supabase query includes the organization_id filter. All tests use mocked FeatureFlagRepository — no real Supabase connection in unit tests.

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.