Implement auto-pause logic and audit writing in CertificationExpiryJob
epic-peer-mentor-pause-core-logic-task-010 — Implement the core processing loop in CertificationExpiryJob that iterates over expired-certification mentors and for each: calls the update_mentor_status RPC to transition status to 'paused' with reason 'certification_expired', writes an audit history record including the certification_expiry_date and job run ID, and collects results for the notification dispatch phase. Handle individual mentor failures with per-record error logging so a single failure does not abort the entire batch.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
Implement the loop as an async generator or plain for-await loop over the mentor list retrieved in the previous task (task-009). Use a try/catch block per iteration and accumulate errors in an errors array. After the loop, perform a single batched upsert of audit records for all successfully paused mentors to minimise database round-trips. Use a unique job_run_id (crypto.randomUUID()) generated once at job startup and passed into every audit record — this enables log correlation across the audit table and the job output.
The RPC update_mentor_status should be the single point of truth for the transition; do not directly UPDATE the peer_mentors table from the job. Guard against clock skew by using the database server's NOW() for changed_at in the audit record rather than the Edge Function's Date.now().
Testing Requirements
Unit tests (Deno test runner): mock Supabase client to verify (1) the loop correctly skips non-active mentors, (2) a failed RPC increments error_count without throwing, (3) the result list only contains successfully paused mentor IDs, (4) the audit payload includes all required fields. Test idempotency by simulating a second run where all mentors are already 'paused' and asserting paused_count === 0. Test the structured log output shape against the expected JSON schema. All mocks must replace the Supabase client at the module boundary so real network calls are never made.
The status state machine must handle race conditions where two concurrent callers (e.g., a mentor self-pausing and a coordinator force-pausing simultaneously) attempt to update the same mentor's status. Without a concurrency guard, both writes could succeed, leaving the audit log in an inconsistent state.
Mitigation & Contingency
Mitigation: Use a Supabase RPC with a row-level lock (SELECT FOR UPDATE) inside a transaction so only one transition wins. Return a clear error to the losing caller. Test with concurrent requests in the integration test suite.
Contingency: If row-level locking proves unreliable in the Supabase environment, add an optimistic-locking version field to peer_mentors and have the service retry up to three times on version conflict before surfacing an error to the caller.
If the CertificationExpiryJob Edge Function fails silently (network timeout, Supabase cold start), HLF mentors with expired certifications could remain in active status and continue appearing on the chapter website, creating a compliance breach.
Mitigation & Contingency
Mitigation: Implement structured error logging inside the Edge Function, write a monitoring query that checks for mentors with expired certifications still in active status, and set up an alert if any are detected 30 minutes after the scheduled nightly run.
Contingency: Provide a coordinator-accessible manual trigger for the expiry check that can be invoked via the admin interface if the scheduled job is known to have failed. Document the manual recovery procedure for HLF coordinators.
pg_cron registration in Supabase requires superuser-level access that may not be available in all environments (local dev, staging, CI). If the cron job cannot be registered automatically, the Edge Function will never execute on schedule, breaking the HLF certification expiry workflow.
Mitigation & Contingency
Mitigation: Use Supabase's recommended pg_cron setup via the SQL editor migration script and document the exact commands. Validate cron registration in the staging environment as part of the epic's deployment checklist.
Contingency: If pg_cron is unavailable, switch to a Supabase scheduled Edge Function invocation via an external cron service (e.g., a GitHub Actions scheduled workflow calling the Edge Function endpoint with a service-role key) until the pg_cron approach is resolved.