critical priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

SupabaseRoleProvider class is implemented in Dart with a single public method: Future<List<RawRoleRecord>> fetchRolesForCurrentUser()
fetchRolesForCurrentUser() calls supabase.rpc('get_my_roles') and maps the response to a typed List<RawRoleRecord>
On network error (SocketException, TimeoutException), the method retries up to 3 times with exponential backoff (1s, 2s, 4s)
After 3 failed retries, the method throws a RoleProviderException with a descriptive message
On Supabase auth error (e.g., expired session, 401), the method throws a RoleAuthException without retrying
RawRoleRecord is a typed Dart class with fields matching the get_my_roles return columns: id, userId, orgId, orgUnitId, roleName, isActive
RawRoleRecord.fromJson() factory constructor correctly parses all fields from the Supabase RPC response map
SupabaseRoleProvider accepts a SupabaseClient via constructor injection for testability
Unit tests cover happy path, network error with retry exhaustion, and auth error cases
No caching logic is in SupabaseRoleProvider — caching is handled in RoleRepository (task-005)

Technical Requirements

frameworks
Flutter
Supabase Dart SDK
apis
Supabase RPC (get_my_roles)
Supabase Auth
data models
RawRoleRecord
RoleProviderException
RoleAuthException
performance requirements
Single RPC call must complete within 2 seconds on a normal mobile connection
Retry backoff must use exponential delay and not block the UI thread
security requirements
Provider must never log role data to console in production mode
Supabase client must be authenticated before calling the RPC — guard with a null session check and throw RoleAuthException if unauthenticated

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Define an abstract SupabaseRpcClient interface with a single rpc() method to allow mocking in tests without depending on the real Supabase SDK. Implement retry logic as a private _withRetry(Future Function() fn) helper using a for loop with await Future.delayed(). Catch PostgrestException for Supabase-specific errors and check the status code to distinguish auth errors (401) from server errors (500). Use kDebugMode guard around any diagnostic logging.

Keep RawRoleRecord as a plain data class — no business logic. Place this class in lib/data/role/supabase_role_provider.dart following the project's layered directory structure.

Testing Requirements

Write flutter_test unit tests for SupabaseRoleProvider using a mock SupabaseClient. Test cases: (1) successful RPC call returns correctly mapped List; (2) first two calls throw SocketException, third succeeds — verify result is returned after retries; (3) three consecutive SocketExceptions — verify RoleProviderException is thrown; (4) RPC returns a 401/auth error — verify RoleAuthException is thrown without retrying. Use a fake/mock SupabaseClient or extract an abstract interface for the RPC call to enable mocking.

Component
Supabase Role Data Provider
infrastructure low
Epic Risks (2)
medium impact medium prob technical

The get_my_roles RPC call adds a network round-trip immediately after login, potentially increasing the time before the home screen renders. If Supabase RPC is slow or the roles table lacks proper indexing, users with multiple org affiliations could experience noticeable delays.

Mitigation & Contingency

Mitigation: Index user_roles on user_id and org_unit_id. Use JWT claim extraction as the primary fast path; fall back to the RPC only when claims are absent or stale. Set a 3-second timeout with a fallback to cached roles.

Contingency: If RPC latency exceeds acceptable thresholds in production, pre-fetch and embed roles into the session JWT at login time via a Supabase Auth hook, eliminating the post-login RPC entirely.

high impact medium prob integration

Users who belong to multiple organizations (e.g., a coordinator in one NHF chapter who is also a peer mentor in another) may have conflicting role assignments. The repository layer must correctly scope roles to the active organization context set during the organization selection step, or it could return roles from the wrong org.

Mitigation & Contingency

Mitigation: Always filter role queries by the active org_unit_id stored in the tenant session. Write integration tests that simulate multi-org users and verify only the correct org's roles are returned.

Contingency: If org-scoping logic is found to be incorrect during QA, add an explicit org_unit_id parameter to get_my_roles RPC and require the client to always pass the active org context, making the scoping explicit rather than inferred.