high priority medium complexity integration pending integration specialist Tier 5

Acceptance Criteria

After the auto-pause loop completes, the job queries the assignment table to resolve the responsible coordinator(s) for each paused mentor
Paused mentors are grouped by coordinator_id such that each coordinator receives exactly one notification even if they are responsible for multiple paused mentors
Each notification payload includes: coordinator display name, list of paused mentor names, their certification expiry dates, and a deep-link URL to the coordinator pause roster screen
Notification dispatch is performed via CoordinatorNotificationService — no direct FCM or Supabase Realtime calls from within the job itself
If a paused mentor has no assigned coordinator, the notification is sent to the organisation admin instead, and this fallback is recorded in the job log
A notification failure for one coordinator does not abort dispatch to other coordinators — errors are logged and counted
If zero mentors were paused in this job run, no notifications are dispatched
The job log includes a notifications_dispatched_count and notifications_failed_count field in its final structured summary
Notification content is in Norwegian (Bokmål) as the primary locale, consistent with the rest of the app
Notification deep-link URL uses the correct route for the coordinator pause roster screen and does not contain PII in the URL path

Technical Requirements

frameworks
Supabase Edge Functions (Deno)
Supabase PostgreSQL 15
apis
CoordinatorNotificationService internal API
Firebase Cloud Messaging (FCM) API v1
Supabase Edge Functions (Deno) REST API
data models
assignment
certification
contact
performance requirements
Coordinator grouping must be done in-memory after a single database query — do not issue one query per mentor
All coordinator notifications must be dispatched within 10 seconds of the pause phase completing
Notification dispatch calls may be parallelised with Promise.allSettled since failures are non-blocking
security requirements
Notification payloads must not include national identity numbers, personnummer, or raw health data
FCM server key and service account credentials must only be accessed from the Edge Function environment — never from the mobile client
The deep-link URL must be validated to match the expected app scheme before being included in the payload to prevent open redirect
Coordinator identity resolution must respect RLS — service-role client is required for cross-organisation queries within the job

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

After collecting the paused mentor IDs from task-010, perform a single JOIN query: SELECT assignments.coordinator_id, peer_mentors.full_name, certifications.expiry_date FROM assignments JOIN peer_mentors ON ... JOIN certifications ON ... WHERE peer_mentors.id = ANY($paused_ids). Group the result set in a Map using a simple reduce.

Then iterate the map and call CoordinatorNotificationService.sendBatchPauseNotification(coordinatorId, mentorSummaries). Use Promise.allSettled (not Promise.all) for parallel dispatch so a single FCM error does not suppress all other notifications. The deep-link URL format should follow the existing app routing convention (e.g. app://coordinator/pause-roster) — confirm with the frontend team before hardcoding.

Testing Requirements

Unit tests: mock CoordinatorNotificationService and the Supabase client. Test (1) grouping logic: three mentors with two coordinators produces two notification calls, (2) a mentor with no coordinator routes to org admin, (3) a failed notification for coordinator A does not prevent dispatch to coordinator B, (4) zero paused mentors results in zero notification calls. Test the notification payload structure — assert it contains mentor_names array, expiry_dates array, and a valid deep_link field. Test the final job log includes correct notifications_dispatched_count.

Use snapshot testing for payload shape to catch regressions.

Component
Certification Expiry Auto-Pause Job
infrastructure medium
Epic Risks (3)
medium impact low prob technical

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.

high impact medium prob technical

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.

medium impact medium prob dependency

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.