critical priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

enforceAttributionBatch(List<AttributionRecord>) returns a BatchAttributionResult containing succeeded (List<AttributionRecord>) and failed (List<BatchAttributionFailure>) lists
BatchAttributionFailure contains the original record and the AttributionValidationError or AttributionPersistenceException that caused it to fail
Validation failures on individual records do not abort the batch — remaining valid records are still processed
registered_by is overridden from the auth session for every record in the batch (same security guarantee as single record)
Valid records are persisted in a single Supabase batch insert call (not N individual inserts) when all pass validation
If some records fail validation and some pass, only the passing records are included in the single insert call
Empty input list returns a BatchAttributionResult with empty succeeded and failed lists without any network call
If the Supabase batch insert fails entirely (e.g., connection lost), all records are moved to failed with the persistence exception
Batch size limit of 100 records per call enforced with a clear exception
Caller can distinguish validation failures from persistence failures via the exception type in BatchAttributionFailure

Technical Requirements

frameworks
Flutter
Dart
Riverpod
apis
Supabase PostgREST REST API
Supabase Auth API
Supabase Flutter SDK
data models
AttributionRecord
BatchAttributionResult
BatchAttributionFailure
ProxyActivityEntry
performance requirements
Validation phase is synchronous — process all records in memory before any network call
Single Supabase insert for all valid records (bulk insert via .insert(List<Map>))
Batch of 50 records completes in under 2 seconds on 4G network
security requirements
Auth session override applied to every record individually before validation — no batch-level override shortcut that could be bypassed
Self-attribution check applied per-record in batch context same as single record
RLS policies apply to the batch insert identically to individual inserts — no special batch bypass
Batch insert must be wrapped in a Supabase transaction if supported, to avoid partial DB writes on connection failure

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

Implement as an extension of SupabaseActivityAttributionService. The batch method should follow a clear two-phase approach: Phase 1 (synchronous) — iterate all records, override registered_by from auth session, run validation, split into validRecords and failedRecords. Phase 2 (async) — if validRecords is non-empty, call supabase.from('activities').insert(validRecords.map((r) => r.toJson()).toList()). On insert success, populate succeeded with the returned rows.

On insert exception, move all validRecords to failed with the persistence exception. The BatchAttributionResult and BatchAttributionFailure models should be defined in the domain layer as part of task-001 or as a small addendum here. Avoid wrapping the batch insert in a try-catch that silently swallows errors — always surface exceptions via BatchAttributionFailure so callers know what happened.

Testing Requirements

Unit tests: empty batch, all records valid and inserted, all records fail validation, mixed valid/invalid batch (verify correct split), batch size limit exceeded, Supabase insert failure moves all to failed list, auth session override applied to every record. Verify that a valid batch uses exactly one Supabase insert call. Integration test: submit a mixed batch against Supabase test project and verify database state matches succeeded list only. Test that failed records are not partially inserted.

flutter_test with mocktail. 85%+ 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.