critical priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

enforceAttribution(AttributionRecord) validates that registered_by is non-null and non-empty, throwing AttributionValidationError with field_name='registered_by' if missing
enforceAttribution validates that attributed_to is non-null and non-empty, throwing AttributionValidationError with field_name='attributed_to' if missing
Both validations run before any Supabase persistence call is attempted
On successful validation, the method persists the record to Supabase with registered_by and attributed_to correctly populated
The persisted record's registered_by is always the coordinator's current Supabase auth user ID — the service reads this from the auth session, not from caller input, preventing spoofing
Returns the persisted AttributionRecord with the generated activity_id and created_at fields populated
If Supabase persistence fails, a typed AttributionPersistenceException is thrown wrapping the Supabase error
The service does not accept a record where attributed_to equals registered_by (coordinator attributing to themselves is invalid for proxy entries) — throws AttributionValidationError with reason='self_attribution_not_allowed'
Audit log entry is created in a separate audit table (or via Supabase trigger) capturing coordinator_id, mentor_id, activity_id, and timestamp

Technical Requirements

frameworks
Flutter
Dart
Riverpod
apis
Supabase PostgREST REST API
Supabase Auth API
Supabase Flutter SDK
data models
AttributionRecord
ProxyActivityEntry
CoordinatorUser
PeerMentor
performance requirements
Validation is synchronous and completes in under 1ms
Single Supabase insert call for persistence — no pre-fetch or post-fetch queries
Total method execution under 800ms on 4G network
security requirements
registered_by MUST be sourced from supabase.auth.currentUser.id — never trust caller-provided coordinator ID
RLS policy on activities table must enforce that a coordinator can only insert records where registered_by equals their own user ID
attributed_to must be validated as a real peer mentor ID in the coordinator's organization — query user_profiles or equivalent table to confirm role before inserting
Prevent coordinators from attributing activities to users in other organizations

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Dependents (28)
epic-proxy-activity-registration-orchestration-task-001 component Cross-Epic Component proxy-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-orchestration-task-002 component Cross-Epic Component proxy-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-orchestration-task-003 component Cross-Epic Component proxy-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-orchestration-task-004 component Cross-Epic Component proxy-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-orchestration-task-005 component Cross-Epic Component proxy-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-orchestration-task-006 component Cross-Epic Component bulk-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-orchestration-task-007 component Cross-Epic Component bulk-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-orchestration-task-008 component Cross-Epic Component bulk-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-orchestration-task-009 component Cross-Epic Component bulk-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-orchestration-task-010 component Cross-Epic Component proxy-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-orchestration-task-011 component Cross-Epic Component bulk-registration-service depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-001 component Cross-Epic Component bulk-participant-list depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-002 component Cross-Epic Component bulk-participant-list depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-003 component Cross-Epic Component bulk-participant-list depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-004 component Cross-Epic Component duplicate-warning-dialog depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-005 component Cross-Epic Component duplicate-warning-dialog depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-006 component Cross-Epic Component duplicate-warning-dialog depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-007 component Cross-Epic Component proxy-peer-mentor-selector depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-008 component Cross-Epic Component proxy-peer-mentor-selector depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-009 component Cross-Epic Component proxy-peer-mentor-selector depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-010 component Cross-Epic Component proxy-activity-form depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-011 component Cross-Epic Component proxy-activity-form depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-012 component Cross-Epic Component proxy-registration-screen depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-013 component Cross-Epic Component proxy-registration-screen depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-014 component Cross-Epic Component proxy-registration-screen depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-015 component Cross-Epic Component bulk-registration-screen depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-016 component Cross-Epic Component bulk-registration-screen depends on activity-attribution-service
epic-proxy-activity-registration-ui-task-017 component Cross-Epic Component bulk-registration-screen depends on activity-attribution-service

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.

Component
Activity Attribution Service
service low
Epic Risks (2)
medium impact medium prob scope

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.

high impact low prob security

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.