critical priority medium complexity database pending database specialist Tier 1

Acceptance Criteria

ScenarioRuleRepository class defined as an abstract interface with a concrete SupabaseScenarioRuleRepository implementation
fetchRulesForChapter(chapterId) returns List<ScenarioRule> filtered by chapter_id and where enabled = true, ordered by created_at ascending
upsertRule(rule) uses Supabase upsert (ON CONFLICT on id) and returns the persisted ScenarioRule with server-side updatedAt timestamp
deleteRule(ruleId) soft-deletes or hard-deletes based on a configurable flag; on success the rule is no longer returned by fetchRulesForChapter
fetchDefaultTemplates() returns a List<ScenarioRule> from the scenario_rules table where chapter_id IS NULL (global templates)
All methods throw a typed ScenarioRuleRepositoryException with an error code on Supabase error (network error, RLS violation, constraint violation)
Riverpod provider scenarioRuleRepositoryProvider exposed and returns SupabaseScenarioRuleRepository by default
Repository methods are covered by unit tests using a mocked Supabase client (no real network calls in tests)
fetchRulesForChapter is called in the Rule Engine and Configuration Manager via the Riverpod provider — no direct instantiation allowed

Technical Requirements

frameworks
Flutter
Dart
Riverpod
Supabase
apis
Supabase PostgREST: GET /scenario_rules?chapter_id=eq.{id}&enabled=eq.true
Supabase PostgREST: POST /scenario_rules with upsert header
Supabase PostgREST: DELETE /scenario_rules?id=eq.{id}
data models
ScenarioRule
ScenarioRuleRepositoryException
performance requirements
fetchRulesForChapter must return results within 500ms on a stable connection for chapters with up to 50 rules
Repository must cache fetched rules per chapterId for the lifetime of the Riverpod provider scope to avoid redundant network calls
security requirements
Supabase Row Level Security (RLS) must be configured on scenario_rules so a user can only fetch rules for chapters they belong to — the repository must not apply chapter-level filtering in Dart as a security measure (RLS is the enforcement layer)
upsertRule must only be callable by users with coordinator or org_admin role — validate role in repository before calling Supabase, throw PermissionDeniedException otherwise
deleteRule must require org_admin role minimum

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Follow the repository pattern already established in the codebase — define an abstract ScenarioRuleRepository interface first, then implement SupabaseScenarioRuleRepository. This allows the Rule Engine and Configuration Manager to depend on the interface, making them testable with mock repositories. For caching: use a simple in-memory Map> keyed by chapterId inside the repository, invalidated on any upsert or delete affecting that chapter. Do not use a persistent cache (e.g., SharedPreferences) for rules — they may change frequently.

For upsert: set the Prefer: resolution=merge-duplicates header via the Supabase Flutter client's upsert method. Ensure the scenario_rules Supabase table has the correct indexes: chapter_id (for fetchRulesForChapter), enabled (partial index where enabled = true). The fetchDefaultTemplates method supports the future admin portal use case where org admins can start from a template rather than creating rules from scratch.

Testing Requirements

Write unit tests in test/features/scenario_prompts/repositories/scenario_rule_repository_test.dart using a mock Supabase client (implement a MockSupabaseClient or use mockito/mocktail). Cover: (1) fetchRulesForChapter returns correctly deserialized list, (2) fetchRulesForChapter with empty result returns empty list, (3) upsertRule sends correct payload and returns updated rule, (4) deleteRule sends correct filter, (5) fetchDefaultTemplates filters on null chapter_id, (6) Supabase error propagates as ScenarioRuleRepositoryException with correct code, (7) RLS violation (403 response) maps to PermissionDeniedException. Also write one integration test against a local Supabase instance to verify the actual SQL queries and RLS policies work end-to-end.

Component
Scenario Configuration Manager
service medium
Epic Risks (2)
high impact medium prob scope

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.

medium impact medium prob technical

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.