medium priority low complexity backend pending backend specialist Tier 5

Acceptance Criteria

A badge_evaluation_logs table exists in Supabase with columns: id (uuid PK), peer_mentor_id (uuid), org_id (uuid), triggered_at (timestamptz), badges_evaluated (jsonb), criteria_outcomes (jsonb), badges_awarded (uuid[]), error_message (text nullable), duration_ms (integer)
A log record is inserted after every edge function invocation — both successful and failed runs produce a log entry
badges_evaluated field contains the full list of badge_definition IDs that were checked during the evaluation run
criteria_outcomes field contains a JSON object mapping each badge_id to a boolean (true = criteria met, false = not met) for the current run
badges_awarded field contains only the badge IDs newly awarded in this invocation (already-earned badges excluded)
error_message is populated when an exception occurs; it contains a safe descriptive message without stack traces or PII
duration_ms records the total wall-clock time from start of evaluation to log write in milliseconds
Log insertion failures do not cause the edge function to return an error — logging is fire-and-forget after the pipeline completes
Logs are queryable by org_id and peer_mentor_id for operational debugging without requiring full table scans
No personally identifiable information beyond peer_mentor_id and org_id (both UUIDs) is stored in the log table

Technical Requirements

frameworks
Supabase Edge Functions (Deno runtime)
Supabase JS client v2
apis
Supabase PostgreSQL (badge_evaluation_logs table insert)
data models
badge_definition
bufdir_export_audit_log
performance requirements
Log insertion must not block the HTTP response — use fire-and-forget pattern (do not await log insert before responding)
Log insert must complete within 1 second under normal DB load
badge_evaluation_logs table must have indexes on org_id and peer_mentor_id columns for query performance
security requirements
badge_evaluation_logs table must have RLS enabled: coordinators can read logs for their own org_id only, peer mentors cannot read any logs
Service role key used for log insert must never be exposed in log payloads
error_message field must be sanitized — never include raw exception messages that might contain SQL or internal paths
Logs containing criteria_outcomes must not include raw activity content or contact data

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Create the badge_evaluation_logs table via a Supabase migration file, not inline SQL. Use `performance.now()` in Deno at the start and end of the evaluation pipeline to compute duration_ms. Structure the log insert as a dedicated `insertEvaluationLog(log: EvaluationLog)` function imported into the edge function so it can be mocked in tests. Call this function after `res.respond()` or after building the response object — do not block the response on the log write.

If using `EdgeRuntime.waitUntil()` is available in Supabase's Deno environment, use it for the fire-and-forget pattern; otherwise wrap in a void promise with a catch handler. Add database indexes: `CREATE INDEX idx_eval_logs_org ON badge_evaluation_logs(org_id)` and `CREATE INDEX idx_eval_logs_mentor ON badge_evaluation_logs(peer_mentor_id)` in the migration.

Testing Requirements

Unit tests: (1) verify log record fields are correctly populated for a successful run — check badges_evaluated list, criteria_outcomes map, badges_awarded list, and duration_ms is a positive integer, (2) verify error_message is set and badges_awarded is empty when evaluation throws, (3) verify log insert failure does not propagate to function response (mock Supabase client insert to throw and assert function still returns 200). Integration test with real Supabase: run a full evaluation cycle and query badge_evaluation_logs to assert a row was inserted with correct org_id and peer_mentor_id. Verify RLS policy by attempting to read logs as a peer mentor role user and asserting zero rows returned.

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.