Build pg_cron scheduler entry point Edge Function
epic-scenario-push-engagement-core-engine-task-011 — Implement the Scenario Edge Function Scheduler as a Supabase Edge Function designed to be invoked by a pg_cron job. The function queries all active peer mentors from the database and iterates over each, delegating evaluation to the Scenario Trigger Engine. Include pagination to handle large peer mentor rosters without timeouts.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 7 - 84 tasks
Can start after Tier 6 completes
Implementation Notes
Implement keyset pagination as `SELECT id FROM peer_mentors WHERE status = 'active' AND id > $cursor ORDER BY id ASC LIMIT $pageSize`. Store the last ID from each page as the cursor for the next query. Use `Promise.all` with a semaphore (simple counter + awaited promises) for bounded concurrency rather than a third-party library — Deno's module ecosystem in Edge Functions can be unreliable. The timeout guard should check `Date.now() - startTime > TIMEOUT_THRESHOLD_MS` at the start of each page loop iteration, not mid-page.
Design the scheduler as stateless — it does not maintain any cross-invocation state; pg_cron handles scheduling. The shared secret should be a random 256-bit hex string stored as a Supabase Edge Function secret (environment variable), compared using `crypto.timingSafeEqual` to prevent timing attacks.
Testing Requirements
Unit tests: (1) pagination cursor advances correctly across multiple pages, (2) bounded concurrency limiter respects max parallel invocations, (3) timeout guard triggers at threshold and returns partial=true in response, (4) zero mentors handled without error, (5) per-mentor error caught and counted without aborting batch. Integration test with local Supabase: seed 50 active mentors, invoke scheduler, assert all 50 evaluation requests received by mock trigger engine. Pagination test: seed 250 mentors with page size 100, assert 3 pages queried. Load test: seed 500 mentors, assert function completes within 28 seconds.
Unauthorized request test: missing or wrong secret returns 401.
The scenario-edge-function-scheduler must evaluate all active peer mentors within the 30-second Supabase Edge Function timeout. For large organisations, a sequential evaluation loop may exceed this limit, causing partial runs and missed notifications.
Mitigation & Contingency
Mitigation: Design the trigger engine to batch mentor evaluations using database-side SQL queries (bulk inactivity check via a single query rather than per-mentor calls), and add a performance test against 500 mentors during development. Document the evaluated mentor count per scenario type in scenario-evaluation-config to allow selective scenario execution per run.
Contingency: If single-run execution is insufficient, split evaluation into per-scenario-type scheduled functions (inactivity check, milestone check, expiry check) on separate cron schedules, dividing the computational load across multiple invocations.
A race condition between concurrent scheduler invocations or retried cron triggers could cause the same scenario notification to be dispatched multiple times to a mentor, severely degrading trust in the feature.
Mitigation & Contingency
Mitigation: Implement cooldown enforcement using a database-level upsert with a unique constraint on (user_id, scenario_type, cooldown_window_start) so that a second invocation within the same window is rejected at the persistence layer rather than the application layer.
Contingency: Add an idempotency key derived from (user_id, scenario_type, evaluation_date) to the notification record insert; if a duplicate key violation is caught, log it as a warning and skip dispatch without error.
The trigger engine queries peer mentor activity history across potentially multiple organisations and chapters. RLS policies configured for app-user roles may block the Edge Function's service-role queries, or query performance may degrade on large activity tables.
Mitigation & Contingency
Mitigation: Confirm the Edge Function runs with the Supabase service role key (bypassing RLS) and add composite indexes on (user_id, activity_date) to the activity tables before implementing the inactivity detection query.
Contingency: If service-role access is restricted by organisational policy, implement a dedicated database function (SECURITY DEFINER) that performs the inactivity aggregation and is callable by the Edge Function with limited scope.