high priority medium complexity database pending database specialist Tier 0

Acceptance Criteria

Given a valid ProxyActivity object, when insertActivity() is called, then a single row is inserted in the proxy_activities Supabase table and the created record (with server-assigned ID) is returned
Given a list of ProxyActivity objects (up to 50), when batchInsertActivities() is called, then all records are inserted in a single Supabase upsert call and all created records are returned
Given a coordinatorId, when getActivitiesByCoordinator() is called, then all proxy activities where registered_by equals coordinatorId are returned, ordered by activity_date descending
Given a mentorId, when getActivitiesByMentor() is called, then all proxy activities where attributed_to equals mentorId are returned
Given an activityId and a new ProxyActivityStatus value, when updateStatus() is called, then the status field is updated and no other fields are modified
Given a batch insert of 51 items, when batchInsertActivities() is called, then an ArgumentError is thrown before any Supabase calls (batch size limit enforced client-side)
Given a Supabase RLS violation (coordinator writing activity for a mentor outside their organization), when any write operation is called, then a RepositoryException with code 'unauthorized' is thrown
The ProxyActivity model must include: id, attributedTo (mentorId), registeredBy (coordinatorId), organizationId, activityType, activityDate, durationMinutes, notes (String?), submissionStatus (ProxyActivityStatus), createdAt, updatedAt
ProxyActivityStatus enum must support: draft, submitted, approved, rejected
All write operations (insert, batchInsert, updateStatus) must be unavailable to peer mentor role — enforced by both RLS and application-layer role check

Technical Requirements

frameworks
Flutter
Dart
Riverpod
apis
Supabase REST API — proxy_activities table
Supabase RLS policies
data models
ProxyActivity
ProxyActivityStatus
ActivityType
UserRole
performance requirements
Batch insert of 50 records must complete in under 5 seconds on a standard connection
Query by coordinator must use indexed columns (registered_by, activity_date) — confirm indexes exist in Supabase schema
Status update must be a targeted PATCH to a single row, not a fetch-modify-replace cycle
security requirements
Supabase RLS: INSERT and UPDATE policies must check that auth.uid() matches registered_by AND that the coordinator belongs to the same organization as the mentor (attributed_to)
SELECT policy: coordinators see activities they registered; mentors see only activities attributed to themselves; org admins see all within their org
The notes field may contain sensitive activity details — never log note contents, sanitize before display
Role check must be performed in the application layer (via user's role from auth context) before any write call, as a defense-in-depth layer on top of RLS

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Define `abstract class ProxyActivityRepository` and `SupabaseProxyActivityRepository implements ProxyActivityRepository`. For batchInsertActivities, use Supabase's `.upsert(List> rows)` — this is a single HTTP call regardless of list size. Enforce the 50-item limit with `if (activities.length > 50) throw ArgumentError(...)` at the top of the method. For the role check, inject a `UserRoleService` or read the current user's role from a Riverpod provider — if role is not 'coordinator' or 'org_admin', throw `UnauthorizedException` before making any Supabase call.

Map `ProxyActivityStatus` to/from Supabase string using a static `fromString()` factory. Use `eq('registered_by', coordinatorId)` and `order('activity_date', ascending: false)` in the query builder for getActivitiesByCoordinator. Define the Supabase table name as a constant: `static const _tableName = 'proxy_activities'`. This repository is a foundational dependency for the bulk registration orchestration layer — keep it thin and focused on data access only, with no business logic.

Testing Requirements

Unit tests mocking Supabase client for all operations. Test: (1) single insert maps response to ProxyActivity with correct field mapping, (2) batch insert of 3 items sends single upsert call with all 3 records, (3) batch insert of 51 items throws ArgumentError before Supabase call, (4) getActivitiesByCoordinator returns only records where registered_by matches, (5) getActivitiesByMentor returns only records where attributed_to matches, (6) updateStatus sends PATCH with only status field changed (verify payload), (7) Supabase PostgrestException mapped to RepositoryException with correct code, (8) role check blocks peer mentor from calling insert (throws UnauthorizedException). Minimum 10 test cases. Use flutter_test with Mockito-generated or manual mocks for the Supabase client.

Component
Proxy Activity Repository
data medium
Epic Risks (3)
high impact high prob scope

Partial failures in bulk registration — where some mentors succeed and others fail — create a complex UX state that is easy to mishandle. If the UI does not clearly communicate which records succeeded and which failed, coordinators may re-submit already-saved records (creating duplicates) or miss failed records entirely (creating underreporting).

Mitigation & Contingency

Mitigation: Design the per-mentor result screen as a primary deliverable of this epic, not an afterthought. Use a clear list view with success/failure indicators per mentor name, and offer a 'Retry failed' action that pre-selects only the failed mentors for resubmission.

Contingency: If partial failure UX proves too complex to deliver within scope, implement a simpler all-or-nothing submission mode for the initial release with a clear error message listing which mentors failed, and defer the partial-retry UI to a follow-up sprint.

medium impact medium prob technical

Submitting proxy records for a large group (e.g., 30+ mentors) as individual Supabase inserts may cause latency issues or hit rate limits, degrading the coordinator experience and potentially causing timeout failures that leave data in an inconsistent state.

Mitigation & Contingency

Mitigation: Implement the BulkRegistrationOrchestrator to batch inserts using a Supabase RPC call that accepts an array of proxy records, reducing round-trips to a single network call. Add progress indication using a stream of per-record results if the RPC supports it.

Contingency: If the RPC approach is blocked by Supabase limitations, fall back to chunked parallel inserts (5 records per batch) with retry logic, capping total submission time and surface a progress bar to manage coordinator expectations.

medium impact medium prob technical

Unifying state management for both single and bulk proxy flows in a single BLoC risks state leakage between flows — for example, a previously selected mentor list persisting when a coordinator switches from bulk to single mode — causing confusing UI states or incorrect submissions.

Mitigation & Contingency

Mitigation: Define separate, named state subtrees within the BLoC for single-proxy state and bulk-proxy state, with explicit reset events triggered on flow entry. Write unit tests for state isolation scenarios using the bloc_test package.

Contingency: If unified BLoC state becomes unmanageable, split into two separate BLoCs (ProxySingleRegistrationBLoC and ProxyBulkRegistrationBLoC) sharing only common events via a parent coordinator Cubit.