Deploy auto-approval evaluation edge function
epic-travel-expense-registration-foundation-task-009 — Write and deploy the Supabase Edge Function (Deno/TypeScript) that receives a claim payload, loads the organisation's threshold configuration from the database, evaluates distance and receipt rules, records the auto-approval decision with a timestamp and threshold snapshot in expense_claims, and returns a structured response. Include unit tests for threshold boundary conditions.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Implement the function in `supabase/functions/evaluate-auto-approval/index.ts`. Structure: (1) verify JWT and extract org_id, (2) fetch threshold config with a prepared query (cache result), (3) run decision logic in a pure function `evaluateClaim(claim, config): Decision` — this is what unit tests target, (4) if auto-approved, open a Supabase transaction and update expense_claims, (5) return structured response. Keep the decision logic in a separate module `decision-engine.ts` with no Supabase imports so it is trivially unit-testable in Deno without mocks. Store the threshold snapshot as `JSON.stringify(config)` — do not reference the live config row by ID.
The snapshot is the audit record. Use `crypto.randomUUID()` for any new IDs; do not rely on Postgres sequences from the edge function layer. Watch for the HLF rule: '< 50 km AND no receipts = auto-approved' — confirm the exact operator with the product team; implement as a named constant `AUTO_APPROVE_DISTANCE_KM_LIMIT` so it can be overridden per org in future.
Testing Requirements
Unit tests (Deno test runner): threshold boundary matrix (at, below, above threshold; with and without receipts — 6 combinations minimum). Mock the Supabase client to assert exact SQL mutations. Integration test: deploy to local Supabase emulator, submit a real claim payload, assert database state after function returns. Test idempotency by calling the function twice.
Test error paths: missing fields, wrong org, expired JWT. Aim for 100% branch coverage of the decision logic. CI pipeline must run unit tests on every PR; integration tests run on merge to main.
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.
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.
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.