high priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

First tap on a flag toggle changes the UI to a 'staged' visual state (e.g., toggle appears pending/highlighted) without persisting the change
Second tap on the staged toggle opens an AlertDialog or ModalBottomSheet showing the flag's human-readable impact description (minimum 1 sentence, non-technical language)
The confirmation dialog displays the flag name, current state, proposed new state, and impact description
Tapping 'Confirm' in the dialog dispatches the toggle action via FeatureFlagProvider and persists via FeatureFlagRepository
Tapping 'Cancel' or dismissing the dialog reverts the toggle to its original (pre-staged) state with no persistence call made
An optimistic UI update reflects the confirmed new state immediately; a failure rolls back and shows an error snackbar
While the persist call is in-flight, the toggle and confirm button are disabled to prevent double-submission
Each feature flag record includes a non-empty impactDescription string; the dialog must not render blank descriptions
The staged state is local to the widget and does not leak between list items or screen navigations
All interactive elements meet WCAG 2.2 AA contrast ratios and have descriptive semantic labels for screen readers

Technical Requirements

frameworks
Flutter
Riverpod
BLoC
apis
FeatureFlagRepository.toggleFlag(flagId, newState)
FeatureFlagProvider (Riverpod notifier)
data models
FeatureFlag (id, name, isEnabled, impactDescription, rolloutConditions)
performance requirements
Dialog open animation must complete within 200ms
Persist call must not block the UI thread; use async/await with loading indicator
Staged state transitions must be synchronous and immediate (no perceptible lag)
security requirements
Toggle actions must be gated behind admin role check before dispatching to provider
Repository call must include authenticated Supabase session token
No feature flag impact descriptions may contain PII
ui components
StagedToggleWidget (custom Switch wrapper with pending state)
FlagImpactConfirmationDialog (AlertDialog subclass)
LoadingOverlay or CircularProgressIndicator within dialog confirm button

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Model the toggle state as a local StateProvider (idle | staged | persisting | error) rather than embedding it in global BLoC state—this keeps the two-step flow encapsulated per list item. The impactDescription field should be loaded alongside the flag list from the repository so the dialog opens instantly without an additional network call. Use showDialog and await its result to drive the state machine cleanly. Ensure the Riverpod notifier invalidates the flag list after a successful toggle so other parts of the screen (audit log, rollout panel) reflect the new state.

Guard against the widget being unmounted while the persist call is in-flight by checking mounted before calling setState or ref.read.

Testing Requirements

Write Flutter widget tests using flutter_test and ProviderScope overrides. Test cases: (1) first tap sets staged state without calling repository; (2) second tap opens confirmation dialog; (3) confirm dispatches toggle and closes dialog; (4) cancel reverts staged state and closes dialog; (5) repository failure rolls back toggle and shows error snackbar; (6) double-tap on confirm button does not issue two repository calls; (7) dialog impact description is visible and non-empty. Achieve 100% branch coverage on the staged-state machine. Use mockito or mocktail to mock FeatureFlagRepository.

Component
Feature Flag Admin Screen
ui medium
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.