Implement cross-chapter duplicate activity comparison logic
epic-multi-chapter-membership-handling-core-services-task-006 — Implement the core comparison algorithm in DuplicateActivityDetectionService: accept a PendingActivity (contact_id, date, activity_type), call CrossChapterActivityQuery to retrieve all stored activities for the contact across chapters, compare each candidate against the pending activity using a ±1 day date tolerance and exact activity_type match, and return a structured DuplicateActivityWarning if a match is found, or null if none.
Acceptance Criteria
Technical Requirements
Execution Context
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.
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.
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.