critical priority medium complexity database pending database specialist Tier 0

Acceptance Criteria

A Supabase migration file exists that adds a B-tree index on the certification expiry date column
CertificationStatusRepository exposes a method `fetchExpiringSoon({required int withinDays})` returning all certifications whose expiry date falls within today + withinDays (inclusive)
A separate method `fetchExpiredToday()` returns all certifications whose expiry date equals today's UTC date
A method `fetchMentorStatusByIds(List<String> mentorIds)` returns the current PeerMentorStatus for a given set of mentor IDs
All three query methods are implemented as Supabase PostgREST calls (or Supabase Edge Function if PostgREST filter is insufficient) with correct column filtering to avoid over-fetching
EXPLAIN ANALYZE output on a seeded dataset of ≥1,000 certifications confirms index is used for expiry-date range queries (seq scan not observed for date-bounded queries)
Queries apply RLS-compatible service-role key pattern for nightly batch context (not user JWT)
Repository methods return typed Dart model objects, not raw Map<String, dynamic>
Unit tests cover: certifications expiring in 1 day, 7 days, 14 days, 30 days, 31 days (excluded), already expired (excluded from expiring-soon, included in expired-today), empty result set

Technical Requirements

frameworks
Dart
Flutter
Supabase Dart client (supabase_flutter)
Riverpod (repository provider)
apis
Supabase PostgREST REST API
Supabase service-role key authentication for batch queries
data models
certification (id, mentor_id, issued_at, expires_at, certification_type, status)
peer_mentor_status (mentor_id, current_status, updated_at)
CertificationExpiryRecord (Dart model)
MentorStatusRecord (Dart model)
performance requirements
Nightly query for 30-day expiry window must complete in under 2 seconds for up to 10,000 certification rows
Index on expires_at column must be present before deploying to production
Avoid N+1 queries: fetch all mentor statuses in a single IN-clause query, not per-mentor
security requirements
Batch queries must use Supabase service-role key stored in Supabase Vault or environment secret — never hardcoded
RLS policies must not block the service-role key from reading certifications across all orgs
Returned certification data must not be logged at INFO level — only counts and IDs at DEBUG level

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Use Supabase's `.lte('expires_at', futureDate).gte('expires_at', today)` filter chain for the range query. Represent dates as ISO-8601 UTC strings when passing to PostgREST to avoid timezone edge cases — always normalise to UTC midnight. The index migration should be a separate numbered migration file (e.g. `20260001_idx_certification_expires_at.sql`) so it can be reviewed independently.

For the Dart model, use `DateTime.parse()` with UTC enforcement. Consider a sealed class `CertificationExpiryWindow { case expiredToday; case expiringSoon(int daysLeft); }` to make the caller's intent explicit. The service-role client instance should be a singleton injected via Riverpod `Provider`, separate from the user-scoped Supabase client used in the rest of the app.

Testing Requirements

Unit tests (flutter_test) for all three repository methods using a mocked Supabase client. Test matrix: (1) happy path returns correctly typed Dart objects, (2) empty result returns empty list not null, (3) date boundary conditions (expiry = today, expiry = today+30, expiry = today+31), (4) Supabase network error throws typed RepositoryException. Integration test against a local Supabase instance (via supabase CLI) seeded with known fixture data to confirm index usage and correct row counts. No UI tests required.

Component
Certification Expiry Checker Service
service high
Epic Risks (4)
high impact medium prob technical

The nightly expiry checker may run multiple times due to scheduler retries or infrastructure issues, causing duplicate auto-transitions and duplicate coordinator notifications that erode trust in the notification system.

Mitigation & Contingency

Mitigation: Implement idempotency via a unique constraint on (mentor_id, threshold_day, certification_expiry_date) in the cert_expiry_reminders table. Auto-transitions should be wrapped in a Postgres RPC that checks current status before applying, making repeated invocations safe.

Contingency: Add a compensation query in the reconciliation log that detects duplicate log entries for the same certification period and alerts the operations team for manual review within 24 hours.

high impact medium prob integration

The HLF Dynamics portal API may have eventual-consistency behaviour or rate limits that cause website listing updates to lag behind status changes, leaving expired mentors visible on the public website for an unacceptable window.

Mitigation & Contingency

Mitigation: Design the sync service to be triggered immediately on status transitions (event-driven via database webhook) in addition to the nightly batch run. Implement a reconciliation job that verifies sync state against app state and re-triggers any divergent records.

Contingency: If real-time sync cannot be guaranteed, implement a manual 'force sync' action in the coordinator dashboard so coordinators can trigger an immediate re-sync for urgent cases. Document the expected sync lag in coordinator onboarding materials.

medium impact medium prob scope

Stakeholder requests to extend the expiry checker to handle additional certification types, grace periods, or organisation-specific threshold configurations may significantly increase scope beyond what is designed here, delaying delivery.

Mitigation & Contingency

Mitigation: Parameterise threshold day values (30, 14, 7) via configuration repository rather than hard-coding them, enabling per-organisation customisation without code changes. Document that grace period logic and additional cert types are out of scope for this epic and require a dedicated follow-up.

Contingency: Deliver the feature with hard-coded HLF-standard thresholds first and introduce the configuration repository as a follow-up task in the next sprint, using a feature flag to enable per-org threshold overrides.

high impact low prob security

Dynamics portal API credentials stored as environment secrets in Supabase Edge Function configuration may be rotated or invalidated by HLF IT without notice, causing silent sync failures that go undetected for multiple days.

Mitigation & Contingency

Mitigation: Implement credential health-check calls on each scheduler run and emit an immediate alert on auth failure rather than only alerting after N consecutive failures. Document the credential rotation procedure with HLF IT and establish a rotation notification protocol.

Contingency: Maintain a break-glass manual sync script accessible to HLF administrators that can re-execute the Dynamics sync with newly provided credentials while the automated system is restored.