Build expiry scan and auto-transition logic
epic-peer-mentor-pause-management-automated-expiry-task-003 — Implement the core scan loop inside CertificationExpiryChecker. For each certification that has passed its expiry date, auto-transition the linked mentor's status to 'expired_cert' via the PeerMentorStatusRepository. Wrap each record update in a try/catch to ensure one failed transition does not abort the entire batch, and collect results for audit logging.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Model the scan loop as a pure function taking repository dependencies as constructor-injected interfaces — this makes unit testing straightforward without needing a live Supabase instance. For concurrency control, use a package like `pool` or implement a simple semaphore with `Completer` to cap parallel Supabase writes at 10. The deduplication step should be `certifications.map((c) => c.mentorId).toSet()` before iterating — process unique mentor IDs, not raw certification rows. Use a sealed class or `Either
Guard against timezone bugs: compare dates as UTC; `DateTime.now().toUtc()` not `DateTime.now()`. The `transitionStatus` call should write to the peer_mentor_status_history table atomically — use a Supabase RPC (Postgres function) wrapping the UPDATE + INSERT in a single transaction to ensure the history row always accompanies the status change.
Testing Requirements
Unit tests (flutter_test) with mocked repository dependencies: (1) 0 expired certifications → returns BatchTransitionResult with all zeros, (2) 5 expired certifications → all 5 transitioned, result shows transitioned=5, (3) 1 of 5 transitions throws exception → result shows transitioned=4, failed=1, failed_mentor_ids contains the failing ID, other 4 completed, (4) mentor already in 'expired_cert' status → skipped, no write call made, (5) same mentor appears twice in expired list (two certs) → transitioned only once. Integration test against local Supabase: seed 10 expired certs, run scan, assert 10 status rows updated in DB. Idempotency test: run scan twice in same test, assert second run makes 0 DB writes.
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.
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.
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.
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.