high priority medium complexity backend pending backend specialist Tier 1

Acceptance Criteria

TrainingCompletionCriteriaEvaluator implements the shared ICriteriaEvaluator interface established in task-001
Evaluator correctly returns true when a peer mentor has a certification record matching the badge definition's required course ID
Evaluator returns false when no matching certification record exists for the peer mentor
Evaluator returns false when a matching certification exists but has an expired or revoked status
Evaluator handles the case where a peer mentor has zero certification records without throwing an exception
Evaluator accepts aggregated stats (from task-006) rather than issuing its own database queries
Evaluation result is deterministic: same input stats always produces the same boolean output
HLF-specific: expired certificate correctly maps to false, preventing badge award until renewed
Blindeforbundet-specific: RK office honorar completion (3rd and 15th thresholds) is NOT handled here — evaluator only checks training/certification completion status
All evaluation logic is side-effect-free (no database writes, no external calls)

Technical Requirements

frameworks
Supabase Edge Functions (Deno runtime)
Deno standard library
apis
Supabase PostgREST REST API (read-only, via aggregation layer)
data models
peer_mentor_certifications
badge_definitions
PeerMentorStats (in-memory aggregation object)
performance requirements
Evaluation must be purely in-memory after stats aggregation — zero additional DB queries
Single evaluation call must complete in under 1 ms (CPU-only logic)
security requirements
Evaluator must not log raw certification data (contains potentially sensitive health/training records)
Accept only typed PeerMentorStats input — reject untyped or partial objects at the TypeScript type level

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

This evaluator is the simplest of the three evaluator types. Implement as a pure function or a stateless class with a single `evaluate(stats: PeerMentorStats, definition: BadgeDefinition): boolean` method. The badge definition should carry a `required_course_id` (or array thereof) field; the evaluator checks whether `stats.completedCourseIds` contains all required IDs. Use `Set` intersection for multi-course requirements to keep the logic O(n).

Avoid pulling HLF-specific or Blindeforbundet-specific business rules into this class — org-specific logic should live in badge definitions or a separate org-policy layer. Ensure the TypeScript interfaces introduced in task-001 are imported (not redefined) here.

Testing Requirements

Unit tests covered by task-008. During implementation, author accompanying test stubs covering: (1) peer mentor with exactly the required certification and status 'completed', (2) peer mentor with matching course but status 'expired', (3) peer mentor with no certification records at all, (4) peer mentor with multiple certifications where only an unrelated one exists. Tests run with `deno test` in the edge function directory. No integration or e2e tests required for this unit.

Component
Badge Evaluation Service
service high
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.