critical priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

ProxyContactListProvider extends AsyncNotifier<List<PeerMentorContact>> and is registered as a Riverpod provider
On build(), provider fetches peer mentors assigned to the authenticated coordinator's active chapter from Supabase and returns a typed list
Each row is mapped to a PeerMentorContact value object with fields: id (String), displayName (String), avatarUrl (String?), status (PeerMentorStatus enum)
If the authenticated user is not a coordinator, the provider returns an empty list (does not throw)
RLS enforcement is verified: a coordinator in chapter A cannot see peer mentors from chapter B even if the Dart query is manipulated
Provider state is AsyncData on success, AsyncError on Supabase failure, AsyncLoading during fetch
PeerMentorContact is an immutable value object (final fields, == and hashCode based on id)
Provider is accessible via ref.watch(proxyContactListProvider) in widget tests
Supabase query selects only required columns (no SELECT *) to minimize data transfer

Technical Requirements

frameworks
Dart
Riverpod (AsyncNotifier)
Supabase Dart client
apis
Supabase PostgREST — query peer_mentor_assignments or equivalent join table
data models
PeerMentorContact (Dart value object)
user_roles
coordinator_assignments
auth.users / profiles
performance requirements
Initial fetch must complete in < 1 second on a stable connection for rosters up to 500 peer mentors
Query must use indexed columns only (coordinator_id, chapter_id) — no full table scans
security requirements
Chapter scope filter is enforced by RLS, not by Dart-side filtering — do not rely solely on client-side filtering
Avatar URLs must not expose PII beyond a display name and profile image
Provider must not cache raw Supabase session tokens

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Define PeerMentorContact in lib/domain/models/ as a freezed or plain Dart value object — avoid using database row maps directly in the UI layer. The Supabase query should join from coordinator_assignments to profiles (or equivalent) in a single call rather than two sequential queries. Use .select('id, display_name, avatar_url, status') to be explicit. If the project already has a contacts repository pattern, consider whether this provider should delegate to it or query Supabase directly — prefer consistency with existing patterns.

Add a TODO comment linking to task-005 for the caching layer that will be added next.

Testing Requirements

Write flutter_test unit tests with a mocked Supabase client: (1) returns correct PeerMentorContact list from mock response, (2) returns empty list when user is not a coordinator, (3) emits AsyncError when Supabase throws PostgrestException, (4) maps avatarUrl to null when column is absent. Write one widget test that wraps a consumer widget with ProviderScope overriding the provider, verifying AsyncData renders the contact list. Write one integration test against a local Supabase instance seeded with two coordinators in different chapters, confirming each sees only their own roster.

Component
Proxy Contact List Provider
data low
Epic Risks (2)
high impact medium prob security

Adding recorded_by_user_id to the activities table and writing correct RLS policies is error-prone: overly permissive policies would allow coordinators to record activities under arbitrary user IDs they do not manage, while overly restrictive policies would silently block valid proxy inserts. A policy defect here would either create a security vulnerability or break the entire proxy feature at runtime.

Mitigation & Contingency

Mitigation: Write RLS policies in a local Supabase emulator first. Include policy unit tests using pg_tap or supabase test helpers. Have a second reviewer check the migration SQL before merging. Explicitly test the three cases: coordinator inserting for their own mentors (should succeed), coordinator inserting for another chapter's mentors (should fail), peer mentor inserting for themselves (should succeed as before).

Contingency: If a policy defect is discovered in staging, roll back the migration with a down-migration script. Delay feature release until the policy is corrected and re-verified. Apply a feature flag to keep the proxy entry point hidden from coordinators until the fix is confirmed.

high impact low prob technical

The insert_bulk_activities RPC must behave atomically — a failure on row 7 of 12 must roll back rows 1–6. If Supabase's RPC transaction handling is misconfigured or if network interruptions cause partial acknowledgements, some peer mentors could receive duplicate or missing activity records, directly corrupting Bufdir statistics for the coordinator's chapter.

Mitigation & Contingency

Mitigation: Implement the RPC as a PostgreSQL function with explicit BEGIN/EXCEPTION/END block to guarantee atomicity. Add an integration test that inserts a batch where one row violates a unique constraint and asserts zero rows are committed. Document the transaction semantics in code comments.

Contingency: If atomicity cannot be guaranteed via RPC (e.g., due to Supabase plan limitations), fall back to a sequential insert loop with a compensating DELETE in case of partial failure, and surface a clear error to the coordinator listing which mentors failed and which succeeded.