Build feature flag admin screen scaffold
epic-organization-feature-flags-ui-task-003 — Create the Feature Flag Admin Screen scaffold with role-based route guard restricting access to organization admins only. Implement the full-page layout with a header, flag list section, audit log section, and stale-cache indicator banner. Wire up FeatureFlagProvider to load and display all flags with their current enabled/disabled state and rollout metadata.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Create the screen at `lib/features/feature_flags/screens/feature_flag_admin_screen.dart`. Implement the GoRouter redirect as: `redirect: (context, state) { final role = ref.read(currentUserRoleProvider); if (role != UserRole.orgAdmin) return '/no-access'; return null; }`. Structure the screen as a `ConsumerWidget` with a `CustomScrollView` containing: (1) `SliverAppBar` or `PageHeader`, (2) stale cache banner as a `SliverToBoxAdapter`, (3) flag list as a `SliverList` with `ListView.builder` semantics, (4) audit log section header + `SliverList`. The flag list reads from `ref.watch(allFeatureFlagsProvider)` (a provider exposing the full list of flags with metadata).
The stale cache banner reads from `ref.watch(featureFlagStaleCacheProvider)`. Audit log is a separate `FutureProvider` fetching from Supabase — does not block the main render. All toggle interactions on this screen dispatch to an admin-specific write provider (defined in a later task), not directly to FeatureFlagProvider. This screen is scaffold only — toggle write actions can be no-ops or stubbed for this task.
Testing Requirements
Widget and integration tests using flutter_test. Test cases: (1) Non-admin role navigates to route → redirected, screen not rendered; (2) Admin role navigates to route → screen renders with flag list; (3) Stale cache signal true → banner visible with Refresh button; (4) Stale cache signal false → banner not visible; (5) All flags displayed with correct enabled/disabled state matching mock provider; (6) Audit log section shows loading skeleton then list after async resolve; (7) Audit log empty state renders when no events; (8) Accessibility test: verify toggle switches have Semantics labels using flutter_test `tester.getSemantics`. Use ProviderScope with mock overrides for FeatureFlagProvider and audit log provider.
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.
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.
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.