high priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

getProxyRegisteredByCoordinator(coordinatorId, DateRange) returns all activity records where registered_by = coordinatorId AND attributed_to != coordinatorId AND activity_date is within the date range (inclusive on both ends)
getSelfRegisteredByMentor(mentorId, DateRange) returns all activity records where registered_by = mentorId AND attributed_to = mentorId AND activity_date is within the date range (inclusive on both ends)
Both methods return List<AttributionRecord> with all fields populated from the database
Both methods return empty list when no records match — no exception for empty results
DateRange with null from-date defaults to beginning of current year; null to-date defaults to today — documented in method signature
Voided/cancelled records are excluded from both query results
Both queries complete in under 1 second for date ranges up to 12 months with up to 500 records
getProxyRegisteredByCoordinator results can be used directly for Bufdir export: every returned record represents an activity the coordinator submitted on behalf of a peer mentor
getSelfRegisteredByMentor results correctly exclude any records the coordinator submitted for that mentor (attributed_to = mentorId but registered_by != mentorId are excluded)
Both methods are accessible only to users with coordinator or admin role — unauthorized callers receive an empty list or an AuthorizationException based on RLS policy

Technical Requirements

frameworks
Flutter
Dart
Riverpod
apis
Supabase PostgREST REST API
Supabase Flutter SDK
data models
AttributionRecord
ProxyAuditQueryFilter
ActivityRecord
CoordinatorUser
PeerMentor
performance requirements
Each query uses indexed columns: registered_by, attributed_to, activity_date
Pagination support (limit/offset) for result sets exceeding 200 records
Date range filtering applied in SQL, not post-fetch in Dart
Single Supabase query per method call
security requirements
getProxyRegisteredByCoordinator must verify the caller is authorized to view the specified coordinatorId's records — coordinators can only query their own records; admins can query any
getSelfRegisteredByMentor must verify the caller is authorized to view the specified mentorId's records — coordinators can query their own mentors; peer mentors can query their own records only
Results must never include records from other organizations — organization_id filter applied in all queries
Do not expose the full activity record content in error messages

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 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

Both methods are read-only query helpers on SupabaseActivityAttributionService. For getProxyRegisteredByCoordinator: query activities where registered_by = coordinatorId AND attributed_to != coordinatorId AND activity_date >= dateRange.from AND activity_date <= dateRange.to AND status != 'voided'. For getSelfRegisteredByMentor: query where registered_by = mentorId AND attributed_to = mentorId AND same date/status filters. Consider adding an overloaded variant accepting ProxyAuditQueryFilter for more complex filtering (used by Bufdir export).

For Bufdir compliance specifically, the distinction is: proxy records = coordinator registered on behalf of mentor (registered_by != attributed_to), self-registered = mentor logged their own activity (registered_by == attributed_to). Expose both as Riverpod FutureProviders with the coordinatorId/mentorId and DateRange as family parameters so the coordinator stats BLoC can subscribe reactively. Pagination: add optional page and pageSize parameters with defaults of page=0 and pageSize=50.

Testing Requirements

Unit tests with mocked Supabase client: coordinator with proxy records in range, coordinator with no records in range, date range boundary (inclusive), voided records excluded, self-registered records correctly excluded from proxy query, mentor self-registered records correctly include only self-attributed entries, null date range defaults applied correctly, pagination returns correct page. Integration test against Supabase test project with pre-seeded mixed record set (proxy and self-registered) verifying correct split between both methods. flutter_test with mocktail. 85%+ branch coverage.

Also manually validate that the results of both methods sum to total non-voided records for a given date range (completeness check).

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.