Implement PromptHistoryRepository read/write interface
epic-scenario-based-follow-up-prompts-core-logic-task-003 — Create the PromptHistoryRepository class backed by the scenario_prompt_history Supabase table. Implement recordPromptSent(activityId, ruleId, sentAt), fetchRecentPrompts(mentorId, windowHours), and hasPromptBeenSent(activityId, ruleId). Include Riverpod provider. Used by the Rule Engine to enforce cooldown windows and prevent duplicate prompts.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
The idempotency of recordPromptSent is critical — the Rule Engine may be triggered multiple times for the same activity (e.g., app restarted, push notification retry). Implement this using a Supabase upsert with onConflict: 'activity_id,rule_id' and ignoreDuplicates: true, or a raw INSERT ... ON CONFLICT DO NOTHING. For hasPromptBeenSent, use a HEAD request with Prefer: count=exact to get just the count without fetching row data — this is the most efficient pattern for an existence check in Supabase PostgREST.
The mentorId parameter in fetchRecentPrompts should always be the currently authenticated user's UID (auth.currentUser.id) — do not accept arbitrary mentorIds from the UI layer, as this would bypass RLS intent. The windowHours parameter maps directly to the ScenarioRule.delayMaxHours: the Rule Engine queries prompt history within the maximum possible delay window to determine whether a prompt has already been sent during any valid delay slot.
Testing Requirements
Write unit tests in test/features/scenario_prompts/repositories/prompt_history_repository_test.dart using a mocked Supabase client. Cover: (1) recordPromptSent inserts and returns correct PromptHistoryRecord, (2) recordPromptSent called twice with same activityId+ruleId returns existing record (idempotency), (3) fetchRecentPrompts returns only records within the window (test with records inside and outside the window), (4) fetchRecentPrompts returns empty list when no recent prompts exist, (5) hasPromptBeenSent returns true when a matching record exists, (6) hasPromptBeenSent returns false when no matching record exists, (7) Supabase error maps to PromptHistoryRepositoryException. Write one integration test against local Supabase verifying the unique constraint and RLS policies.
The Rule Engine must support a flexible JSON rule schema that can express compound conditions (e.g., contact_type AND wellbeing_flag AND delay_days). Underestimating schema expressiveness may require breaking changes to the rule format after coordinators have already configured rules.
Mitigation & Contingency
Mitigation: Define and freeze the rule JSON schema (trigger_type enum, metadata_conditions structure, delay logic) before any implementation begins; validate schema against all known HLF scenarios documented in the feature spec.
Contingency: If schema changes are needed after deployment, implement a schema version field and a migration utility that upgrades stored rules to the new format without coordinator intervention.
Deep-link navigation to the activity wizard with pre-filled arguments may fail if the user's session has expired or if the wizard route is not yet mounted in the navigator stack, causing unhandled navigation exceptions.
Mitigation & Contingency
Mitigation: Implement session state check before navigation; if session is expired, redirect to biometric/login screen and store the pending deep-link URI for post-auth redirect using go_router's redirect mechanism.
Contingency: If post-auth redirect proves unreliable, fall back to navigating to the home screen with a visible action banner that re-triggers the wizard with pre-filled arguments.