high priority medium complexity testing pending backend specialist Tier 4

Acceptance Criteria

subscribe() call opens exactly one Supabase Realtime channel with the correct channel name pattern (e.g., approval-status:{claimId})
Stream emits an ApprovalStatusEvent within 100ms of a simulated status-change payload arriving on the channel
After simulated disconnect, reconnection is attempted with exponential backoff starting at 1 s, capped at 30 s
After successful reconnection, no duplicate events are delivered for events that already arrived before disconnect
dispose() cancels the channel subscription, closes the stream, and releases all listeners — verified by confirming no further events arrive after dispose()
Subscription does not emit events for claim IDs other than the subscribed one
Error events from the channel are mapped to a typed ApprovalSubscriptionError and surfaced on the stream, not swallowed
All integration tests pass against a mock Realtime channel without requiring a live Supabase project in CI

Technical Requirements

frameworks
flutter_test
mocktail
supabase_flutter
apis
Supabase Realtime Channels API (RealtimeChannel, SupabaseClient.channel())
data models
ApprovalStatusEvent
ApprovalSubscriptionError
RealtimePayload
performance requirements
Channel connection must be established within 2 seconds in test environment
Reconnection backoff must not block the test thread — use fake async timers
security requirements
Channel name must include claimId to prevent cross-claim event leakage
Test environment must use isolated Supabase test project or fully mocked channel — never production

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

RealtimeApprovalSubscription wraps supabase_flutter's RealtimeChannel. Abstract the channel factory behind an interface (e.g., RealtimeChannelFactory) so tests can inject a mock. For reconnection logic, use a StreamController that re-subscribes on channel ERROR/CLOSED events with exponential backoff; use fake_async in tests to advance timers without real waiting. Deduplication after reconnect: track a Set of delivered eventIds and filter on re-emission.

This matches HLF's requirement that coordinators receive exactly one notification per state change. Ensure dispose() calls channel.unsubscribe() and controller.close() in a try/finally block so tests can verify teardown even on error.

Testing Requirements

Integration tests using flutter_test with fake_async for timer control. Mock the Supabase RealtimeChannel interface with mocktail to simulate: (1) normal payload delivery, (2) disconnect event followed by reconnect, (3) duplicate payload delivery post-reconnect. Group tests: (a) happy-path subscription lifecycle, (b) disconnect/reconnect behaviour, (c) dispose and cleanup, (d) error propagation. Use StreamQueue from async package to assert ordered stream emissions.

Verify with verify(mockChannel.unsubscribe()) that dispose() triggers cleanup. Do not use real network I/O in CI — all Supabase interactions must be mocked.

Component
Supabase Realtime Approval Subscription
infrastructure medium
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.