high priority medium complexity backend pending backend specialist Tier 6

Acceptance Criteria

A reminder notification is dispatched to the driver exactly once at T+10 days if declaration status is still SENT at that time
Reminder is NOT sent if declaration status has transitioned to ACKNOWLEDGED before the T+10 trigger fires
Reminder push notification body reads: 'Reminder: <declaration_title> still requires your acknowledgement'
Reminder data payload mirrors the initial notification payload (type, declaration_id, deep_link)
When a declaration transitions to ACKNOWLEDGED, any pending scheduled reminder for that declaration is cancelled within 60 seconds
If the driver has no FCM token at reminder time, the reminder is skipped and logged — same as initial dispatch
Scheduler state (pending reminders) is persisted in the database — server restarts do not lose scheduled jobs
No more than one reminder is sent per declaration regardless of retries or re-triggers
Scheduled reminder rows are marked as CANCELLED (not deleted) when acknowledgement occurs, for auditability

Technical Requirements

frameworks
Supabase Edge Functions (Deno) — reminder dispatch function
Supabase pg_cron extension — fires daily check job
Supabase Postgres — scheduled_notifications table for job state
apis
FCM HTTP v1 API (same as task-013)
Supabase pg_cron: SELECT cron.schedule('check-pending-reminders', '0 8 * * *', $$...$$)
Supabase Database Webhook on declaration status UPDATE → cancellation Edge Function
data models
ScheduledNotification (id, declaration_id, driver_id, scheduled_for, status: PENDING|SENT|CANCELLED, notification_type: REMINDER)
Declaration (id, status, created_at, driver_id)
performance requirements
pg_cron job must complete within 30 seconds for up to 500 pending reminders per daily run
Cancellation (on acknowledgement) must update ScheduledNotification.status within the same transaction as the status update
security requirements
pg_cron job runs as a Postgres service role — ensure it cannot be triggered or modified by app-level authenticated users
ScheduledNotification table RLS: app-level users can only SELECT their own rows, no INSERT/UPDATE/DELETE
Reminder dispatch must re-validate declaration status immediately before sending — never trust the scheduled_for timestamp alone

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Architecture: (1) At initial notification dispatch (task-013), INSERT a row into scheduled_notifications with scheduled_for = sent_at + 10 days and status = PENDING. (2) pg_cron runs daily at 08:00 UTC: `SELECT * FROM scheduled_notifications WHERE status = 'PENDING' AND scheduled_for <= NOW()` → for each row, re-check declaration status → dispatch FCM if still SENT → mark row as SENT. (3) Supabase Database Webhook on declarations UPDATE WHERE status = 'ACKNOWLEDGED' → Edge Function `cancel-declaration-reminders` → UPDATE scheduled_notifications SET status = 'CANCELLED' WHERE declaration_id = :id AND status = 'PENDING'. Critical: the pg_cron job and the cancellation webhook can race — use `UPDATE ...

WHERE status = 'PENDING' RETURNING id` to atomically claim the row before dispatching, preventing double sends.

Testing Requirements

Unit tests: (1) pending reminder is created on initial notification dispatch; (2) status check before sending — SENT triggers push, ACKNOWLEDGED skips; (3) cancellation sets status to CANCELLED. Integration tests against local Supabase with pg_cron enabled: seed a declaration with sent_at = NOW() - 10 days, run the cron job manually, assert reminder FCM call is made. Seed an ACKNOWLEDGED declaration, run cron job, assert no FCM call. Test cancellation: insert a PENDING reminder, transition declaration to ACKNOWLEDGED via the acknowledgement RPC, assert reminder row status = CANCELLED.

Epic Risks (4)
high impact medium prob security

Org-scoped encryption key management is complex. If keys are not correctly isolated per organization, a breach in one org's key could expose another org's declarations. Additionally, key rotation is not specified but may be needed for compliance, and the current implementation may not support it.

Mitigation & Contingency

Mitigation: Use Supabase Vault or a dedicated secrets management approach for org-scoped key storage. Define the key derivation strategy (per-org master key) in a security design document reviewed before implementation begins. Include key isolation tests in the test suite.

Contingency: If a full per-org key management system cannot be safely implemented within the sprint, fall back to a single platform-level encryption key with strict RLS isolation as a temporary measure, flagging the key rotation gap as a security debt item with a defined resolution milestone.

medium impact medium prob integration

Push notification delivery to drivers depends on FCM token availability and device connectivity. If a driver has not granted notification permissions or has an expired FCM token, the declaration delivery notification will silently fail, leaving the coordinator unaware and the declaration unacknowledged.

Mitigation & Contingency

Mitigation: Implement delivery status tracking in declaration-notification-service. Fall back to in-app notification and SMS (if configured) when push delivery fails. Expose delivery failure status in the declaration status badge so coordinators can identify and manually follow up.

Contingency: If push delivery proves unreliable, implement a polling-based in-app notification fallback where drivers see pending declarations on next app open, ensuring the workflow can complete even without push notifications.

medium impact medium prob technical

The acknowledgement service is meant to validate that the driver has fully scrolled through the declaration before confirming. Implementing reliable scroll completion detection in Flutter across different screen sizes and font sizes is technically non-trivial and could be bypassed.

Mitigation & Contingency

Mitigation: Implement scroll position tracking using ScrollController with a threshold (e.g., 95% of content height reached) and record the validated state server-side before allowing acknowledgement submission. Document the approach in the legal sign-off checkpoint noted in the feature documentation.

Contingency: If reliable scroll detection cannot be implemented within the sprint, add a mandatory reading delay timer (e.g., estimated reading time based on word count) as an alternative validation mechanism, pending legal review of the approach.

medium impact low prob dependency

The driver assignment service must coordinate with the threshold-based expense approval workflow for fees above configured thresholds. If the expense approval workflow interface changes or is not yet stable, the integration point could break or produce incorrect routing behavior.

Mitigation & Contingency

Mitigation: Define a clear interface contract between driver-assignment-service and the expense approval workflow before implementation. Use dependency injection so the expense workflow client can be mocked in tests. Monitor the expense approval feature for interface changes.

Contingency: If the expense approval workflow interface is not stable, implement a direct database insert to the expense records table as a temporary bypass, with a flag indicating manual review is needed, until the stable interface is available.