high priority low complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

Passing an unknown or unregistered FeatureFlagKeys value causes FeatureGate to render fallback (or SizedBox.shrink), never the gated child
In Flutter debug mode, an `assert` or `FlutterError.reportError` warning is printed to console when an unregistered flag key is detected — no warning in release/profile mode
When FeatureFlagProvider is in a loading state (async initialization not yet complete), FeatureGate renders the fallback widget
When FeatureFlagProvider is in an error state, FeatureGate renders the fallback widget and does not throw
Widget rebuilds ONLY when its own specific flag key changes — confirmed by a rebuild counter test showing 1 rebuild per relevant change, 0 for irrelevant changes
Fallback widget parameter remains optional — existing call sites without fallback continue to compile and render SizedBox.shrink
All edge case behaviors documented with inline code comments explaining the security rationale (default to hidden)

Technical Requirements

frameworks
Flutter
Riverpod
data models
FeatureFlagKeys (enum)
AsyncValue (Riverpod)
performance requirements
Zero extra rebuilds per flag change — verified by test
Edge case guards add no measurable overhead to build() execution
security requirements
Default-to-hidden behavior is a security principle — unknown flags must NEVER show gated content
Debug warnings must only appear in kDebugMode — never expose flag key names in production logs
ui components
FeatureGate (existing widget, hardening)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Refactor the `ConsumerWidget.build()` to use `ref.watch(featureFlagProviderAsync)` (an `AsyncValue`-typed provider) and handle all three AsyncValue states with `.when(data:, loading:, error:)`. For unknown keys, call `isEnabled` which already returns false for unknown keys (from task-005) — but add a debug-mode check: `assert(() { if (!FeatureFlagKeys.values.contains(flag)) { debugPrint('[FeatureGate] WARNING: Unregistered flag key: $flag — defaulting to hidden'); } return true; }());`. The `assert(() { ... return true; }())` pattern is the idiomatic Flutter way to add debug-only side-effect checks.

For selective rebuilds, ensure the `.select` call on the provider is correctly scoped to one key — confirm this with a `debugPrint` rebuild counter during development, then remove before PR.

Testing Requirements

Widget tests using flutter_test. Test cases: (1) Unknown flag key → fallback/SizedBox.shrink rendered, no exception thrown; (2) Provider in loading AsyncValue.loading() → fallback rendered; (3) Provider in error AsyncValue.error() → fallback rendered, no exception; (4) Rebuild counter test: toggle unrelated flag, assert FeatureGate does not rebuild (use a build counter via StatefulBuilder or InheritedWidget trick); (5) Assert warning test: in debug mode, verify FlutterError or assertion fires for unregistered key (use FlutterError.onError override in test). All tests use ProviderScope with mock provider overrides.

Component
FeatureGate Widget
ui low
Epic Risks (3)
high impact low prob security

The feature flag admin screen allows persisting changes to organization_configs. If the role guard is implemented only client-side (checking role state in Riverpod), a user who manipulates their local role state could toggle flags for their organization without proper server-side authorization, potentially exposing features prematurely.

Mitigation & Contingency

Mitigation: Implement server-side authorization for flag update operations using Supabase RLS UPDATE policies that check the user's role in the memberships table. The client-side guard is UX only; the database enforces the actual restriction.

Contingency: If an unauthorized update is detected, audit the RLS policies and add a Supabase Edge Function as an authorization middleware for flag toggle operations, rejecting requests from non-admin role JWTs.

medium impact medium prob scope

Developers on other feature teams may use FeatureGate incorrectly — for example, wrapping business logic rather than UI, or using it before flag initialization completes — leading to features that are visible but non-functional or cause runtime errors when flags are queried in a loading state.

Mitigation & Contingency

Mitigation: Add assert statements in FeatureGate's build method that throw in debug mode if the provider is still in a loading state. Write developer documentation with a clear usage contract: FeatureGate is UI-only; logic gating must use the provider's isEnabled method directly. Include lint examples in the codebase.

Contingency: If misuse is found in code reviews, add a custom Dart lint rule via custom_lint that flags FeatureGate usage outside of the widget tree, and conduct a codebase audit to find existing violations.

medium impact low prob technical

If the audit log is stored in the same organization_configs table without pagination or archival strategy, high-frequency flag changes during pilot testing could produce an unbounded number of rows, degrading query performance on the admin screen.

Mitigation & Contingency

Mitigation: Store audit log entries in a separate feature_flag_audit_log table with an index on (organization_id, changed_at DESC). Implement cursor-based pagination in the repository and limit the initial load to 50 entries.

Contingency: If table size becomes a performance concern, add a Supabase scheduled function to archive entries older than 90 days to cold storage, and add a database index on changed_at for range queries.