critical priority high complexity backend pending backend specialist Tier 10

Acceptance Criteria

BulkRegistrationService.registerBulk() applies ActivityAttributionService to every approved participant record before submitting to the repository
All attributed records are submitted in a single ProxyActivityRepository.batchInsert() call — never in a loop of individual inserts
If the Supabase RPC call succeeds, the method returns BulkRegistrationSuccess containing the exact integer count of committed rows
If the Supabase RPC call throws any exception, no rows are persisted and BulkRegistrationError is returned with a mapped user-readable ErrorType enum value
Partial commit is architecturally impossible: the Supabase RPC function is wrapped in a database transaction and the Dart layer never calls batchInsert with subsets
Empty approved list edge case returns BulkRegistrationSuccess with count 0 without calling the repository
All activity records carry both recorded_by_user_id (coordinator) and peer_mentor_id (attributed mentor) as set by ActivityAttributionService
The service does not contain any UI or BLoC logic — it is a pure Dart class with no Flutter imports
Error mapping covers at minimum: network timeout, Supabase PostgrestException, and unexpected runtime exceptions, each mapping to a distinct ErrorType value

Technical Requirements

frameworks
Flutter
BLoC
Dart
apis
Supabase RPC batch-insert endpoint
ProxyActivityRepository.batchInsert()
ActivityAttributionService.attributeRecord()
data models
Activity
BulkRegistrationSuccess
BulkRegistrationError
ActivityAttribution
ParticipantRecord
performance requirements
Single RPC round-trip for the entire batch regardless of participant count
Attribution mapping of N records must complete in O(N) time with no blocking I/O
Service must not hold locks or open transactions on the Dart side — transaction ownership belongs to the Supabase RPC function
security requirements
recorded_by_user_id must be sourced from the authenticated session token, never from caller-supplied input
Supabase RLS policies must enforce that only coordinators can invoke the batch-insert RPC
No PII should be logged in error messages — log error codes and types only

Execution Context

Execution Tier
Tier 10

Tier 10 - 11 tasks

Can start after Tier 9 completes

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

Implementation Notes

Implement BulkRegistrationService as a plain Dart class injected via Riverpod provider. The core method signature should be: `Future registerBulk(List approved, ActivityTemplate template)`. Use a sealed class (Dart 3) for BulkRegistrationResult with BulkRegistrationSuccess and BulkRegistrationError subtypes — this enforces exhaustive handling at call sites. The attribution loop should be a synchronous `.map()` over the approved list since ActivityAttributionService is a pure value transformer with no async I/O.

Pass the resulting list directly to `repository.batchInsert()`. Define a BulkErrorType enum (networkError, permissionDenied, duplicateConflict, unknown) and map PostgrestException status codes to it inside a private `_mapException()` helper. Do not catch errors in the loop — let them propagate to the single top-level try/catch. The Supabase RPC function (defined separately) should use a BEGIN/EXCEPTION/END block to guarantee atomicity at the database level.

Testing Requirements

Unit tests (covered in task-011) must mock ProxyActivityRepository and ActivityAttributionService via mocktail. Integration test (optional, lower priority): spin up a local Supabase instance and verify that a batch of 5 records either all appear or none appear after a simulated mid-insert failure. Test coverage target: 100% of public method branches in BulkRegistrationService. Specific scenarios: full happy path, empty list, repository exception, attribution service throwing mid-loop.

Component
Bulk Registration Service
service high
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.