high priority low complexity backend pending backend specialist Tier 2

Acceptance Criteria

After the first successful fetch, subsequent calls to the provider within the same app session return the cached list without making a Supabase request
invalidateCache() method is exposed on the notifier and clears the in-memory cache, causing the next build()/fetch to go to Supabase
Cache is scoped to the authenticated session: when the user logs out and a new session begins, the cache is automatically cleared
A debug-mode log line is emitted on cache hit ('ProxyContactListProvider: cache hit') and on cache miss ('ProxyContactListProvider: cache miss, fetching from Supabase') — suppressed in release builds
The cache does not persist across app restarts (in-memory only, not SharedPreferences or Hive)
Calling invalidateCache() followed by a provider watch triggers a fresh Supabase fetch and emits AsyncLoading then AsyncData
Unit tests can override the cache state to simulate both hit and miss scenarios
No changes to PeerMentorContact value object or provider public API beyond the new invalidateCache() method

Technical Requirements

frameworks
Dart
Riverpod (AsyncNotifier)
apis
Supabase Dart client (only on cache miss)
data models
PeerMentorContact (Dart value object)
performance requirements
Cache lookup must be O(1) — use a simple nullable List<PeerMentorContact>? field
No additional async operations on cache hit path
security requirements
Cache must be invalidated on auth state change (sign-out) to prevent data leakage between users on shared devices
Cached data must not be serialized to disk

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Implement the cache as a private nullable field `List? _cachedContacts` inside the AsyncNotifier class. In build(), check if `_cachedContacts != null` and return it immediately if so. To tie cache invalidation to sign-out, listen to Supabase's `auth.onAuthStateChange` stream in the notifier's constructor or via a Riverpod listener — clear `_cachedContacts` on `AuthChangeEvent.signedOut`.

Keep the cache implementation trivially simple: this is not a generic cache layer, it is a single-use optimization. If the project adds roster mutation features later, document that those features must call `invalidateCache()` after successful writes.

Testing Requirements

Write flutter_test unit tests: (1) second call returns cached list and mock Supabase client is called exactly once, (2) invalidateCache() followed by a fetch calls Supabase again (mock called twice total), (3) simulated sign-out clears cache and next fetch goes to Supabase, (4) cache miss log is emitted on first call and cache hit log on second call (capture log output in test). All existing task-004 tests must continue to pass without modification.

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.