critical priority medium complexity backend pending backend specialist Tier 1

Acceptance Criteria

checkDuplicate(DuplicateCheckRequest) queries Supabase for all activity records matching peer_mentor_id AND date (date-only comparison, ignoring time) AND activity_type_id where status != 'voided'
Returns an empty list when no conflicting records exist — does NOT throw for the no-conflict case
Returns a List<DuplicateConflictResult> with one entry per conflicting record found, each containing original_submitter_id, submission_timestamp, and conflicting_record_reference
Voided/cancelled records are explicitly excluded from results via query filter, not post-filter in Dart
Date comparison in the query uses date-only equality (not timestamp equality) so activities at different times on the same day are caught
Network errors and Supabase exceptions are caught and rethrown as typed DuplicateDetectionException with the original error wrapped
Method is idempotent — calling it multiple times with the same input always returns the same result
Returns results within 500ms under normal network conditions
A record submitted by the same coordinator is still returned as a conflict if it matches the composite key (prevents coordinator self-duplication)

Technical Requirements

frameworks
Flutter
Dart
Riverpod
apis
Supabase PostgREST REST API
Supabase Flutter SDK
data models
ActivityRecord
ProxyActivityEntry
DuplicateCheckRequest
DuplicateConflictResult
performance requirements
Single Supabase query per call — no sequential queries
Query must use indexed columns: peer_mentor_id, date, activity_type_id
Response time under 500ms on typical Norwegian mobile network (4G)
security requirements
Query must be executed under the authenticated coordinator's Supabase JWT — no service role key on client
Row-level security (RLS) on the activities table must permit coordinators to read records attributed to their mentors only
Do not log full activity record content in error messages — log only record IDs

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 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

Implement as a concrete class SupabaseDuplicateDetectionService implementing IDuplicateDetectionService. Inject the Supabase client via constructor for testability. The Supabase query should look like: supabase.from('activities').select('id, registered_by, created_at').eq('attributed_to', request.peerMentorId).eq('activity_date', request.date.toIso8601String().substring(0,10)).eq('activity_type_id', request.activityTypeId).neq('status', 'voided'). Confirm the exact table and column names against the Supabase schema before writing queries.

Date handling is critical — normalize to UTC date string before comparison to avoid timezone drift. Register the service via Riverpod Provider so BLoC layers can inject it. Keep this class thin: no business logic beyond mapping Supabase rows to DuplicateConflictResult DTOs.

Testing Requirements

Unit tests with mocked Supabase client covering: no conflicts found (empty list), single conflict found, multiple conflicts found, voided record correctly excluded, network timeout throws DuplicateDetectionException, date boundary cases (activity on same date but different time still matches). Integration test against a Supabase test project verifying the actual query filters work correctly end-to-end. Use flutter_test with mockito or mocktail for unit layer. Minimum 85% branch coverage.

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.