Scaffold ThresholdValidationEdgeFunction Supabase Edge Function
epic-expense-approval-workflow-core-logic-task-009 — Create the Supabase Edge Function project structure for ThresholdValidationEdgeFunction using Deno and TypeScript. Configure the function entry point, request/response type definitions, Supabase client initialisation, RLS-bypassing service role access, and the deployment configuration in supabase/functions/threshold-validation/.
Acceptance Criteria
Technical Requirements
Implementation Notes
Follow the standard Supabase Edge Function boilerplate: `import { serve } from 'https://deno.land/std/http/server.ts'` and `import { createClient } from 'https://esm.sh/@supabase/supabase-js'`. Use `createClient(url, serviceRoleKey, { auth: { persistSession: false } })` for the RLS-bypassing admin client. Additionally create a per-request user-scoped client from the incoming JWT for operations that should respect RLS.
Structure the handler to: (1) parse and validate JWT, (2) extract org context, (3) parse and validate request body, (4) call business logic (stub for now), (5) return typed response. Keep all environment variable access at the top of the file in a single config object for easy auditing. Add a README.md inside the function directory documenting the expected request/response contract.
Testing Requirements
Manual smoke test: deploy to Supabase staging environment and POST a valid request with a real JWT — verify 200 response with placeholder data. POST without Authorization header — verify 401. POST with malformed JSON body — verify 400 with descriptive error. Add a Deno unit test file (threshold_validation_test.ts) that tests the request parsing and JWT extraction logic with mocked Supabase client.
This test file runs in CI via `deno test`.
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.