high priority medium complexity api pending api specialist Tier 1

Acceptance Criteria

AutoApprovalEdgeFunctionClient exposes: evaluateClaim(String claimId) → Future<AutoApprovalResult>
AutoApprovalResult is a sealed class with variants: AutoApproved(approvedAt: DateTime), RequiresManualReview(reason: String), Rejected(reason: String), EvaluationFailed(error: AutoApprovalError)
The JWT from the active Supabase session is attached as the Authorization: Bearer header automatically via supabase_flutter's functions client
On HTTP 200, the response JSON is parsed into AutoApprovalResult; any missing required field throws a typed ParseException
On HTTP 4xx (client error), the method returns EvaluationFailed with AutoApprovalError.clientError — no retry is attempted
On HTTP 5xx or network timeout, exponential backoff retry is performed: attempt 1 after 1s, attempt 2 after 2s, attempt 3 after 4s, then EvaluationFailed(AutoApprovalError.serverError)
Retry attempts are cancellable — if the user navigates away, in-progress retries are cancelled via a CancellationToken or Dart CancelToken pattern
The client does not read or validate threshold rules itself — it delegates entirely to the edge function (single source of truth for approval logic is server-side)
evaluateClaim() must complete (including all retries) within 30 seconds; if timeout exceeded, returns EvaluationFailed(AutoApprovalError.timeout)
The client is abstract interface + concrete implementation to allow mocking in BLoC tests
On successful auto-approval response, the client does NOT update the claim status in the database — that is the edge function's responsibility via service role
All requests and responses are logged at debug level (no PII in logs — only claimId and result type)

Technical Requirements

frameworks
Flutter
Riverpod
supabase_flutter (functions client)
apis
Supabase Edge Functions (Deno) — auto-approval evaluator endpoint
Supabase Auth (JWT injection)
data models
claim_event
annual_summary
performance requirements
First call (no retry) must complete within 3 seconds on 4G network
Exponential backoff must not block the UI thread — use async/await with Future.delayed
Total retry budget capped at 30 seconds
security requirements
Service role key is NEVER present in the mobile client — the edge function is the only component with service role access
JWT is attached automatically by supabase_flutter functions client — do not manually construct Authorization headers
claimId is validated as a valid UUID format before sending to prevent injection via malformed IDs
Response body is parsed defensively — unexpected fields ignored, missing required fields cause typed exceptions not unhandled crashes
Supabase Edge Functions validate org context from JWT claims server-side — client cannot pass a different org_id

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Use supabase_flutter's `supabase.functions.invoke('auto-approve-expense', body: {'claim_id': claimId})` — this automatically attaches the JWT and targets the correct project URL. Model the request as a simple JSON envelope: `{claim_id: String}` — keep the mobile client's contract minimal to reduce coupling to edge function internals. For exponential backoff, use a helper `RetryPolicy` class with configurable maxAttempts, baseDelayMs, and multiplier rather than hardcoding delays inline — this makes the policy testable and reusable for other edge function clients. Define the AutoApprovalResult sealed class using Dart 3 sealed classes (not package:freezed) to keep the dependency count low.

The edge function response envelope should follow the convention: `{status: 'auto_approved' | 'manual_review' | 'rejected', approved_at?: ISO8601, reason?: string}` — agree this contract with the backend developer implementing the edge function. Document the expected response schema in a shared `contracts/` folder in the repo.

Testing Requirements

Unit tests (flutter_test + mocktail): mock the Supabase functions client, test AutoApproved response parsing, test RequiresManualReview response parsing, test HTTP 500 retry sequence (verify exactly 3 retries with correct delays using a fake async clock), test HTTP 400 no-retry behaviour, test timeout after 30 seconds, test cancellation mid-retry. Contract test: define the expected edge function request/response JSON schema and validate the client against it — this protects against edge function API changes breaking the mobile client silently. Integration test (optional, against local Supabase with deno serve): submit a real claim that meets auto-approval criteria and verify AutoApproved result is returned. Minimum 85% line coverage on client class.

Component
Auto-Approval Edge Function Client
infrastructure medium
Epic Risks (3)
high impact medium prob security

Row-level security policies for expense claims must correctly scope data to organisation, role (peer mentor sees own claims only, coordinator sees org-wide queue), and claim status. Incorrect RLS can expose claims cross-organisation or prevent coordinators from accessing the attestation queue.

Mitigation & Contingency

Mitigation: Define RLS policies in code-reviewed migration files. Write integration tests that attempt cross-org reads with different JWT roles and assert access denial. Review with a second engineer before merging migrations.

Contingency: If RLS is misconfigured post-deployment, disable the affected policy temporarily and apply a hotfix migration within the same release window. No claim data is exposed publicly due to Supabase project-level auth requirement.

medium impact medium prob technical

The auto-approval Edge Function is triggered server-side on expense insert. Cold-start latency or Edge Function failures can block the submission response and degrade UX, especially on mobile networks.

Mitigation & Contingency

Mitigation: Implement the auto-approval Edge Function client with a timeout and graceful fallback: if no result is received within 5 seconds, treat the claim as 'pending' and poll for the status update via Supabase Realtime. Keep the Edge Function warm with a periodic ping.

Contingency: If Edge Function reliability is unacceptable, move auto-approval evaluation to a database trigger or Postgres function as an interim measure, accepting that threshold configuration changes require a migration rather than a settings update.

medium impact low prob scope

The expense type catalogue and threshold configuration are cached locally for offline use. If an organisation updates their catalogue exclusion rules or thresholds while a peer mentor is offline, the local cache may allow submissions that violate the new policy.

Mitigation & Contingency

Mitigation: Cache entries include a TTL (24 hours). On connectivity restore, refresh cache before allowing new submissions. Server-side validation in the Edge Function and save functions provides a second enforcement layer.

Contingency: If a stale-cache submission passes client validation but fails server validation, surface a clear error message explaining that the expense type rules have been updated and prompt the user to review their selection with the refreshed catalogue.