critical priority medium complexity backend pending backend specialist Tier 3

Acceptance Criteria

ContactRepository is a concrete class with a constructor accepting SupabaseClient and ContactRLSQueryBuilder; no static singletons or service locator calls inside the class body
fetchAll() returns Future<List<Contact>> and never returns a non-Contact record type
fetchAll() delegates query construction (table, column selection, filters) entirely to ContactRLSQueryBuilder and does not hardcode table name or filter logic
Network errors and Supabase PostgrestException are caught and rethrown as a typed ContactRepositoryException with a meaningful message and the original error attached
An empty response from Supabase returns an empty list, not null or an exception
PeerMentor records in the response are correctly typed as PeerMentor instances (not Contact) in the returned list, based on the role field
Repository exposes an abstract interface (IContactRepository) so BLoC/Riverpod providers can depend on the abstraction
Dependency injection via Riverpod provider is wired in the providers file; repository can be overridden in tests
The repository does NOT implement caching — that responsibility belongs to a higher layer

Technical Requirements

frameworks
Flutter
Dart
Riverpod
BLoC
apis
Supabase PostgREST client (supabase_flutter package)
data models
Contact
PeerMentor
ContactRLSQueryBuilder
ContactRepositoryException
performance requirements
fetchAll() must not perform N+1 queries — all contact data retrieved in a single Supabase select call
Response mapping (JSON → models) must be synchronous and complete before the Future resolves
security requirements
Repository must NOT add client-side role or org filters as the sole security gate — RLS policies are the authoritative enforcement layer
Client-side filters added by ContactRLSQueryBuilder are for UX correctness only; never log raw query results containing personal data

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Create IContactRepository abstract class in lib/features/contacts/domain/repositories/. Concrete implementation goes in lib/features/contacts/data/repositories/. Use Riverpod's Provider or AsyncNotifierProvider to inject ContactRepository — keep the provider thin (just wires dependencies). The mapping loop should use a helper _mapRecord(Map) that checks the role field and returns either Contact.fromJson or PeerMentor.fromJson — this keeps fetchAll() readable.

Avoid using .execute() deprecated Supabase API; use the current .select().eq() chained API. Wrap the entire async body in try/catch catching both PostgrestException and generic Exception to ensure no unhandled exceptions escape the repository boundary.

Testing Requirements

Unit tests with mocked SupabaseClient and ContactRLSQueryBuilder: (1) fetchAll returns List on success, (2) fetchAll returns List containing PeerMentor instances where role matches, (3) fetchAll returns empty list on empty Supabase response, (4) fetchAll throws ContactRepositoryException on PostgrestException, (5) fetchAll throws ContactRepositoryException on network timeout. Integration test (against local Supabase shadow or test project) verifying: coordinator role query returns only org-scoped contacts.

Component
Contact Repository
data medium
Epic Risks (2)
high impact medium prob security

Existing Supabase RLS policies for the contacts and peer_mentors tables may not align with the application-level UserRole model, causing ContactRLSQueryBuilder to construct filter expressions that are redundant, conflicting, or that allow over-fetching. In a multi-chapter context (NHF), this could expose contacts belonging to other chapters.

Mitigation & Contingency

Mitigation: Audit and document the existing RLS policies against the UserRole enum before writing a single line of query builder code. Write integration tests asserting cross-organization data isolation using separate test user tokens for each role.

Contingency: If RLS policies are misaligned at runtime, add an explicit application-level organization_id equality check in ContactRepository as a secondary guard while the database policies are corrected in a coordinated migration.

medium impact low prob integration

The Supabase schema for contacts and peer_mentors tables may differ from the expected typed models — missing columns, renamed fields, or type mismatches — causing deserialization failures that surface only at runtime during integration testing.

Mitigation & Contingency

Mitigation: Document expected schema fields upfront and validate against the live Supabase schema at sprint start. Use freezed and json_serializable for compile-time-safe deserialization with explicit required/optional field declarations.

Contingency: Introduce nullable fields with safe defaults for any schema mismatches discovered in testing; log deserialization errors to the monitoring service so schema drift is caught before production deployment.