critical priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

ReminderConfigRepository is a Dart class following the repository pattern with a clean interface
fetchConfig(String orgId) returns a ReminderConfig value object with reminderDays and escalationDays fields, or throws ReminderConfigNotFoundException if no row exists for the org
updateReminderDays(String orgId, int days) updates the reminder_days column and returns the updated ReminderConfig; throws ReminderConfigValidationException if days <= 0
updateEscalationDays(String orgId, int days) updates the escalation_days column and returns the updated ReminderConfig; throws ReminderConfigValidationException if days <= reminderDays (sourced from current config)
All Supabase PostgrestException errors are caught and mapped to typed domain exceptions (ReminderConfigNotFoundException, ReminderConfigValidationException, ReminderConfigNetworkException)
Repository is provided via Riverpod using a Provider<ReminderConfigRepository> or AutoDisposeProvider
fetchConfig applies .select('reminder_days, escalation_days').eq('org_id', orgId).single() — no over-fetching of unrelated org_settings columns
ReminderConfig is an immutable value object (using Dart's const constructor or freezed-style copyWith)
No hardcoded org IDs or fallback magic numbers — all defaults come from the database column defaults (migration task-003)
Unit tests pass with a mocked Supabase client

Technical Requirements

frameworks
Flutter
Riverpod
Supabase Flutter SDK
apis
Supabase PostgREST (org_settings table)
data models
org_settings
ReminderConfig (domain model)
performance requirements
fetchConfig uses .single() — single row fetch by PK, O(1) lookup
No caching layer required at repository level — BLoC/Riverpod state layer handles caching
Avoid fetching full org_settings row — select only reminder_days and escalation_days columns
security requirements
Repository uses the authenticated Supabase client — never the service role key from Flutter
org_id parameter must match the authenticated user's org from JWT — do not allow arbitrary org_id to be passed from untrusted UI input without validation
updateReminderDays and updateEscalationDays must only be called from admin-gated UI paths (enforce at BLoC/UI layer, not repository layer)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Define a ReminderConfig immutable data class: `class ReminderConfig { final int reminderDays; final int escalationDays; const ReminderConfig({required this.reminderDays, required this.escalationDays}); }`. Define domain exception hierarchy: `abstract class ReminderConfigException implements Exception {}` with subclasses for NotFound, Validation, and Network errors. In updateEscalationDays, fetch current config first to validate client-side before the DB call — this prevents a round-trip error UX but does not replace the DB CHECK constraint. Use Supabase's `.single()` for fetchConfig which throws a PostgrestException if no row is found (code 'PGRST116') — map this to ReminderConfigNotFoundException.

For Riverpod, expose as `final reminderConfigRepositoryProvider = Provider((ref) => ReminderConfigRepository(supabase: ref.watch(supabaseClientProvider)));`. Avoid AsyncNotifier at repository level — keep repositories as plain synchronous Dart classes with async methods; let Riverpod AsyncNotifier/BLoC handle state.

Testing Requirements

Unit tests using flutter_test and a mock SupabaseClient (use mockito or manual stub): (1) fetchConfig returns ReminderConfig with correct values when Supabase returns a valid row; (2) fetchConfig throws ReminderConfigNotFoundException when Supabase returns empty result; (3) fetchConfig throws ReminderConfigNetworkException on PostgrestException with network error code; (4) updateReminderDays returns updated ReminderConfig on success; (5) updateReminderDays throws ReminderConfigValidationException when days <= 0 (before Supabase call is made); (6) updateEscalationDays throws ReminderConfigValidationException when new escalation_days <= current reminder_days; (7) updateEscalationDays propagates database CHECK constraint violation as ReminderConfigValidationException; (8) Riverpod provider resolves correctly in ProviderContainer test. Target: 100% method coverage, all error paths covered.

Epic Risks (3)
high impact medium prob integration

Adding last_contact_date to the assignments table may conflict with existing RLS policies or trigger-based logic that monitors the assignments table. If the migration is not carefully reviewed, existing assignment management features could break in production.

Mitigation & Contingency

Mitigation: Review all existing triggers, policies, and foreign key constraints on the assignments table before writing the migration. Run the migration against a staging Supabase instance with production-like data and execute the full existing test suite before merging.

Contingency: Roll back the migration using Supabase's versioned migration history. Apply the schema change as an additive-only migration (nullable column with default) to ensure zero downtime and reversibility.

medium impact medium prob dependency

The PushNotificationService wraps an existing FCM integration whose internal API contract may have changed or may not expose the payload formatting required for deep-link CTAs. Misalignment discovered late delays the dispatch service epic.

Mitigation & Contingency

Mitigation: Before implementing the wrapper, read the existing push notification integration code and confirm the method signatures, payload structure, and token management model. Agree on a stable interface contract in a shared Dart abstract class.

Contingency: If the existing service is incompatible, implement a thin adapter layer that translates reminder payloads to the existing service's format, isolating the reminder feature from upstream changes.

high impact low prob security

Incorrect RLS policies on notification_log could allow coordinators to read reminder records belonging to peer mentors in other chapters, exposing sensitive assignment information across organisational boundaries.

Mitigation & Contingency

Mitigation: Write explicit RLS policies with integration tests that assert cross-chapter queries return zero rows. Use Supabase's built-in auth.uid() and join through the org membership tables to scope all queries.

Contingency: If a policy gap is discovered post-merge, immediately disable the affected table's SELECT policy, deploy a corrected policy, and audit recent queries in Supabase logs for any cross-boundary reads.