critical priority medium complexity backend pending backend specialist Tier 9

Acceptance Criteria

If BulkConflictSummary.conflicts is non-empty, service returns BulkRegistrationConflictsDetected immediately without proceeding to batch insert
BulkRegistrationConflictsDetected carries the full BulkConflictSummary including both conflict and clean participant lists
The coordinator review flow accepts a Set<String> of approved peer_mentor_ids representing the coordinator's explicit approval decision
Before batch insert, participants are filtered to only those whose peer_mentor_id is in the approved set
Participants not in the approved set are silently excluded — no error is raised for exclusions
If the approved set is empty after filtering, BulkRegistrationService returns BulkRegistrationSuccess with committed_count = 0 rather than calling Supabase
The batch insert is only called after the coordinator explicitly provides the approved set — no implicit approvals
Re-submission with an approved set that includes previously conflicting entries must trigger a final re-check before insert (optional but preferred)

Technical Requirements

frameworks
Flutter
BLoC
apis
Supabase RPC (batch insert — single transaction for all approved participants)
data models
BulkConflictSummary
BulkRegistrationResult
BulkRegistrationSuccess
BulkRegistrationConflictsDetected
performance requirements
Filtering approved participants is O(n) — use a Set<String> lookup, not a nested loop
Batch insert must use a single Supabase RPC call (one transaction) — not one INSERT per participant
security requirements
Approved set must only contain peer_mentor_ids from the original BulkRegistrationRequest — prevent injection of arbitrary IDs
Coordinator approval must be tied to an authenticated session — the approved set cannot be submitted by an unauthenticated caller
Supabase RLS must enforce that batch inserts are restricted to the coordinator's organization scope
ui components
Conflict review screen (displays BulkConflictSummary, allows coordinator to select/deselect conflicting entries)
Approval confirmation button (triggers re-submission with approved Set)
Bulk success screen (shows committed_count)

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 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

Model the two-phase flow as two separate public methods on BulkRegistrationService: (1) checkAndPrepare(BulkRegistrationRequest) → BulkRegistrationResult (returns either ConflictsDetected or proceeds to insert if clean); (2) submitApproved(BulkRegistrationRequest, Set approvedMentorIds) → BulkRegistrationResult. This makes the gate explicit in the API surface. The BLoC calls checkAndPrepare first; if it gets ConflictsDetected it shows the review UI and then calls submitApproved with the coordinator's selection. The filtering in submitApproved should be: final approved = request.participants.where((p) => approvedMentorIds.contains(p.peerMentorId)).toList().

Validate that approvedMentorIds is a subset of the original request's peer mentor IDs — reject any IDs not in the original request with an assertion or error result. For the batch insert, use a single Supabase RPC that accepts a JSON array of participant records and inserts them in one transaction — this is critical for Bufdir statistics accuracy (no partial batch counts).

Testing Requirements

Write unit tests using flutter_test with mocked BulkRegistrationRepository and ProxyDuplicateDetectionService. Required scenarios: (1) no conflicts → gate not triggered, proceeds directly to batch insert; (2) conflicts exist → BulkRegistrationConflictsDetected returned, insert not called; (3) coordinator approves subset → only approved participants passed to batch insert; (4) coordinator approves empty set → BulkRegistrationSuccess(committed_count: 0) returned, insert not called; (5) approved set contains ID not in original request → filtered out (security boundary test); (6) batch insert succeeds → BulkRegistrationSuccess with correct committed_count and activity IDs. Integration test: verify Supabase RPC is called exactly once with only the approved participant IDs.

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.