high priority medium complexity integration pending integration specialist Tier 1

Acceptance Criteria

BadgeCriteriaIntegration implements RecruitmentMilestoneSubscriber (from task-007) and is registered with ReferralAttributionService on app startup or via DI initialization
On receiving a RecruitmentMilestoneEvent, BadgeCriteriaIntegration queries 555-badge-definition-repository for all badge definitions where criteria_type == 'recruitment'
For each retrieved recruitment badge definition, the service compares event.confirmedCount against the badge's criteria threshold value
When confirmedCount meets or exceeds a badge threshold that the mentor does not already hold, the service triggers badge award logic (e.g. calls a BadgeAwardService or dispatches an event)
The service handles the case where the mentor already holds a badge (idempotent — no duplicate badge award)
The service handles empty badge definitions list gracefully (no-op, no crash)
The service handles errors from the badge definitions repository (logs error, does not rethrow — event processing must not crash ReferralAttributionService)
The service is registered as a Riverpod provider or injected via BLoC/service locator consistent with project DI pattern
All Supabase queries are organization-scoped using the mentor's organization_id derived from the event or auth context
Unit tests cover: badge award triggered when threshold met, no duplicate award when badge already held, no-op when no recruitment badges defined, error from repository is caught and logged
Integration test (with mock Supabase) verifies end-to-end: milestone event in → badge award call out

Technical Requirements

frameworks
Flutter
Riverpod (or BLoC event bus)
Dart
apis
Supabase PostgreSQL 15 (via 555-badge-definition-repository)
Internal BadgeAwardService
data models
badge_definition
assignment
contact
performance requirements
Badge criteria query must be cached per organization for the duration of a session — avoid querying Supabase on every milestone event
onMilestoneReached must be non-blocking — use async/await internally but the interface method should return void (fire-and-forget with internal error handling)
security requirements
All database queries must include organization_id filter derived from server-side auth context, never from client-supplied event data alone
Badge award must be validated server-side (Supabase RLS + Edge Function) — client-side threshold check is an optimization, not the authoritative check
mentorId from event must be validated as a UUID before being used in any query

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Design BadgeCriteriaIntegration as a plain Dart class (not a widget) registered as a Riverpod Provider or equivalent. In onMilestoneReached, immediately return void and run all async logic in an internal _processEvent(event) async method wrapped in try/catch. Cache badge definitions using a Map> keyed by organizationId — invalidate cache on app foreground resume. The threshold comparison logic: retrieve the threshold value from badge_definition.criteria_type metadata (likely stored in a JSON criteria_config field — confirm schema).

Avoid tight coupling to ReferralAttributionService internals — register via the abstract subscriber interface only. Logging: use the project's existing logger (not print()) to record errors and award decisions for auditability.

Testing Requirements

Unit tests (flutter_test with mocktail): (1) when event.confirmedCount >= badge threshold and mentor does not hold badge → BadgeAwardService.award() is called with correct mentorId and badgeId, (2) when mentor already holds the badge → BadgeAwardService.award() is NOT called, (3) when badge definitions repository returns empty list → no award call, no crash, (4) when repository throws an exception → exception is caught, error is logged, no rethrow, (5) when multiple recruitment badges exist with different thresholds → only badges whose threshold is met trigger award. Integration test: mock Supabase responses for badge_definition table query, verify correct RPC or insert call is made when criteria met. Use flutter_test group() to separate unit and integration test suites.

Component
Badge Criteria Integration
service medium
Epic Risks (3)
medium impact high prob dependency

BadgeCriteriaIntegration must reference specific badge definition IDs from the badge-definition-repository for recruitment badges. If those badge definitions have not been created in the database when this epic is implemented, the integration will silently fail to award badges.

Mitigation & Contingency

Mitigation: As the first task of this epic, create the four recruitment badge definitions (seed data migration) with known, stable IDs. BadgeCriteriaIntegration hardcodes these IDs as constants. Include an assertion in the integration tests that verifies the badge definition records exist in the test database.

Contingency: If the badge definitions system does not support seeding at migration time, store the badge definition IDs in a feature-flag-style config table and look them up at runtime, falling back to a no-op with a warning log if they are absent.

medium impact medium prob technical

The coordinator dashboard aggregates referral stats across all peer mentors in an organisation. For large organisations (HLF has many peer mentors nationally), the aggregation query may be slow, causing the dashboard to feel unresponsive.

Mitigation & Contingency

Mitigation: Implement the aggregation as a Supabase database view or RPC that runs server-side with appropriate indexes on (mentor_id, org_id, created_at, event_type). Add a composite index on referral_events during the foundation epic's migration. Cache the result in the Riverpod provider with a 5-minute TTL.

Contingency: If query performance remains unacceptable at scale, materialise the aggregation in a nightly pg_cron job into a stats_cache table, and serve the dashboard from the cache with a 'last updated' timestamp shown to the coordinator.

medium impact low prob integration

The existing badge award service is implemented by the achievement-badges feature. If that feature's public API (BadgeAwardService interface) changes while this epic is in progress, the BadgeCriteriaIntegration will break at compile time or behave incorrectly at runtime.

Mitigation & Contingency

Mitigation: Confirm the BadgeAwardService interface is stable and document the exact method signatures this integration depends on. Write a narrow integration test that constructs the real BadgeAwardService against a test database to detect breaking changes immediately.

Contingency: If the badge service interface changes, adapt the BadgeCriteriaIntegration adapter class to match the new contract. The adapter pattern used here isolates the change to a single class.