critical priority high complexity backend pending backend specialist Tier 1

Acceptance Criteria

DuplicateActivityDetectionService.checkForDuplicate(PendingActivity) returns a non-null DuplicateActivityWarning when an existing activity for the same contact_id has the same activity_type and a date within ±1 calendar day
checkForDuplicate returns null when no activities exist for the contact across any chapter
checkForDuplicate returns null when existing activities have the same activity_type but a date delta of ±2 or more days
checkForDuplicate returns null when existing activities are within ±1 day but have a different activity_type
When multiple candidate matches exist, the service returns the warning for the closest matching activity (smallest absolute date delta)
CrossChapterActivityQuery is called exactly once per invocation, receiving the contact_id as the sole filter parameter
The method signature is async and returns Future<DuplicateActivityWarning?>
Date comparison uses calendar days (not 24-hour periods), accounting for time-zone-agnostic date objects
The service does not write to any database; it is read-only and side-effect-free
If CrossChapterActivityQuery throws, the exception propagates unhandled (caller is responsible for error handling)

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
CrossChapterActivityQuery (internal service)
Supabase REST API (via repository layer)
data models
PendingActivity
StoredActivity
DuplicateActivityWarning
ContactChapter
performance requirements
checkForDuplicate must complete in under 500ms on a standard device with up to 200 stored activities for the contact
No N+1 queries: all activities for the contact must be fetched in a single CrossChapterActivityQuery call
Comparison loop must be O(n) where n is the count of stored activities returned
security requirements
contact_id must be validated as non-null and non-empty before querying
Service must not expose raw Supabase query results; all data flows through typed domain models
No PII (names, addresses) is stored in the warning object beyond what is strictly necessary for UI rendering

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Model PendingActivity and StoredActivity as immutable Dart classes (final fields, const constructors). Represent dates as DateTime with time set to midnight to ensure calendar-day comparison — avoid Duration(hours: 24) comparisons which break across DST transitions. The ±1 day tolerance should be implemented as: (stored.date.difference(pending.date).inDays).abs() <= 1. Expose the service via Riverpod Provider so it can be injected and mocked in tests.

Do not couple this service to any BLoC directly; BLoCs call it via the provider. If multiple matches exist, sort candidates by date delta ascending and return the first — document this tiebreak rule in code comments.

Testing Requirements

Write unit tests in flutter_test covering all comparison branches (exact match, +1 day, -1 day, +2 day returns null, different type returns null, empty result set returns null). Mock CrossChapterActivityQuery using mockito or manual test doubles. Verify the returned DuplicateActivityWarning fields are correctly populated. Assert the query is called exactly once per invocation.

Achieve 100% branch coverage on the comparison logic. No integration tests required for this service in isolation.

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

The ±1 day duplicate detection tolerance is specified in the acceptance criteria but timezone handling is not defined. A coordinator in UTC+2 submitting at 23:00 and another in UTC+0 submitting at 01:00 the next calendar day could trigger or miss a duplicate depending on which timezone the comparison uses.

Mitigation & Contingency

Mitigation: Define and document the authoritative timezone for all date comparisons (UTC stored in Supabase, all comparisons performed in UTC). Add timezone boundary unit tests covering the ambiguous ±1 day edges.

Contingency: If false positives or false negatives are reported in production, provide a coordinator-visible audit trail of duplicate detections so erroneous flags can be investigated and cleared manually.

medium impact medium prob technical

The Duplicate Activity Detection Service performs a cross-chapter join query synchronously during the activity submission flow. On slow mobile connections this could cause a perceptible stall on the submission confirmation step, degrading user experience.

Mitigation & Contingency

Mitigation: Pre-fetch the cross-chapter activity dataset for the selected contact immediately when the contact is selected in the activity wizard (not only at submit time), storing the result in the state manager for instant comparison at submission.

Contingency: If latency is still unacceptable, implement a loading indicator on the submit action and add a configurable server-side timeout with graceful degradation: if the check times out, allow submission with a logged 'check skipped' audit entry rather than blocking the user.