Define ClaimEvents database schema and Dart models
epic-expense-approval-workflow-foundation-task-001 — Create the Supabase database table for claim_events with immutable audit columns (id, claim_id, event_type, actor_id, metadata, created_at). Define corresponding Dart model classes with fromJson/toJson serialization. Apply RLS policies so only authenticated users can insert; deny UPDATE and DELETE at the database level via policy and constraint.
Acceptance Criteria
Technical Requirements
Implementation Notes
Use Supabase migrations (supabase/migrations/) for schema versioning — never apply schema changes manually in production. The immutability guarantee must be dual-layered: RLS has no UPDATE/DELETE policies (application layer), AND a BEFORE UPDATE/DELETE trigger raises an exception (database layer) as a belt-and-suspenders approach. For ClaimEventType, prefer a Dart enum with a fromString() factory that throws on unknown values rather than returning null — fail loudly for schema drift. The metadata JSONB column is intentionally flexible but document expected keys per event_type in a code comment or companion constants file.
Use const constructors on the Dart model where possible. Avoid using dynamic for metadata in typed contexts — wrap in a ClaimEventMetadata sealed class hierarchy if the metadata shapes are known.
Testing Requirements
Unit tests for ClaimEvent.fromJson() and toJson() covering: full object round-trip, null metadata field, all ClaimEventType enum values, malformed date string throws FormatException. Integration test (flutter_test with Supabase test instance): verify INSERT succeeds for authenticated user, verify UPDATE attempt throws PostgrestException with code 42501, verify DELETE attempt throws PostgrestException with code 42501, verify SELECT returns only events for claims in user's scope. Migration SQL must be tested by running it against a clean Supabase schema and verifying table structure with DESCRIBE or information_schema.
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.
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.
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.