critical priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

ClaimEventsRepository is an abstract interface with a concrete SupabaseClaimEventsRepository implementation
insertEvent(ClaimEvent event) returns Future<ClaimEvent> with the server-assigned id and created_at populated
getEventsForClaim(String claimId) returns Future<List<ClaimEvent>> ordered by created_at ASC
Any method named update*, delete*, or remove* on the repository throws UnsupportedError immediately without making a network call
Network failures (SocketException, TimeoutException) trigger up to 3 retries with exponential backoff (1s, 2s, 4s)
Supabase PostgrestException with code 42501 (permission denied) maps to ClaimEventPermissionException
Supabase PostgrestException with code 23503 (FK violation) maps to ClaimNotFoundDomainException
All other PostgrestExceptions map to ClaimEventRepositoryException with the original error preserved
Repository is injectable and mockable (depends on SupabaseClient via constructor injection)
Unit tests achieve 100% branch coverage on error mapping logic
No direct references to Supabase types leak outside the repository implementation class

Technical Requirements

frameworks
Flutter
Supabase
Riverpod
apis
Supabase REST API — claim_events table
data models
ClaimEvent
ClaimEventType
performance requirements
getEventsForClaim() must complete within 2 seconds on a standard mobile connection
Retry logic must not block the UI — all operations are async
security requirements
SupabaseClient instance must be authenticated before any repository call — throw AuthenticationRequiredException if session is null
Never log event metadata content — only log event_type and claim_id at debug level
actor_id must always be derived from the current auth session, never passed as a parameter

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Pattern: define an abstract ClaimEventsRepository interface in the domain layer; place SupabaseClaimEventsRepository in the data layer. This keeps BLoC/Riverpod providers testable without Supabase. Retry logic should only apply to transient errors (network timeouts, 503s) — never retry 4xx authorization errors, as retrying a permission denied error is wasteful and can mask bugs. Use Dart's Future.delayed with doubling intervals for backoff.

Register the repository with Riverpod as a Provider so it can be overridden in tests. The actor_id injection from auth session should happen inside the repository, not in the calling BLoC, to prevent callers from spoofing actors. Consider a ClaimEventsRepositoryException hierarchy: base class + subclasses for permission, not-found, and generic.

Testing Requirements

Unit tests using flutter_test and a mock SupabaseClient: test insertEvent() success path returns populated ClaimEvent, test insertEvent() with network error retries 3 times then throws, test insertEvent() with permission error throws ClaimEventPermissionException on first attempt (no retry), test getEventsForClaim() returns list ordered by created_at, test that calling any update/delete method throws UnsupportedError synchronously, test exponential backoff timing using fake async. Integration test against Supabase test project: insertEvent() persists and getEventsForClaim() retrieves it, concurrent inserts from two sessions both succeed and return distinct events.

Component
Claim Events Repository
data low
Epic Risks (3)
medium impact medium prob technical

Optimistic locking in ExpenseClaimStatusRepository may produce excessive concurrency exceptions in high-volume coordinator sessions where multiple coordinators process the same queue simultaneously, causing confusing UI errors and coordinator frustration.

Mitigation & Contingency

Mitigation: Design the locking strategy with a short retry window (1-2 automatic retries with 200ms back-off) before surfacing the error to the UI. Document the concurrency model clearly so the UI layer can display a contextual 'claim was already actioned' message rather than a generic error.

Contingency: If contention remains high under load testing, switch to a last-writer-wins update with a conflict notification rather than a hard block, and log all concurrent edits for audit purposes.

medium impact medium prob integration

FCM device tokens stored for peer mentors may be stale (app reinstalled, token rotated) causing push notifications for claim status changes to silently fail, leaving submitters unaware their claim was approved or rejected.

Mitigation & Contingency

Mitigation: Implement token refresh on every app launch and store updated tokens in Supabase. ApprovalNotificationService should fall back to in-app Realtime delivery when FCM returns an invalid-token error and should queue a token refresh request.

Contingency: If FCM delivery rates fall below acceptable thresholds in production monitoring, add a polling fallback in the peer mentor claim list screen that checks status on foreground resume.

high impact low prob dependency

Supabase Realtime has per-project channel and connection limits. If many coordinators and peer mentors are simultaneously subscribed across multiple screens, the project may hit quota limits causing subscription failures.

Mitigation & Contingency

Mitigation: Design RealtimeApprovalSubscription to use a single shared channel per user session rather than per-screen subscriptions. Implement subscription reference counting so channels are only opened once and reused across screens.

Contingency: Upgrade the Supabase plan tier if limits are reached, and implement graceful degradation to polling with a 30-second interval when Realtime is unavailable.