critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

Abstract class DuplicateDetectionService is defined in lib/domain/services/duplicate_detection_service.dart with no concrete implementation
DuplicateCandidate model includes fields: candidateActivityId (String), contactId (String), activityDate (DateTime), activityType (String), durationMinutes (int), overlapScore (double), and matchedFields (List<String>)
DuplicateCheckResult model includes fields: candidates (List<DuplicateCandidate>), isBlocked (bool), severity (DuplicateSeverity), and checkedAt (DateTime)
DuplicateSeverity is defined as an enum with values: none, warning, block
checkForDuplicates() method signature accepts an ActivityDraft parameter and returns Future<DuplicateCheckResult>
isBlockableDuplicate() method signature accepts a DuplicateCheckResult and returns bool (pure, synchronous)
getCandidates() method signature accepts a DuplicateCheckResult and returns List<DuplicateCandidate>
All models implement copyWith(), equality (== and hashCode via Equatable or manual override), and toString() for debugging
All models and the interface are exported from a single barrel file: lib/domain/services/duplicate_detection_exports.dart
No Supabase, Flutter, or repository imports exist in the interface or model files — domain layer must be pure Dart

Technical Requirements

frameworks
Dart (pure, no Flutter dependency)
equatable package for value equality
data models
ActivityDraft (existing model, imported as dependency)
DuplicateCandidate (new)
DuplicateCheckResult (new)
DuplicateSeverity (new enum)
performance requirements
Domain models must be immutable value objects — all fields final
No async operations in model constructors or equality checks
security requirements
DuplicateCandidate must not expose raw PII — use IDs only, not names or contact details
overlapScore must be a normalized value between 0.0 and 1.0 to prevent score inflation attacks

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Place all files under lib/domain/services/ to maintain clean architecture separation. The interface must be abstract with no factory constructor — dependency injection (via Riverpod provider or manual DI) will wire the concrete implementation later. Use the equatable package for DuplicateCandidate and DuplicateCheckResult to avoid boilerplate equality code. Define DuplicateSeverity as an enum rather than a class constant so switch statements in the BLoC are exhaustive and compiler-enforced.

The ActivityDraft parameter type for checkForDuplicates() should be the same draft model used in the activity submission wizard — confirm the exact type name with the activity creation epic before finalizing the signature.

Testing Requirements

Write unit tests in test/domain/services/duplicate_detection_service_test.dart. Test: (1) DuplicateCandidate equality — two instances with identical fields are equal, (2) DuplicateCheckResult with isBlocked=true and severity=block is correctly constructed, (3) copyWith() on DuplicateCheckResult produces expected partial update, (4) DuplicateSeverity enum covers all 3 values and .name returns expected strings. No mocking needed — pure value object tests. Target 100% line coverage for the model files.

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

If the duplicate check RPC fails due to a network error or Supabase outage, the service must decide whether to block submission entirely (safe but disruptive) or allow submission to proceed silently (functional but risks data duplication). An incorrect choice leads to either user frustration or data quality issues.

Mitigation & Contingency

Mitigation: Define an explicit error policy in the service: RPC failures result in a DuplicateCheckResult with status: 'check_failed' and no candidates. The caller treats this as 'allow submission, flag for async review'. Document this as the intended graceful degradation behaviour in the service interface contract.

Contingency: If stakeholders require blocking on RPC failure, expose a configurable `failMode` parameter in the service that can be toggled per organisation via the feature flag system without a code deployment.

medium impact medium prob scope

The DuplicateComparisonPanel must handle varying activity schemas across organisations (NHF, HLF, Blindeforbundet each have different activity fields). A rigid layout may not accommodate all field variations, causing truncation or missing data in the comparison view.

Mitigation & Contingency

Mitigation: Design the panel to render a dynamic list of key-value pairs rather than a fixed-column layout. Define a `ComparisonField` model that each service populates with only the fields relevant to the activity type and organisation, allowing the panel to adapt without schema knowledge.

Contingency: If dynamic rendering proves too complex within the timeline, ship a simplified panel showing only the five most critical fields (peer mentor, activity type, date, chapter, submitter) and log a follow-up ticket for full field rendering in a later sprint.