Implement activity attribution service - single record
epic-proxy-activity-registration-core-services-task-004 — Implement the core method on activity-attribution-service that takes a proxy activity record and enforces correct field population: registered_by must be set to the acting coordinator's user ID and attributed_to must be set to the target peer mentor's user ID. The service should validate both fields are present and non-null before persisting, throwing a typed AttributionValidationError if either is missing.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Implement as SupabaseActivityAttributionService implementing IActivityAttributionService. Inject Supabase client via constructor. Critical security pattern: always call supabase.auth.currentUser to get the acting coordinator's ID — do not use the registered_by field from the input AttributionRecord. Instead, override it: final enforced = record.copyWith(registeredBy: supabase.auth.currentUser!.id).
This prevents any caller from impersonating another coordinator. The self-attribution check (attributed_to != registered_by) must happen after the auth override, not before, to ensure you're comparing against the real coordinator ID. For the peer mentor ID validation, consider a lightweight Supabase RPC or a select count query against user_profiles where id = attributed_to AND organization_id = coordinator.organization_id AND role = 'peer_mentor'. Keep validation failures fast by throwing early before any network call.
Testing Requirements
Unit tests with mocked Supabase client and mocked auth session: valid attribution succeeds, null registered_by throws AttributionValidationError, null attributed_to throws AttributionValidationError, self-attribution throws correct error, Supabase insert failure throws AttributionPersistenceException, auth session missing throws appropriate error. Test that registered_by is always overwritten from auth session regardless of caller input. Integration test: insert a real proxy record via the service and verify both fields in the database match expected values. flutter_test with mocktail.
90%+ branch coverage.
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.