Define typed feature flag key constants file
epic-organization-feature-flags-foundation-task-003 — Create the canonical Dart constants file that enumerates every feature flag key as a typed string constant (e.g., kFlagTravelReimbursement, kFlagCourseCertification, kFlagEncryptedAssignmentDispatch, kFlagGamification). This file is the single source of truth for all flag identifiers used across the app, preventing string typos and enabling IDE autocomplete.
Acceptance Criteria
Technical Requirements
Implementation Notes
Use an `abstract final class FeatureFlagKeys` (Dart 3 style) with `static const String` members — this prevents instantiation while grouping constants logically. Group constants by feature domain using block comments (e.g., `// --- Expense & Travel ---`, `// --- Gamification ---`, `// --- Accessibility ---`). The `kAllFeatureFlagKeys` list should be derived from the class fields using a hand-maintained list rather than reflection, since Flutter tree-shaking can affect mirror-based approaches. Ensure the snake_case values align exactly with the column names planned in the database migration (task-001) — coordinate with the database_specialist to confirm naming before finalising.
This file will be imported by the FeatureFlagRepository (task-004), FeatureFlagCache (task-005), and RolloutEvaluator (task-006), so stable naming is critical from the start.
Testing Requirements
Unit tests are lightweight for this task but should confirm: (1) `kAllFeatureFlagKeys` contains no duplicate values; (2) every value in `kAllFeatureFlagKeys` matches the `^[a-z][a-z0-9_]*$` regex pattern (enforcing snake_case database column compatibility); (3) the list length equals the expected number of defined flags (hard-coded assertion catches accidental omissions). A CI grep step should assert zero occurrences of hard-coded flag string literals outside of the constants file. Use `flutter_test` for the unit tests.
Supabase RLS policies for organization_configs may have gaps that allow cross-organization reads if the JWT claim for organization_id is absent or malformed, leading to data leakage between tenants.
Mitigation & Contingency
Mitigation: Implement RLS policies using auth.uid() joined against a memberships table to derive organization_id rather than trusting a client-supplied claim. Write integration tests that simulate a cross-org read attempt and assert it returns zero rows.
Contingency: If a gap is discovered post-launch, immediately disable the affected RLS policy, roll back the migration, and re-implement with a parameterized policy tested against all organization fixture data.
Dart does not have a built-in semantic version comparison library; a naive string comparison (e.g., '2.10.0' < '2.9.0' lexicographically) would cause rollout evaluator to produce incorrect eligibility results for organizations on different app versions.
Mitigation & Contingency
Mitigation: Use the pub.dev `pub_semver` package or implement a proper three-segment integer comparison. Add parameterized unit tests covering 20+ version pairs including double-digit minor/patch segments.
Contingency: If incorrect comparison is discovered in production, push a hotfix with corrected comparison logic and temporarily disable phase-gated flags until all affected organizations have updated to the corrected version.
Persistent local cache written to shared_preferences or Hive could become corrupted or deserialized incorrectly after an app update changes the FeatureFlag schema, causing startup crashes or all flags defaulting to disabled.
Mitigation & Contingency
Mitigation: Wrap all cache reads in try/catch with explicit fallback to the all-disabled default map. Version the cache key (e.g., `feature_flags_v2_{orgId}`) so schema changes automatically invalidate old entries.
Contingency: If cache corruption is detected in a release, publish an app update that clears the versioned cache key on first launch and re-fetches from Supabase.