high priority medium complexity integration pending backend specialist Tier 2

Acceptance Criteria

BadgeCriteriaIntegration calls BadgeAwardService.award(mentorId, badgeDefinitionId) exactly once when a recruitment threshold is crossed
No duplicate badge award is triggered if the mentor already holds the badge — checked via BadgeRepository before calling BadgeAwardService
A success event is emitted on the integration's event stream when BadgeAwardService completes without error
A failure event (with error type, not raw exception) is emitted when BadgeAwardService throws, without crashing the integration
The integration handles concurrent threshold-crossing events idempotently — two rapid events for the same mentor/badge pair result in exactly one award call
mentorId and badgeDefinitionId passed to BadgeAwardService exactly match the values from the triggering recruitment event
Badge criteria evaluation does not proceed if BadgeRepository returns an error — the error is surfaced via the failure event stream
All integration wiring compiles without errors and is covered by the unit tests in task-010

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
BadgeAwardService internal API
BadgeRepository internal API
ReferralAttributionService event stream
data models
badge_definition
assignment
performance requirements
Badge duplicate check must complete within one async round-trip to the repository before award call
Event emission must be non-blocking — award trigger must not delay the recruitment event processing pipeline
security requirements
mentorId must originate from a validated JWT claim — never trust client-supplied mentor identity
BadgeAwardService calls must be scoped to the organisation context derived from the triggering event
No badge state written client-side — all persistence via server-side repository abstractions

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Use a simple check-then-act pattern with repository read before award call. To prevent race conditions in concurrent scenarios, prefer a server-side upsert or unique constraint on (mentor_id, badge_definition_id) in the database as the final guard — the client-side check is a fast-path optimisation, not the sole guard. Emit events via a StreamController exposed on the integration class; use sealed classes for success/failure variants so callers are forced to handle both. Keep BadgeCriteriaIntegration free of UI dependencies — it is a pure domain-layer integration class.

Register it as a Riverpod provider with keepAlive: true so the event subscription persists across screen transitions.

Testing Requirements

Unit tests (task-010) must cover this wiring: mock BadgeAwardService and BadgeRepository, assert award called once on threshold cross, assert award NOT called when badge already exists, assert success/failure events emitted correctly. Integration test should fire a real recruitment event through the full integration stack in a test Supabase environment and confirm badge_definition row is created. Target 100% branch coverage on the duplicate-guard and event-emission paths.

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.