critical priority low complexity database pending database specialist Tier 0

Acceptance Criteria

BadgeDefinitionRepository exposes: fetchAllDefinitions() → Future<List<BadgeDefinition>> and fetchDefinitionById(String id) → Future<BadgeDefinition?>
BadgeDefinition typed model includes: id, displayName, description, iconKey, tierThresholds (Map<String, int>), criteria (String), and organizationScope (nullable — null means global)
In-memory cache is populated on first call to fetchAllDefinitions and reused on subsequent calls within the same app session
Cache can be explicitly invalidated by calling invalidateCache() — useful after admin updates to badge definitions
When Supabase is unavailable, repository falls back to a bundled JSON asset file (assets/badge_definitions.json) and logs a warning
Fallback asset file is committed to the repository and kept in sync with the Supabase table via a documented process
Unit tests cover: cache hit on second call (no Supabase call made), fallback to asset on network error, correct parsing of all BadgeDefinition fields
Repository registered as a Riverpod provider

Technical Requirements

frameworks
Flutter
Riverpod
apis
Supabase REST API
data models
BadgeDefinition
performance requirements
After cache population, fetchAllDefinitions must return synchronously from memory (< 5ms)
Initial load from Supabase must complete within 3 seconds on a standard mobile connection
Bundled asset fallback must load within 100ms
security requirements
Badge definitions are read-only for peer mentors — no write access via this repository
organizationScope field must be respected: definitions scoped to another organization must not be returned to the calling user

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Use a simple Map in-memory cache keyed by definition ID. Populate on first call with a lock pattern to prevent concurrent initial fetches (use a Completer as a loading gate). The bundled fallback file assets/badge_definitions.json must mirror the Supabase table schema — document this in a comment at the top of the repository class. Supabase table: badge_definitions.

Expected columns: id (text PK), display_name (text), description (text), icon_key (text), tier_thresholds (jsonb), criteria (text), organization_scope (uuid, nullable). Register as a Riverpod Provider. Note: badge definitions are considered relatively static (change rarely); a session-scoped cache is appropriate. If real-time updates are needed in a future iteration, replace with a Supabase stream.

Testing Requirements

Unit tests using flutter_test with mocked Supabase client and mocked AssetBundle. Test: (1) first call fetches from Supabase and populates cache, (2) second call returns cached data without contacting Supabase, (3) invalidateCache() followed by a call re-fetches from Supabase, (4) Supabase error triggers asset fallback and returns non-empty list, (5) fetchDefinitionById returns null for unknown ID without throwing, (6) organizationScope filtering excludes out-of-scope definitions. Verify all fields of BadgeDefinition are correctly parsed from both Supabase JSON and asset JSON.

Component
Badge BLoC
infrastructure medium
Epic Risks (2)
medium impact medium prob integration

The badge-earned-celebration overlay must appear within 2 seconds of the triggering activity being saved, but badge evaluation runs server-side in an edge function triggered by a database webhook. Network latency, edge function cold start, and Supabase Realtime delivery delays could cause the overlay to appear late or not at all, breaking the motivational loop.

Mitigation & Contingency

Mitigation: Implement an optimistic UI path: after activity save, badge-bloc immediately checks whether any badge thresholds are crossed client-side using cached stats and badge definitions, showing the overlay speculatively before server confirmation. The server result then reconciles. Subscribe to Supabase Realtime on the earned_badges table for authoritative confirmation.

Contingency: If Realtime delivery is unreliable in production, add a polling fallback: badge-bloc polls for new earned badges 3 seconds after an activity save and shows the overlay if a new record is detected, accepting up to 5-second latency as a fallback SLA.

high impact low prob technical

The celebration overlay uses animation for positive reinforcement, but motion sensitivity (prefers-reduced-motion) and screen reader users require a non-animated or text-only alternative. Failing to handle this risks excluding Blindeforbundet users or triggering vestibular discomfort for motion-sensitive volunteers.

Mitigation & Contingency

Mitigation: Check MediaQuery.disableAnimations in badge-earned-celebration-overlay and skip animation entirely when true, showing a static card instead. Add an ExcludeSemantics wrapper around the decorative animation widget and a separate Semantics node with a live region announcement of the badge name and congratulatory message.

Contingency: If accessibility issues are identified in TestFlight testing with Blindeforbundet's test group, fast-track a patch that defaults to the static card path and gates the animation behind a user preference setting in notification preferences.