critical priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

checkDuplicateBatch(List<DuplicateCheckRequest>) returns a Map<DuplicateCheckRequest, List<DuplicateConflictResult>> with one entry per input key
Keys with no conflicts are present in the map with an empty list value — no keys are omitted from the result
The implementation issues exactly one Supabase query or RPC call regardless of batch size (no N+1 pattern)
Batch size limit of 100 composite keys per call is enforced with a clear exception if exceeded
Empty input list returns an empty map without making any network call
Results are correctly correlated back to their input keys even when Supabase returns rows in arbitrary order
Partial batch: if some keys yield conflicts and others don't, the map correctly reflects both outcomes
Network or Supabase errors throw DuplicateDetectionException wrapping the cause, consistent with the single-record method
Performance: batch of 20 keys completes in under 1 second on 4G network

Technical Requirements

frameworks
Flutter
Dart
Riverpod
apis
Supabase PostgREST REST API
Supabase RPC (PostgreSQL functions)
Supabase Flutter SDK
data models
DuplicateCheckRequest
DuplicateConflictResult
ActivityRecord
performance requirements
Single network round-trip for any batch size up to 100
In-memory result grouping must complete in O(n) using a HashMap
Avoid deserializing unnecessary columns — select only id, attributed_to, activity_date, activity_type_id, registered_by, created_at, status
security requirements
All queries run under coordinator JWT — no elevated privileges for batch queries
Batch input validation: each composite key must have non-null, non-empty peer_mentor_id, valid date, and non-empty activity_type_id before the query is issued
RLS policies must still apply to batch queries — do not bypass with service role

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Dependents (28)
epic-proxy-activity-registration-orchestration-task-001 component Cross-Epic Component proxy-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-orchestration-task-002 component Cross-Epic Component proxy-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-orchestration-task-003 component Cross-Epic Component proxy-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-orchestration-task-004 component Cross-Epic Component proxy-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-orchestration-task-005 component Cross-Epic Component proxy-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-orchestration-task-006 component Cross-Epic Component bulk-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-orchestration-task-007 component Cross-Epic Component bulk-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-orchestration-task-008 component Cross-Epic Component bulk-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-orchestration-task-009 component Cross-Epic Component bulk-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-orchestration-task-010 component Cross-Epic Component proxy-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-orchestration-task-011 component Cross-Epic Component bulk-registration-service depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-001 component Cross-Epic Component bulk-participant-list depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-002 component Cross-Epic Component bulk-participant-list depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-003 component Cross-Epic Component bulk-participant-list depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-004 component Cross-Epic Component duplicate-warning-dialog depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-005 component Cross-Epic Component duplicate-warning-dialog depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-006 component Cross-Epic Component duplicate-warning-dialog depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-007 component Cross-Epic Component proxy-peer-mentor-selector depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-008 component Cross-Epic Component proxy-peer-mentor-selector depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-009 component Cross-Epic Component proxy-peer-mentor-selector depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-010 component Cross-Epic Component proxy-activity-form depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-011 component Cross-Epic Component proxy-activity-form depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-012 component Cross-Epic Component proxy-registration-screen depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-013 component Cross-Epic Component proxy-registration-screen depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-014 component Cross-Epic Component proxy-registration-screen depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-015 component Cross-Epic Component bulk-registration-screen depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-016 component Cross-Epic Component bulk-registration-screen depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-ui-task-017 component Cross-Epic Component bulk-registration-screen depends on proxy-duplicate-detection-service

Implementation Notes

Two viable implementation strategies: (1) Use Supabase RPC with a PostgreSQL function that accepts a JSON array of composite keys and returns matching records — preferred for complex multi-column IN matching. (2) Use PostgREST OR filter chains if Supabase client supports it. Strategy 1 is recommended because multi-column IN-clause is not natively supported in PostgREST without RPC. The PostgreSQL function signature should be: check_duplicate_batch(keys jsonb) RETURNS TABLE(...).

After fetching results, group rows in Dart by their composite key using a Map> where the key is a normalized string like '{peer_mentor_id}_{date}_{activity_type_id}'. Then map back to original DuplicateCheckRequest objects. Ensure the Supabase RPC function is created in a migration file and committed to source control. Do not create database functions ad-hoc.

Testing Requirements

Unit tests with mocked Supabase client: empty input, single item batch, large batch (20 items), batch with mix of conflicting and clean keys, batch where all keys conflict, batch size limit exceeded, network error mid-batch. Verify exactly one query is issued per call using mock call count assertions. Integration test against Supabase test project with pre-seeded conflicting records. Performance test: simulate 20-key batch and assert completion under 1 second.

Use flutter_test with mocktail.

Component
Proxy Duplicate Detection Service
service medium
Epic Risks (2)
medium impact medium prob scope

Overly strict duplicate matching (exact date + type) may flag legitimate back-to-back sessions of the same activity type on the same day as duplicates, frustrating coordinators and undermining trust in the feature.

Mitigation & Contingency

Mitigation: Confirm with product owners whether the matching key should be (mentor_id, date, activity_type_id) only or should also consider duration and time-of-day. Document the chosen threshold in the service and surface it in the duplicate warning dialog for transparency.

Contingency: If false-positive rates are high in user testing, add a duration-window tolerance parameter to the detection query that can be tuned without code changes.

high impact low prob security

If the current session token is invalidated between the coordinator starting the proxy form and submitting it, the activity-attribution-service may fail to resolve the coordinator's user ID, causing a silent attribution error.

Mitigation & Contingency

Mitigation: Read the coordinator's user ID from the session at service call time rather than at form-open time. Validate the session is still active before committing the insert, and surface a clear re-authentication prompt if it has expired.

Contingency: If a mis-attributed record is detected post-submission, the audit log retains the original session metadata, allowing a corrective record to be created with accurate attribution.