critical priority medium complexity database pending backend specialist Tier 0

Acceptance Criteria

DuplicateCheckRepository class is implemented as an abstract interface + concrete Supabase implementation, enabling mock injection in tests
Method `checkForDuplicates({required String peerMentorId, required DateTime activityDate, required String activityType, String? excludeActivityId})` returns a `Future<List<CandidateDuplicate>>` with all matching records
Query uses the check_activity_duplicates Supabase RPC or equivalent indexed query — no full table scans
Returned CandidateDuplicate model includes: id, peer_mentor_id, peer_mentor_name, activity_type, activity_date, duration_minutes, notes, chapter_id, chapter_name, created_at
Cross-chapter detection is supported: query is not restricted to the submitting user's chapter, enabling detection of duplicates from NHF multi-chapter members
excludeActivityId parameter correctly excludes the activity being edited from duplicate results (prevents self-match)
Empty list is returned (not an error) when no duplicates are found
Repository throws a typed DuplicateCheckException on Supabase network/auth errors, not raw PostgrestException
Unit tests cover: duplicates found, no duplicates found, cross-chapter duplicate, self-exclusion, error propagation
Repository is registered in the Riverpod provider graph and accessible to BLoC layer

Technical Requirements

frameworks
Flutter
Riverpod
BLoC
Supabase
apis
Supabase RPC check_activity_duplicates
Supabase PostgREST activities table
data models
Activity
PeerMentor
Chapter
CandidateDuplicate
performance requirements
Repository method must complete in < 500ms p95 (relies on indexed RPC from foundation epic)
No N+1 queries: all metadata (peer_mentor_name, chapter_name) must be fetched in a single query via joins or RPC
security requirements
Repository must respect Supabase RLS policies — authenticated user's JWT is passed automatically via Supabase client; no service role key in client code
Peer mentor data from other chapters is only exposed at the level of duplicate detection, not as a general directory lookup
Input parameters must be validated before sending to Supabase to prevent injection via crafted strings

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Define a CandidateDuplicate value object (immutable, with `==` and `hashCode`) rather than reusing the general Activity model — duplicate detection needs a trimmed projection, and coupling to the full Activity model creates fragile dependencies. The Riverpod provider should be a `Provider` (not `StateProvider`) since the repository is stateless. For cross-chapter detection, NHF members can belong to up to 5 chapters — the query must join on a `peer_mentor_chapters` junction table, not filter on a single chapter_id column. Pitfall: if the Supabase RPC is not yet deployed (dependency on foundation tasks), stub the RPC call with a direct PostgREST query as a temporary fallback during development, clearly marked with a TODO.

Use `const` constructors on CandidateDuplicate for efficient widget rebuilds downstream.

Testing Requirements

Write unit tests using flutter_test with a mocked Supabase client (implement a MockSupabaseClient satisfying the interface). Test cases: (1) single duplicate returned with correct fields mapped to CandidateDuplicate; (2) multiple duplicates returned sorted by activity_date desc; (3) empty list when no match; (4) cross-chapter duplicate included in results; (5) excludeActivityId filters out the matching ID; (6) PostgrestException from Supabase is wrapped in DuplicateCheckException; (7) null/empty peerMentorId throws ArgumentError before network call. Achieve 100% branch coverage on the repository class. Integration tests against a local Supabase instance (via supabase CLI `supabase start`) should cover the happy path and cross-chapter case with seeded data.

Component
Duplicate Detection BLoC
infrastructure medium
Epic Risks (2)
medium impact high prob technical

For bulk registration with many participants, running duplicate checks sequentially before surfacing the consolidated summary could introduce a multi-second delay as each peer mentor is checked individually against the RPC. This degrades the bulk submission UX significantly.

Mitigation & Contingency

Mitigation: Issue all duplicate check RPC calls concurrently using Dart's `Future.wait` or a bounded parallel executor (max 5 concurrent calls to avoid Supabase rate limits). The BLoC collects all results and emits a single BulkDuplicateSummary state with the consolidated list.

Contingency: If concurrent RPC calls hit Supabase connection limits or rate limits, implement a batched sequential approach with a progress indicator showing 'Checking participant N of M' so the coordinator understands the delay is expected and bounded.

high impact medium prob integration

In proxy registration, the peer mentor's ID must be used as the duplicate check parameter, not the coordinator's ID. If the proxy context is not correctly threaded through the BLoC and service layer, duplicate checks will silently run against the wrong person, missing actual duplicates.

Mitigation & Contingency

Mitigation: Define a `SubmissionContext` model that carries the effective `peer_mentor_id` (distinct from `submitter_id`) and pass it explicitly through the BLoC event payload. The DuplicateDetectionService always reads peer_mentor_id from SubmissionContext, never from the authenticated user session.

Contingency: If SubmissionContext threading proves difficult to retrofit into the existing proxy registration BLoC, add an assertion in DuplicateDetectionService that throws a descriptive error when peer_mentor_id is null or matches the coordinator's own ID in a proxy context, making the bug immediately visible in testing.