Create follow_up_notification_log migration
epic-scenario-based-follow-up-prompts-foundation-task-002 — Write and apply a Supabase database migration that creates the follow_up_notification_log table for tracking delivery state and enabling deduplication. Include columns for log_id, peer_mentor_id, chapter_id, scenario_rule_id (FK), delivered_at, delivery_channel (fcm/apns), idempotency_key (unique), and status enum. The idempotency_key unique constraint is the primary deduplication mechanism.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Use the Supabase CLI (`supabase migration new follow_up_notification_log`) to generate the migration file shell. Write both an `up` and `down` migration. Use `gen_random_uuid()` as the default for log_id to avoid importing pgcrypto separately. The idempotency_key should be constructed by callers as a deterministic hash (e.g.
SHA-256 of `{peer_mentor_id}:{scenario_rule_id}:{date_window}`) — document this convention in a comment inside the migration file. Do NOT define RLS in this migration — RLS is handled in task-004. Ensure the FK on scenario_rule_id has `ON DELETE CASCADE` so that deleting a rule automatically clears its delivery history. Place composite index creation AFTER the table creation statement.
Avoid using SERIAL for primary key — UUID is required for distributed consistency across potential future multi-region setups.
Testing Requirements
Unit tests are not applicable for raw SQL migrations. Integration tests must: (1) apply the migration to a local Supabase instance and assert all columns exist with correct types using `information_schema.columns`; (2) attempt to insert a duplicate idempotency_key and assert a unique constraint violation is raised; (3) attempt to insert a row with null idempotency_key and assert a NOT NULL violation; (4) insert a valid row and verify all CHECK constraints accept valid enum values; (5) insert an invalid delivery_channel value (e.g. 'sms') and assert rejection; (6) run the rollback migration and verify the table no longer exists. All tests should run against a fresh local Supabase instance using `supabase start`.
Supabase RLS policies for chapter-scoped rule access may interact unexpectedly with service-role keys used by the Edge Function, potentially blocking backend reads or leaking cross-chapter data.
Mitigation & Contingency
Mitigation: Write and review RLS policies in isolation with automated policy tests before merging; define a dedicated service-role bypass policy scoped to the edge function's Postgres role.
Contingency: If RLS blocks the edge function, temporarily use a bypass policy with audit logging while a permanent fix is implemented; escalate to a Supabase security review.
FCM device tokens become invalid when users reinstall the app or revoke permissions; stale tokens cause silent delivery failures that are hard to detect without explicit error handling.
Mitigation & Contingency
Mitigation: Implement token invalidation handling in PushNotificationDispatcher that removes stale tokens from the database on FCM 404/410 responses; log all delivery failures with structured output.
Contingency: If token hygiene proves unreliable, add a periodic token refresh job that re-registers all active users' tokens via the FCM registration endpoint.