critical priority medium complexity integration pending integration specialist Tier 2

Acceptance Criteria

dispatch(token, title, body, data) method is implemented and routes to FCM HTTP v1 for Android tokens and APNs for iOS tokens based on the platform field from the device_token record
On a successful HTTP 200/201 response from FCM/APNs, delivery status is recorded as 'delivered' via ScenarioNotificationRepository.updateDeliveryStatus
On transient failure (HTTP 429, 500, 502, 503, network timeout), dispatch retries up to 3 times with exponential backoff: 1s, 2s, 4s delays between attempts
On permanent failure (HTTP 400, 401, 404 — invalid token), no retry is attempted; failure is logged and delivery status is recorded as 'failed' with error code
After all retries are exhausted without success, the notification is marked for retry via ScenarioNotificationRepository.getPendingRetries eligibility
FCM server key and APNs credentials are sourced from server-side environment only (Supabase Edge Function context) — never from the Flutter mobile client
dispatch method is invoked exclusively from a Supabase Edge Function, not directly from the Flutter app
All outbound HTTP calls use TLS; no plaintext push dispatch is permitted
The method signature accepts a data map (Map<String, String>) for deep-link payload fields (scenario_type, entity_id, target_route)
Unit tests cover success, transient retry, and permanent failure paths with mocked HTTP responses

Technical Requirements

frameworks
Dart http package
Supabase Edge Functions (Deno)
apis
Firebase Cloud Messaging (FCM) HTTP v1 API
APNs HTTP/2 API
data models
device_token
performance requirements
Total dispatch time including up to 3 retries must not exceed 15 seconds
Exponential backoff must not block the Edge Function event loop — use async/await with Future.delayed
security requirements
FCM server key and APNs credentials stored exclusively in Supabase Edge Function environment secrets — never in mobile app binary
All push dispatch executed server-side via Edge Functions only
Notification payloads contain minimal data — full content fetched from API on open, not embedded in push payload
FCM tokens sourced from device_token table with RLS enforced; dispatcher never accepts arbitrary tokens from client input
TLS enforced for all outbound HTTP calls to FCM and APNs endpoints

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

This dispatcher runs in a Supabase Edge Function (Deno runtime), not in the Flutter app. Use Deno's fetch API rather than Dart's http package for the server-side implementation; the Dart http package reference in the description applies only if a Dart-based server is used. For FCM HTTP v1, authenticate using a Google service account JWT (not the legacy server key). For APNs, use JWT-based authentication with the .p8 key file.

Platform routing: query the device_token table for the token's platform field before dispatching. Exponential backoff: implement as a simple loop with await Future.delayed(Duration(seconds: pow(2, attempt))). Log all retry attempts with attempt number, error code, and token hash (not plain token) for auditability without PII exposure.

Testing Requirements

Write unit tests using flutter_test (or Dart test package for Edge Function logic). Mock the HTTP client to return HTTP 200, HTTP 429 (retry scenario), HTTP 400 (permanent failure), and network timeout. Verify retry count increments correctly and backoff delays are applied. Verify ScenarioNotificationRepository is called with correct status in each scenario.

Verify that after 3 failed retries the record is marked as pending retry. Integration test: deploy to Supabase Edge Function staging environment and send a real test push to a TestFlight device; confirm receipt. Achieve 90% branch coverage on dispatcher logic.

Component
Push Notification Dispatcher
infrastructure medium
Epic Risks (3)
high impact medium prob dependency

FCM service account key and APNs certificate configuration may be missing or misconfigured in the Supabase Edge Function secrets store, blocking end-to-end push delivery testing until resolved by the infrastructure owner.

Mitigation & Contingency

Mitigation: Raise a credentials-setup task in the project board at epic start; document the exact secret names required in scenario-evaluation-config so the infrastructure owner can provision them independently of development work.

Contingency: Implement a mock push-notification-dispatcher stub that records payloads to the database for local testing, allowing the rest of the feature to proceed while credentials are obtained.

high impact low prob security

Incorrect RLS policies on the scenario_notifications or notification_preferences tables could allow one user to read or modify another user's notification records, constituting a data privacy breach.

Mitigation & Contingency

Mitigation: Write dedicated RLS policy tests using Supabase's built-in test framework before any application code touches the tables; require a peer security review of all policy definitions before merging.

Contingency: If a policy gap is discovered post-merge, immediately disable the affected table's read policy, notify the security lead, and deploy a hotfix with corrected policies before re-enabling access.

medium impact medium prob dependency

Norwegian Bokmål ARB localisation strings for all scenario message templates may not be available at implementation time, causing content-builder tests to fail and delaying integration.

Mitigation & Contingency

Mitigation: Define all required ARB message keys in a tracked document shared with the content owner at epic kickoff; use English placeholder strings that follow the final format so template injection logic can be tested independently.

Contingency: Ship with English-only strings in the first release and gate Norwegian strings behind a feature flag that is enabled once translations are reviewed and approved.