Dispatch and record delivery in Scheduler Service
epic-scenario-based-follow-up-prompts-scheduler-and-ui-task-005 — Implement the final two stages of the Scheduler pipeline: forward deduplicated prompts to the Push Notification Dispatcher (FCM via Supabase Edge Function) and record each dispatched prompt in the Prompt History Repository with timestamp, scenario ID, peer mentor ID, and delivery status.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
Structure the dispatch stage as: final results = await Future.wait(prompts.map((p) => _dispatchOne(p))). Inside _dispatchOne: call dispatcher.send(p), then call historyRepo.save(HistoryRecord(...)). Catch all errors from send() and save() independently. Use a local record-per-prompt approach rather than bulk insert to preserve individual delivery_status accuracy.
The DispatchSummary can be built by counting results. For the Edge Function call, use supabase.functions.invoke('dispatch-scenario-prompt', body: {...}) — this uses the authenticated client so JWT is forwarded. Keep dispatch payload minimal: { 'peer_mentor_id': p.peerId, 'scenario_id': p.scenarioId, 'device_token': p.deviceToken }. Device tokens should be pre-fetched earlier in the pipeline, not looked up per-dispatch call.
Testing Requirements
Unit tests with flutter_test. Mock both PushNotificationDispatcher and PromptHistoryRepository. Test cases: (1) 3 prompts, all dispatch successfully — assert send() called 3 times, save() called 3 times with status='dispatched', DispatchSummary(3,0,0); (2) 3 prompts, second dispatch fails — assert save() called 3 times (first status='dispatched', second status='failed', third status='dispatched'), DispatchSummary(2,1,0); (3) empty list — assert neither send() nor save() called, DispatchSummary(0,0,0); (4) dispatch succeeds but save() throws — assert exception logged, pipeline does not throw. Integration test: trigger full pipeline against Supabase dev instance with a test FCM token, verify history record written with correct fields.
If the scheduler runs concurrently (e.g., two overlapping cron invocations due to edge function retry), duplicate prompts could be dispatched before the first run's history records are committed, breaking the deduplication guarantee.
Mitigation & Contingency
Mitigation: Use a Postgres advisory lock or unique constraint on (user_id, scenario_id, activity_ref) in the prompt history table to make concurrent writes idempotent; design the scheduler to check history inside a transaction.
Contingency: If concurrency issues persist in production, add a distributed lock via Supabase Edge Function concurrency limit (max_instances=1) for the evaluation function as a hard guard.
Coordinators may find scenario configuration unclear if trigger conditions are expressed as raw JSON or technical terminology, leading to misconfiguration and irrelevant prompts being sent to peer mentors.
Mitigation & Contingency
Mitigation: Design the ScenarioConfigurationScreen to display human-readable descriptions of each template's trigger condition (e.g., 'Send 3 days after first contact if wellbeing concern was flagged') rather than raw rule properties; validate with an HLF coordinator in a design review before implementation.
Contingency: If coordinators still misconfigure rules after launch, add a preview mode that shows a simulated prompt based on a test activity before the rule is enabled.