critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

A CriteriaEvaluator<TConfig, TStats> generic interface is defined with a single evaluate(config: TConfig, stats: TStats): Promise<EvaluationResult> method signature
EvaluationResult interface is defined with fields: met (boolean), currentValue (number), requiredValue (number), metadata (Record<string, unknown>) for evaluator-specific debug info
PeerMentorStats interface is defined covering all fields needed by all three evaluator types: activityCount (number), honorarCount (number), activityHistory (ActivityRecord[]), completedTrainingIds (string[])
ActivityRecord interface is defined with fields: activityId (string), completedAt (Date), activityTypeId (string), organizationId (string)
ThresholdCriteriaConfig, StreakCriteriaConfig, and TrainingCompletionCriteriaConfig interfaces are each defined with their specific configuration fields
An abstract base class AbstractCriteriaEvaluator is provided with a protected validate() helper method that throws a typed error if required config fields are missing
All interfaces and the abstract class are exported from a single index.ts barrel file in the evaluator module
TypeScript compilation with strict mode enabled produces zero errors on all interface/type definitions
JSDoc comments describe each interface field with its purpose and valid value range or format

Technical Requirements

frameworks
Deno
TypeScript (strict mode)
apis
Supabase (data access types referenced but not implemented in this task)
data models
PeerMentorStats
ActivityRecord
EvaluationResult
BadgeDefinition (referenced for config shape)
performance requirements
Interface definitions introduce zero runtime overhead — pure TypeScript types only, no class instantiation in this task
security requirements
PeerMentorStats must not include personally identifiable information beyond organizationId and peer mentor ID — no names, addresses, or contact details in the stats contract

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Place all interface files under supabase/functions/_shared/badge-evaluation/ so they are importable by any Edge Function in the project. Use Deno's native TypeScript support — no tsconfig.json is needed for Edge Functions, but add // @ts-check comments if running in a mixed environment. Keep the generic CriteriaEvaluator interface instead of a union type so each evaluator gets full type safety on its specific config without casting. For ActivityRecord.completedAt, use Date (not string) — Supabase returns ISO strings from the database; add a toActivityRecord(row: DatabaseRow): ActivityRecord mapper function in a separate utils file (do not implement in this task, but define the return type here).

Name the barrel file mod.ts following Deno conventions rather than index.ts if the project already uses mod.ts convention — check existing Edge Functions for the pattern in use.

Testing Requirements

Type-level tests using tsd or TypeScript's own type assertion patterns to verify assignability: assert that a valid ThresholdCriteriaConfig satisfies ThresholdCriteriaConfig, that an invalid config (missing required field) causes a compile error. Unit test the AbstractCriteriaEvaluator.validate() helper with a concrete test subclass — verify it throws BadCriteriaConfigError when required fields are absent and does not throw when all fields are present. Run deno test with --check flag to confirm all type exports are valid Deno modules.

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.