Deduplication pass against Prompt History in Scheduler
epic-scenario-based-follow-up-prompts-scheduler-and-ui-task-004 — Implement the deduplication step in the Scheduler Service that filters out ScenarioPrompt candidates already present in the Prompt History Repository within the cooldown window (configurable, default 72 hours per peer mentor per scenario). Return only net-new prompts eligible for dispatch.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
Define a PromptHistoryRepository interface with: Future> getRecentHistory(List
Then do client-side Set intersection to build the 'already sent' set keyed on '$peerId:$scenarioId'. Filter candidates using candidates.where((c) => !alreadySentKeys.contains('${c.peerId}:${c.scenarioId}')).toList(). Keep repository injectable for unit testing.
Testing Requirements
Unit tests with flutter_test using a mocked PromptHistoryRepository. Test cases: (1) 3 candidates, mock returns 1 matching history record — assert 2 net-new candidates returned; (2) mock returns empty history — assert all 3 candidates returned; (3) mock returns history for all 3 — assert empty list; (4) empty candidates input — assert repository query method never called; (5) cooldownHours=0 — assert repository query skipped, all candidates returned. Integration test: seed prompt_history table with a record for (peer1, scenario-A) at T-48h, pass candidate for same pair with cooldownHours=72 — assert it is filtered. Pass candidate for same pair with cooldownHours=24 — assert it passes through.
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.