critical priority medium complexity infrastructure pending infrastructure specialist Tier 18

Acceptance Criteria

A pg_cron job (or Supabase Edge Function cron schedule) is registered with the expression '0 6 * * *' (daily at 06:00 UTC) and is visible in cron.job or supabase/functions/schedule
The registration script is idempotent: running it twice does not create a duplicate job (use SELECT cron.unschedule() + cron.schedule() pattern, or upsert logic)
The cron job successfully invokes the ReminderSchedulerService HTTP endpoint (Edge Function URL or internal RPC) — confirmed by a manual trigger test in the Supabase dashboard or via supabase functions invoke
Cron job survives a Supabase project restart / infrastructure redeploy without being lost (job is registered via a migration file, not only through the dashboard UI)
A Supabase database migration file (supabase/migrations/YYYYMMDDHHMMSS_register_reminder_cron.sql) is committed to the repository containing the registration SQL
Cron execution is logged: each invocation writes a row to a cron_execution_log table (or equivalent) with columns: job_name, triggered_at, status
Failed invocations (non-2xx from ReminderSchedulerService) are logged with the HTTP status code and do not prevent the next scheduled run
The cron expression and target URL/function name are not hardcoded inline — they are read from Supabase Vault secrets or environment variables (REMINDER_CRON_EXPRESSION, REMINDER_FUNCTION_URL)
A runbook comment in the migration file explains how to pause, reschedule, or remove the cron job in an emergency

Technical Requirements

frameworks
Supabase (pg_cron extension for PostgreSQL-level scheduling, or Supabase Edge Functions with cron trigger)
PostgreSQL (migration SQL)
Supabase CLI (supabase migrations, supabase functions deploy)
apis
pg_cron: cron.schedule(), cron.unschedule(), cron.job view
Supabase Edge Functions invoke API (for ReminderSchedulerService)
Supabase Vault (for storing cron expression and function URL as secrets)
Supabase Management API (optional, for CI-based cron verification)
data models
cron_execution_log (new table: id, job_name TEXT, triggered_at TIMESTAMPTZ, status TEXT, error_message TEXT NULLABLE)
Assignment (read by ReminderSchedulerService to identify due follow-ups)
Notification (written by ReminderSchedulerService when reminders are dispatched)
performance requirements
Cron invocation overhead (pg_cron trigger → Edge Function HTTP call) must complete within 5 seconds — the heavy reminder evaluation logic runs inside ReminderSchedulerService, not in the cron trigger itself
cron_execution_log table must have an index on triggered_at to support log queries without full table scans
security requirements
The ReminderSchedulerService endpoint must require a service-role JWT or a shared secret header — the cron job must supply this credential (stored in Supabase Vault, not in plain SQL)
pg_cron jobs run as the postgres superuser by default — scope the job to execute as the least-privileged role possible (create a dedicated cron_runner role with EXECUTE permission on the required function only)
Migration file must not contain plaintext secrets — use vault.decrypt_secret() references or Supabase CLI env substitution
cron_execution_log must be write-only for the cron_runner role and read-only for the service role (no external delete permissions)

Execution Context

Execution Tier
Tier 18

Tier 18 - 1 tasks

Can start after Tier 17 completes

Implementation Notes

Prefer pg_cron over Edge Function cron triggers for this use case — pg_cron is natively supported in Supabase and does not count against Edge Function invocation limits. Use the pattern: SELECT cron.unschedule('daily-reminder-eval'); then SELECT cron.schedule('daily-reminder-eval', '0 6 * * *', $$SELECT net.http_post(...)$$); inside the migration to guarantee idempotency. Use the pg_net extension (net.http_post) to call the Edge Function HTTP endpoint from within PostgreSQL — this keeps the call non-blocking. Store the service-role key in Supabase Vault and retrieve it with vault.decrypted_secret('reminder_service_role_key') inside the SQL call.

Create the cron_execution_log table in a prior migration (or in the same one) with a TRIGGER on pg_cron's job run results if using pg_cron 1.5+. Always test with supabase db reset followed by supabase db push locally before deploying to staging. Include a comment block at the top of the migration file: -- Emergency pause: SELECT cron.unschedule('daily-reminder-eval'); -- Resume: re-run this migration.

Testing Requirements

Write a Supabase migration integration test (or a local supabase test suite entry) that: (1) applies the migration against a local Supabase instance (supabase start); (2) asserts the cron job exists in cron.job with the correct schedule and command; (3) manually triggers the job via SELECT cron.schedule() with a 1-minute window and asserts a row appears in cron_execution_log within 70 seconds. Also write a unit test for the idempotency script: apply it twice and assert cron.job contains exactly one row for 'daily-reminder-eval'. Document manual verification steps for the TestFlight / staging environment in the PR description.

Component
Assignment Reminder Cron Trigger
infrastructure medium
Epic Risks (2)
high impact low prob technical

If the daily cron job takes longer than 24 hours to complete (due to a large dataset or a slow query), a second instance will start while the first is still running, causing duplicate reminder dispatch for assignments processed twice.

Mitigation & Contingency

Mitigation: Implement an advisory lock that prevents a second run from starting if the first is still active. Monitor run duration via the execution log table and alert if any run exceeds 30 minutes. The 10,000-assignment load test should verify the run completes in under 5 minutes.

Contingency: If a double-run occurs, the idempotency guard in ReminderDispatchService prevents duplicate notifications from being sent. The execution log identifies the overlap and allows the ops team to investigate the root cause.

high impact medium prob integration

If the activity registration hook that resets last_contact_date is implemented incorrectly or not triggered for all activity types (e.g., proxy registrations, bulk registrations), peer mentors will continue receiving reminders even after logging contact, damaging user trust.

Mitigation & Contingency

Mitigation: Audit all code paths that create activity records (direct registration, proxy registration, bulk registration, coordinator proxy) and ensure each path calls the assignment contact update. Write integration tests for each registration path asserting that last_contact_date is updated.

Contingency: Provide an authenticated admin endpoint that allows manual correction of last_contact_date for a specific assignment, enabling ops to resolve individual cases while the bug is fixed and deployed.