high priority low complexity backend pending backend specialist Tier 3

Acceptance Criteria

localStorageAdapterProvider is defined as a Provider<LocalStorageAdapter> and initializes SharedPreferences correctly
supabaseActivityClientProvider is defined as a Provider<SupabaseActivityClient> and reads from supabaseClientProvider
registrationPreferencesStoreProvider is defined as a Provider<RegistrationPreferencesStore> and reads from localStorageAdapterProvider
activityRepositoryProvider is defined as a Provider<ActivityRepository> and reads from supabaseActivityClientProvider
All providers can be overridden in ProviderScope for widget and unit tests without modifying source code
Each provider is placed in a providers.dart file co-located with the component it provides
The dependency graph compiles without circular references
localStorageAdapterProvider handles async SharedPreferences.getInstance() initialization — if required, uses a FutureProvider with appropriate error handling

Technical Requirements

frameworks
Flutter
Riverpod
data models
LocalStorageAdapter
SupabaseActivityClient
RegistrationPreferencesStore
ActivityRepository
performance requirements
Providers must be lazily initialized — no eager instantiation at app startup for components not yet needed
security requirements
supabaseClientProvider must be the single source of the Supabase client instance — no secondary Supabase.instance.client calls in provider bodies

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

LocalStorageAdapter requires SharedPreferences which has an async initializer. Two approaches: (a) require callers to await a top-level initialization before app start and expose a synchronous Provider, or (b) use FutureProvider and handle the loading state in consumers. Approach (a) is simpler and aligns with most Flutter app startup patterns — SharedPreferences.getInstance() is typically awaited in main() before runApp(). If the project already has an app initialization sequence, hook into it.

File layout suggestion: `lib/features/activity_registration/data/providers.dart` exporting all four providers. For override-friendliness, all providers should use `ref.read` not `ref.watch` inside Provider bodies (since these are non-reactive service providers, not state providers). Annotate with `@riverpod` code generation only if the project already uses riverpod_generator — otherwise plain `final xProvider = Provider(...)` is preferred for consistency.

Testing Requirements

Provider wiring is validated through the unit tests of higher-layer consumers (BLoC tests). For this task, write a single provider graph smoke test using ProviderContainer that reads activityRepositoryProvider with all dependencies overridden by mocks, asserting no ProviderException is thrown. This confirms the dependency graph is correctly wired without requiring real Supabase or SharedPreferences. Use flutter_test.

Component
Activity Repository
data medium
Dependencies (4)
Implement RegistrationPreferencesStore on top of LocalStorageAdapter. Expose getLastUsedActivityTypeId() → Future<String?> and saveLastUsedActivityTypeId(String id) → Future<void>. This store is the sole persistence point for the pre-fill default that enables the two-tap happy path. Must handle null gracefully for first-time users with no saved preference. epic-quick-activity-registration-data-infrastructure-task-004 Implement the SupabaseActivityClient wrapping the Supabase Flutter SDK. Provide typed methods: insertActivity(ActivityRecord) → Future<ActivityRecord>, fetchActivities({required String peerId}) → Future<List<ActivityRecord>>, and deleteActivity(String id). Use PostgREST query construction against the 'activities' table. All methods must be fully typed — no dynamic maps in public interfaces. Handle Supabase errors by throwing domain exceptions. epic-quick-activity-registration-data-infrastructure-task-003 Implement ActivityRepository as the central data access object. The core method is insertActivity(ActivityRecord draft) → Stream<ActivityRecord>: immediately emit a locally-constructed ActivityRecord with a temporary 'local_' UUID and SyncStatus.pending, then perform the Supabase insert asynchronously and emit the server-confirmed record (with real ID) on success, or emit with SyncStatus.failed on error. Also provide getActivities(String peerId) and deleteActivity(String id). This optimistic pattern is the architectural foundation consumed by all higher layers. epic-quick-activity-registration-data-infrastructure-task-005 Implement the LocalStorageAdapter class wrapping Flutter's SharedPreferences (or Hive for structured data). Expose typed read/write/delete/clear methods for key-value persistence. This adapter is the low-level persistence primitive that all preference and cache stores in this epic depend on. Must be injectable and mockable for testing. epic-quick-activity-registration-data-infrastructure-task-002
Epic Risks (3)
high impact medium prob technical

The optimistic insert pattern requires reconciling temporary local IDs with server-assigned IDs after the async Supabase write completes. If reconciliation logic is incorrect, the UI may display stale records, duplicate entries may appear, or subsequent operations (edit, delete) may target the wrong record ID, corrupting data integrity.

Mitigation & Contingency

Mitigation: Define a clear contract for temporary ID generation (e.g., UUID prefixed with 'local-') and implement a dedicated reconciliation method in ActivityRepository that atomically swaps the temporary ID. Write integration tests that simulate the full optimistic → confirm cycle.

Contingency: If reconciliation proves too complex, fall back to a simpler non-optimistic insert with a loading spinner for the network round-trip. The UX degrades slightly but correctness is preserved. Re-introduce optimistic behaviour once the pattern is stable.

high impact medium prob integration

Supabase row-level security policies on the activities table may not be configured to match the access patterns required by the client. If RLS blocks inserts or selects for the authenticated peer mentor session, all activity registration operations will silently fail or return empty results, which is difficult to diagnose in production.

Mitigation & Contingency

Mitigation: Define and test RLS policies in a dedicated Supabase migration script as part of this epic. Create integration tests that execute against a local Supabase instance with RLS enabled, covering insert, select by peer mentor ID, and denial of cross-mentor access.

Contingency: Maintain a fallback service-role client path (server-side only) that can be activated via a feature flag if client-side RLS is blocking legitimate operations while policies are corrected.

medium impact low prob technical

SharedPreferences on Flutter can become corrupted if the app crashes mid-write or if the device runs out of storage. A corrupted last-used activity type preference would cause the defaults manager to return null or an invalid ID, breaking the zero-interaction happy path.

Mitigation & Contingency

Mitigation: Wrap all LocalStorageAdapter reads in try/catch with typed safe defaults. Validate the retrieved activity type ID against the known list before returning it. Use atomic write operations where the platform supports them.

Contingency: If the preference store is corrupted, silently reset to the hardcoded default (first activity type alphabetically or 'general') and log a warning. The user loses their last-used preference but the app remains functional.