critical priority low complexity infrastructure pending infrastructure specialist Tier 0

Acceptance Criteria

A top-level `supabaseClientProvider` using `Provider<SupabaseClient>` (or `riverpod` equivalent) is defined in a dedicated file (e.g., `lib/core/providers/supabase_client_provider.dart`)
The provider reads Supabase URL and anon key exclusively from environment/build-time configuration (e.g., `--dart-define` or `flutter_dotenv`), never from hardcoded string literals
All downstream repository providers that require a `SupabaseClient` receive it by reading `supabaseClientProvider` via `ref.watch` or `ref.read`, not by calling `Supabase.instance.client` directly
Running the app with valid credentials results in exactly one `SupabaseClient` instance being created across the entire dependency graph (verifiable via identity check in tests)
Missing or malformed environment variables cause the provider to throw a descriptive `ConfigurationException` at startup rather than a null-pointer crash
The provider is registered at the root `ProviderScope` so it is available to all widget subtrees without re-instantiation
A unit test confirms that two separate `ref.read(supabaseClientProvider)` calls return the same object identity

Technical Requirements

frameworks
Flutter
Riverpod
apis
Supabase Flutter SDK (`supabase_flutter`)
performance requirements
Single connection pool — no duplicate `SupabaseClient` instances across the app lifecycle
Provider must resolve synchronously (no async initialization at read time); Supabase.initialize() must complete before ProviderScope is mounted
security requirements
Supabase URL and anon key must never be committed to source control; use `--dart-define` or an `.env` file excluded via `.gitignore`
Anon key grants row-level-security (RLS) scoped access only; service-role key must never appear in client code
No logging of credentials, even in debug mode

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Initialize Supabase in `main.dart` with `await Supabase.initialize(url: ..., anonKey: ...)` before `runApp`. The provider body can then safely return `Supabase.instance.client` synchronously. Prefer `Provider` (not `FutureProvider`) to keep downstream providers simple. Place the provider in `lib/core/providers/` to establish a clear infrastructure layer separate from feature providers.

If the project uses `flutter_riverpod` with code generation (`@riverpod`), annotate with `@Riverpod(keepAlive: true)` to prevent disposal. Avoid putting business logic inside this provider — its sole responsibility is client exposure.

Testing Requirements

Write unit tests using `riverpod_test` / `ProviderContainer`. Test 1: provider returns a non-null `SupabaseClient` when valid env vars are present. Test 2: two sequential `read` calls return identical object instances (singleton). Test 3: provider throws `ConfigurationException` (or equivalent) when env vars are absent or empty.

No integration tests required at this layer; mock `SupabaseClient` for all downstream tests that depend on this provider.

Component
Supabase Client Provider
infrastructure low
Epic Risks (2)
high impact medium prob integration

The design token theme extension may conflict with existing ThemeData extensions already registered in the app, causing runtime assertion errors or token resolution failures across all screens that consume the tokens.

Mitigation & Contingency

Mitigation: Audit all existing ThemeData extensions before implementation. Use a unique extension key namespace and add integration tests that instantiate the combined theme in a test app harness.

Contingency: If conflicts arise, isolate design tokens behind a dedicated provider singleton (Riverpod) rather than a ThemeData extension, updating all consuming widgets to read from the provider instead.

medium impact medium prob scope

The 30-day warning threshold for expiring_soon status may differ between HLF's stated requirement in workshops (60 days mentioned in user stories) and the 30-day value in component documentation, causing disagreement during acceptance testing.

Mitigation & Contingency

Mitigation: Explicitly confirm the threshold value with HLF stakeholder before implementation. Make the threshold a named constant (kCertificationWarnDays) so it can be updated without logic changes.

Contingency: If stakeholder confirms 60 days post-implementation, update the constant and re-run the unit test suite — no architectural change required.