high priority low complexity testing pending testing specialist Tier 3

Acceptance Criteria

recordDecision() inserts a row containing actor_id matching the authenticated coordinator's user ID
threshold_at_decision field captured from the ThresholdEvaluationResult at the moment of the call, not from a later lookup
decided_at is set to the current UTC timestamp at insertion time (within 1 s tolerance in tests)
Attempting to record a decision for a claim outside the coordinator's org scope throws a ClaimScopeViolationException
Duplicate decision insertion (same claim_id + actor_id already approved) returns a typed DuplicateDecisionError, not an unhandled exception
Database constraint violation (e.g., NOT NULL on claim_id) surfaces as a typed RepositoryException with the original cause attached
All three required audit fields (actor_id, threshold_at_decision, decided_at) are present and non-null on every inserted record verified via mock capture
Unit tests do not make real Supabase calls — all DB interactions are mocked

Technical Requirements

frameworks
flutter_test
mocktail
apis
Supabase PostgREST (claim_approvals table insert)
data models
ClaimApprovalRecord
CoordinatorScope
ThresholdEvaluationResult
ClaimScopeViolationException
RepositoryException
performance requirements
Unit tests complete in under 300ms — no real DB calls
security requirements
actor_id must be sourced from the authenticated session, never from caller-supplied input
Scope check must run before insert — never rely on DB constraint alone for access control

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Inject a clock abstraction (e.g., `DateTime Function() clock = DateTime.now`) into ClaimApprovalRepository so tests can supply a fixed timestamp and assert decided_at exactly. Scope validation: before insert, query coordinator_scopes table (mocked in tests) to confirm the claim's org_id is in the coordinator's allowed scope list. Use a dedicated ClaimApprovalRecord value object to build the insert payload — avoids Map typos and makes payload assertions in tests type-safe. This audit pattern is critical for Bufdir compliance (task-018) and must not be skipped.

Testing Requirements

Unit tests with mocktail mocking the Supabase client's from().insert() chain. Use an ArgumentCaptor pattern (captured() in mocktail) to assert the exact payload passed to insert() contains all three required audit fields. Group tests: (1) successful insertion — assert payload shape, (2) scope violation — assert exception type and message, (3) duplicate decision — assert typed error returned, (4) DB constraint violation — assert RepositoryException wrapping. Use a fixed DateTime in tests (clock injection) so decided_at assertions are deterministic.

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.