Integrate duplicate detection with attribution service
epic-proxy-activity-registration-core-services-task-007 — Wire proxy-duplicate-detection-service and activity-attribution-service together so that the attribution service invokes a duplicate pre-check before enforcing attribution fields. If a duplicate is detected, the service returns a typed DuplicateBlockedError containing the full conflict metadata rather than silently overwriting. This integration ensures the two services behave as a coordinated invariant enforcement layer.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
Inject DuplicateDetectionService into AttributionService via constructor (Riverpod Provider). The check must happen before any field mutation so attribution fields are never partially written. Define DuplicateBlockedError as a sealed class or final class in a shared domain errors file to allow exhaustive pattern matching in callers. For batch flow, use Future.wait with individual try/catch per record to collect partial failures without aborting the entire batch prematurely.
Avoid direct Supabase calls inside AttributionService — delegate persistence to the repository layer to keep services testable without network.
Testing Requirements
Unit tests covering: (1) duplicate detected → DuplicateBlockedError thrown with correct metadata, (2) no duplicate → attribution proceeds and returns enriched record, (3) batch with partial duplicates → BatchPartialFailureResult lists per-record outcomes, (4) DuplicateDetectionService throws unexpected error → AttributionService propagates a wrapped ServiceUnavailableError (not swallowed). Mock DuplicateDetectionService to isolate attribution logic. Verify DuplicateDetectionService.check() is called exactly once per record in single and batch flows.
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.
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.