critical priority medium complexity backend pending backend specialist Tier 9

Acceptance Criteria

ActivityAttributionService is called after a clean duplicate check and before any Supabase write
The persisted record contains recorded_by_user_id (coordinator), peer_mentor_id (attributed mentor), and created_at timestamp
ProxyActivityRepository.persist() is called only once per registration flow and only after successful attribution
Supabase constraint violations (e.g. unique index, foreign key) are caught and mapped to ProxyRegistrationError.databaseConstraint
Network/timeout errors from Supabase are caught and mapped to ProxyRegistrationError.networkFailure
On successful persistence, ProxyRegistrationSuccess is returned carrying the newly created activity ID
No coordinator identity data is ever lost — if attribution fails, the entire flow is aborted and an error result is returned
All three fields (coordinator ID, peer mentor ID, timestamp) are verified present via unit test assertions before the repository call

Technical Requirements

frameworks
Flutter
BLoC
apis
Supabase REST API (INSERT on activities table)
Supabase RPC (if batch context needed)
data models
ProxyActivityRecord
ActivityAttribution
ProxyRegistrationResult
ProxyRegistrationError
performance requirements
Single Supabase INSERT per registration — no multi-step round trips for the persist phase
Repository persist call must complete within 3 seconds on a standard mobile connection
security requirements
coordinator_id must be sourced from the authenticated session (Supabase auth.uid()), never from client-supplied input
Row-level security on the activities table must restrict INSERT to coordinators with the correct org scope
Attribution metadata must not be modifiable by the peer mentor role

Execution Context

Execution Tier
Tier 9

Tier 9 - 22 tasks

Can start after Tier 8 completes

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

Implementation Notes

The attribution step should produce an immutable ProxyActivityRecord value object before passing it to the repository — never mutate the record after attribution. Use Dart's freezed package for the record type to enforce immutability. Repository exceptions should be caught at the repository call site with a specific try/catch, not a blanket catch at the top of the method — this preserves stack traces and allows precise error mapping. For the Supabase error mapping, inspect the error code: 23505 = unique_violation → databaseConstraint, others → networkFailure or unknown.

The coordinator ID must be injected from the auth session provider (Riverpod) rather than passed as a parameter from the UI layer, preventing client-side identity spoofing. Ensure the timestamp is set server-side using Supabase's now() default rather than the device clock to prevent timezone inconsistencies critical for Bufdir statistics.

Testing Requirements

Write unit tests using flutter_test with mocked ActivityAttributionService and ProxyActivityRepository. Required scenarios: (1) successful attribution + persistence → ProxyRegistrationSuccess with correct activity ID; (2) attribution service throws → ProxyRegistrationError returned, repository not called; (3) repository throws PostgresException (unique) → mapped to ProxyRegistrationError.databaseConstraint; (4) repository throws network error → mapped to ProxyRegistrationError.networkFailure; (5) persisted record contains all three required fields. Use Mockito or mocktail for mocking. Integration test: verify row appears in Supabase test DB with correct recorded_by_user_id and peer_mentor_id.

Component
Proxy Registration Service
service medium
Epic Risks (3)
high impact low prob integration

If the Supabase batch RPC partial-inserts some records before encountering an error and does not roll back cleanly, the bulk service may report failure while orphaned records exist in the database, corrupting reporting data.

Mitigation & Contingency

Mitigation: Wrap the bulk insert in an explicit Supabase transaction via the RPC function. Write an integration test that simulates a mid-batch constraint violation and asserts zero records were written.

Contingency: If a partial-write incident occurs, the registered_by audit field allows identification and deletion of the orphaned records. Implement a coordinator-facing bulk submission status screen to surface any such anomalies.

medium impact medium prob scope

When a bulk submission of 15 participants has 4 duplicates, the aggregated conflict summary may be too complex for coordinators to process quickly, leading to blanket override decisions that defeat the purpose of duplicate detection.

Mitigation & Contingency

Mitigation: Design the conflict result type to support per-participant override flags, so the UI can present a clear list of conflicting participants with individual cancel/override toggles rather than a single global decision.

Contingency: If coordinator usability testing reveals the conflict review screen is too complex, simplify to a 'skip all conflicts and submit the rest' mode as an immediate fallback while a more granular UI is designed.

high impact low prob security

If the coordinator role check inside proxy-registration-service is inconsistent with the route-level guard, a regression in the guard could allow peer mentors to call the service directly via deep links, submitting records with incorrect attribution.

Mitigation & Contingency

Mitigation: Enforce role authorization at both the route guard level (coordinator-role-guard) and inside each service method independently. Write a security test that calls the service directly with a peer mentor session token and asserts rejection.

Contingency: If a bypass is discovered, immediately enable the server-side RLS policy as the final enforcement layer and audit any records written during the exposure window using the registered_by field.