Implement ClaimStatusAuditTimeline data loading from ClaimEventsRepository
epic-expense-approval-workflow-core-logic-task-013 — Implement the data-loading layer for ClaimStatusAuditTimeline widget. Create a Riverpod provider that fetches the ordered list of ClaimEvent records for a given claim ID from ClaimEventsRepository. The provider must expose loading, error, and data states and deliver events in ascending chronological order.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 5 - 253 tasks
Can start after Tier 4 completes
Implementation Notes
Create `ClaimEventsRepository` as a plain Dart class with a constructor-injected `SupabaseClient`. Keep the repository thin — only Supabase query + JSON deserialization. Do NOT add business logic to the repository. Define the Riverpod provider in a separate `claim_events_provider.dart` file, not inside the widget file.
Use `.family` modifier to parameterize by `claimId`. For `ClaimEvent.fromJson()`, use strict field mapping — do not use dynamic keys. Ensure `created_at` is parsed as `DateTime` in UTC, then converted to local timezone only at the display layer (task-015). Follow the existing Riverpod provider pattern in the codebase for consistency.
Testing Requirements
Unit tests using `flutter_test` with a mocked `ClaimEventsRepository` (using `mocktail` or manual fake). Test cases: (1) provider emits loading then data with events in ascending order; (2) provider emits loading then error when repository throws; (3) provider returns empty list when repository returns []; (4) provider re-fetches after invalidation. Use `ProviderContainer` directly in tests — no widget needed. Assert on `AsyncValue` states using `container.read(claimEventsProvider(testClaimId))`.
All tests must be deterministic — use fixed timestamps in test data.
The ThresholdEvaluationService is described as shared Dart logic used both client-side and in the Edge Function. Supabase Edge Functions run Deno/TypeScript, not Dart, meaning the threshold logic must be maintained in two languages and can diverge, causing the server to reject legitimate client submissions.
Mitigation & Contingency
Mitigation: Implement the threshold logic as a single TypeScript module in the Edge Function and call it via a thin Dart HTTP client wrapper for client-side preview feedback only. The server is always authoritative; the client version is purely for UX (showing the user whether their claim will auto-approve before they submit).
Contingency: If dual-language maintenance is unavoidable, create a shared golden test file (JSON fixtures with inputs and expected outputs) that is run against both implementations in CI to detect divergence immediately.
A peer mentor could double-tap the submit button or a network retry could trigger a duplicate submission, causing the ApprovalWorkflowService to attempt two concurrent state transitions from draft→submitted for the same claim, potentially resulting in two audit events or conflicting statuses.
Mitigation & Contingency
Mitigation: Implement idempotency in the ApprovalWorkflowService using a database-level unique constraint on (claim_id, from_status, to_status) per transition, combined with a UI-level submission lock (disable button after first tap until response returns).
Contingency: Add a deduplication check at the start of every state transition method that returns the existing state if an identical transition is already in progress or completed within the last 10 seconds.
Claims with multiple expense lines (e.g., mileage + parking) must have their combined total evaluated against the threshold. If individual lines are added asynchronously or the evaluation runs before all lines are persisted, the auto-approval decision may be computed on an incomplete set of expense lines.
Mitigation & Contingency
Mitigation: The Edge Function always fetches all expense lines from the database (not from the client payload) before computing the threshold decision. Define a clear claim submission contract that requires all expense lines to be persisted before the submit action is called.
Contingency: Add a validation step in ApprovalWorkflowService that counts expected vs. persisted expense lines before allowing the transition, returning a validation error if lines are missing.