critical priority medium complexity integration pending integration specialist Tier 5

Acceptance Criteria

When the trigger engine produces a dispatch decision for a peer mentor, it invokes the push notification dispatcher (582) with the correct payload including user_id, scenario_type, notification content, and locale
The notification record (user_id, scenario_type, dispatched_at) is written to the scenario notification repository only after the push dispatcher returns a success response — not before
If the push dispatcher returns an error (network failure, FCM error, invalid token), no notification record is written and the error is logged with full context
If the notification record write fails after a successful push dispatch, the failure is logged as a critical error with the dispatch details, but the function does not retry the push — idempotency is maintained via the notification record, not re-dispatch
The notification record write uses INSERT ... ON CONFLICT DO NOTHING to prevent duplicate records under concurrent execution
Dispatch integration must handle the case where the peer mentor has no registered device token — skip dispatch gracefully and log with reason 'no_device_token', do not write a notification record
End-to-end: a scenario that passes all evaluation gates (opt-out, cooldown, deduplication) must result in exactly one push notification and exactly one notification repository record per evaluation run
Dispatcher call includes idempotency key (hash of user_id + scenario_type + date) to prevent duplicate FCM sends if the Edge Function retries

Technical Requirements

frameworks
Supabase Edge Functions (Deno)
Supabase PostgreSQL 15
Firebase Cloud Messaging (FCM) API v1
apis
582-push-notification-dispatcher internal Edge Function API
Firebase Cloud Messaging API v1
Supabase REST API
data models
device_token
activity
assignment
performance requirements
Dispatch + record write must complete within 3 seconds total to avoid Edge Function timeout
Device token lookup must use indexed query on (user_id, platform) — no full scan
Notification record insert must be a single statement with ON CONFLICT clause
security requirements
FCM server key and service account credentials never bundled in mobile app — all dispatch via server-side Edge Function only
Notification payload contains minimal data — only notification_id and scenario_type — full content fetched from API on app open
Device token table access restricted to service role — RLS prevents mobile client direct access
Idempotency key prevents duplicate sends if function retries due to transient infrastructure failure
No PII in FCM payload — user_id UUID only

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Structure the integration as: `evaluateAndDispatch(mentorId, scenarioType, config, deps)` where `deps` is an injectable dependency object containing the push dispatcher client and notification repo — this enables clean unit testing without network calls. The write-after-dispatch ordering is critical: use a try/catch around the push dispatcher call, only enter the record write block on success. For the idempotency key, use `crypto.subtle.digest('SHA-256', encode(userId + scenarioType + isoDate))` — this is available natively in Deno without additional dependencies. The 582 dispatcher should be called via Supabase Edge Function invocation using the service role key from environment variables.

Design the notification record schema with columns: id (uuid), user_id (uuid), scenario_type (text), dispatched_at (timestamptz), idempotency_key (text UNIQUE) — the UNIQUE constraint on idempotency_key provides the ON CONFLICT target.

Testing Requirements

Integration tests with mock push dispatcher and mock scenario notification repository: (1) successful dispatch path — assert dispatcher called with correct payload and record written, (2) dispatcher failure — assert no record written and error logged, (3) record write failure after successful dispatch — assert critical error logged with dispatch details, (4) no device token — assert skip with correct log reason, (5) concurrent duplicate dispatch — assert ON CONFLICT prevents duplicate records. End-to-end test against local Supabase with FCM sandbox: trigger a complete evaluation cycle and verify exactly one notification record exists afterward. Test idempotency: run same evaluation twice and assert dispatcher called once (second blocked by cooldown or deduplication).

Component
Scenario Trigger Engine
service high
Epic Risks (3)
high impact medium prob technical

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.

high impact low prob technical

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.

medium impact medium prob integration

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.