critical priority high complexity integration pending backend specialist Tier 4

Acceptance Criteria

Edge function calls BadgeEvaluationService.evaluate(peer_mentor_id, org_id) exactly once per invocation and awaits the result before proceeding
The returned list of newly satisfied badge IDs is iterated and each ID is passed individually to BadgeAwardService.award(badge_id, peer_mentor_id, org_id)
Each award call is executed atomically — a failure awarding one badge does not prevent awarding other badges in the same batch
If BadgeEvaluationService throws or rejects, the edge function logs the error, returns 500, and does not invoke BadgeAwardService
If a specific BadgeAwardService.award call fails (e.g. duplicate-award constraint), the error is caught per-badge and logged without halting remaining awards
When BadgeEvaluationService returns an empty list (no new badges), the function returns 200 with body { awarded: [] } and does not call BadgeAwardService at all
The edge function response body on success includes { awarded: [badge_id, ...] } matching the IDs actually processed by BadgeAwardService
org_id is propagated to both BadgeEvaluationService and BadgeAwardService calls — never inferred from a secondary source
Total pipeline execution time (evaluation + all awards) must complete within 10 seconds to stay within Supabase Edge Function timeout
Concurrent invocations for different peer_mentor_ids do not interfere with each other (no shared mutable state between requests)

Technical Requirements

frameworks
Supabase Edge Functions (Deno runtime)
Supabase JS client v2 (for DB access inside services)
apis
BadgeEvaluationService internal API
BadgeAwardService internal API
Supabase PostgreSQL (via service role client)
data models
badge_definition
activity
assignment
annual_summary
performance requirements
Full evaluation + award pipeline must complete within 10 seconds per invocation
BadgeEvaluationService must complete criteria checks within 5 seconds for a mentor with up to 500 activities
Award processing for up to 10 badges in a single invocation must complete within 3 seconds total
security requirements
org_id must be validated against the peer mentor's actual organization in the database before evaluation proceeds
BadgeAwardService must enforce a unique constraint check server-side to prevent duplicate badge awards regardless of race conditions
Service role client used inside services must not be passed to or accessible from untrusted code paths
No badge_id or peer_mentor_id may appear in error messages returned to the webhook caller

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Import BadgeEvaluationService and BadgeAwardService as ES modules within the Deno function — ensure imports use URL-based or relative paths compatible with Deno. Wrap the per-badge award loop in individual try/catch blocks, collecting results into an `awarded: string[]` and `failed: string[]` array. Return both arrays in the response body for observability. Use `Promise.allSettled` if awards are safe to parallelize, otherwise use sequential `for...of` to avoid overwhelming the DB connection pool.

Validate that peer_mentor's organization_id from the database matches the org_id from the payload before calling any service — this is a critical multi-tenant guard. Consider a short-circuit: if peer mentor record does not exist, return 404 immediately. Document the expected service interfaces (method signatures, return types) in a shared types file so both services and the edge function agree on the contract.

Testing Requirements

Integration tests (with a real or emulated Supabase instance): (1) evaluation returns 2 badge IDs → assert BadgeAwardService called twice and response body contains both IDs, (2) evaluation returns empty list → assert BadgeAwardService never called and response is 200 with { awarded: [] }, (3) BadgeEvaluationService throws → assert 500 returned and BadgeAwardService not called, (4) first award succeeds, second award throws duplicate-key error → assert first badge awarded, error logged, function still returns 200 with partial awarded list, (5) cross-tenant test: peer_mentor_id belongs to org_A but org_id in payload is org_B → assert 400 or 403 with no evaluation performed. Mock BadgeEvaluationService and BadgeAwardService in unit tests using Deno's mock utilities.

Component
Badge Criteria Edge Function
infrastructure medium
Epic Risks (3)
medium impact medium prob technical

Supabase Edge Functions may experience cold start latency of 500ms–2s when they have not been invoked recently. If evaluation latency consistently exceeds the 2-second UI expectation, the celebration overlay timing SLA cannot be met without the optimistic UI fallback from the UI epic.

Mitigation & Contingency

Mitigation: Keep the edge function warm by scheduling a lightweight health-check invocation every 5 minutes in production. Optimise the function size to minimise Deno module load time. Implement the optimistic UI path in badge-bloc (from the UI epic) as the primary UX path so cold start only affects server-side reconciliation, not perceived responsiveness.

Contingency: If cold starts remain problematic, migrate badge evaluation to a Supabase database function (pl/pgsql) triggered directly by a database trigger on activity insert, eliminating the Edge Function overhead entirely for the evaluation logic while keeping Edge Function only for FCM notification dispatch.

high impact low prob integration

Supabase database webhooks can fail silently if the edge function returns a non-2xx response or times out. A missed webhook means a peer mentor does not receive a badge they earned, which is both a functional defect and a trust issue for organisations relying on milestone tracking.

Mitigation & Contingency

Mitigation: Implement idempotent webhook processing: the edge function reads the activity ID from the webhook payload and checks whether evaluation for this activity has already run (via an audit log query) before proceeding. Add Supabase webhook retry configuration (3 retries with exponential backoff). Monitor webhook failure rates via Supabase logs alert.

Contingency: Implement a nightly reconciliation job (Supabase scheduled function) that scans all activities from the past 24 hours, re-evaluates badge criteria for any peer mentor with no corresponding evaluation log entry, and awards any missing badges. Alert operations if reconciliation awards more than 5% of badges, indicating systematic webhook failure.

high impact low prob security

The evaluation service loads badge definitions per organisation, but a misconfigured RLS policy or incorrect organisation scoping in the edge function could cause one organisation's badge criteria to be evaluated against another organisation's peer mentor activity data, leading to incorrect or cross-contaminated badge awards.

Mitigation & Contingency

Mitigation: The edge function must extract organisation_id from the webhook payload activity record and pass it explicitly to every database query. Write a security test that seeds two organisations with distinct badge definitions and verifies that evaluating a peer mentor in org A never reads or awards org B definitions. Use Supabase service role key only within the edge function, never the anon key.

Contingency: If cross-org contamination is detected in audit logs, immediately disable the edge function webhook, run a targeted SQL query to identify and revoke incorrectly awarded badges, notify affected organisations, and perform a full security review of all RLS policies on badge-related tables before re-enabling.