medium priority low complexity testing pending testing specialist Tier 1

Acceptance Criteria

resolveIconPath(badgeType: 'beginner', isEarned: true) returns 'assets/badges/unlocked/beginner.png' (or the exact path defined in the naming convention)
resolveIconPath(badgeType: 'beginner', isEarned: false) returns 'assets/badges/locked/beginner.png'
resolveIconPath for a badge type that does not have an asset file returns the placeholder icon path (e.g., 'assets/badges/placeholder.png') rather than throwing
validateContrastRatio(foreground: Colors.white, background: Color(0xFF003087)) returns true (dark blue background, white text — known WCAG AA pass)
validateContrastRatio(foreground: Color(0xFFCCCCCC), background: Colors.white) returns false (light grey on white — known WCAG AA fail at 4.5:1 threshold)
validateContrastRatio(foreground: Colors.black, background: Colors.white) returns true (21:1 contrast — maximum)
validateContrastRatio computes contrast ratios accurate to within ±0.05 of the reference WCAG formula result
BadgeIconAssetManager initialises without throwing when the Flutter asset manifest contains the expected badge assets
All tests pass without accessing the file system or Flutter asset bundle at runtime (use dependency injection or mocking for asset existence checks)

Technical Requirements

frameworks
Flutter
flutter_test
performance requirements
resolveIconPath must be a synchronous O(1) lookup — no async file I/O in the happy path
validateContrastRatio must be a pure synchronous function with no side effects

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

validateContrastRatio must implement the full WCAG relative luminance formula. For sRGB channels, linearise using: if (c <= 0.04045) c/12.92 else ((c+0.055)/1.055)^2.4. Then L = 0.2126*R + 0.7152*G + 0.0722*B. Contrast = (lighter + 0.05) / (darker + 0.05).

Flutter's Color class stores ARGB as integers — extract channels via color.red / 255.0, color.green / 255.0, color.blue / 255.0. For the asset existence check in resolveIconPath, accept a Set of known asset paths as a constructor parameter (populated from the asset manifest at app startup) so tests can inject a controlled set without touching the real bundle. The fallback-to-placeholder behaviour is critical for robustness: new badge types may be added to the database before their icons are added to the asset bundle, so the fallback prevents runtime image-load errors.

Testing Requirements

Use flutter_test. BadgeIconAssetManager should be designed for testability — inject an asset-existence checker (or an abstract AssetManifest interface) so tests can simulate missing assets without touching the real asset bundle. Test the contrast ratio computation with at least five known colour pairs: white/dark-blue (pass), grey/white (fail), black/white (pass, maximum), black/yellow (pass, ~19:1), and medium-grey/medium-grey (fail). For each pair, compare the computed ratio against a pre-calculated reference value accurate to two decimal places.

Do not use flutter_test's TestWidgetsFlutterBinding unless necessary — plain unit tests are sufficient for pure logic like contrast computation.

Component
Badge Icon Asset Manager
infrastructure low
Epic Risks (2)
high impact medium prob scope

Badge criteria are stored as structured JSON in badge_definitions. If the JSON schema for criteria (threshold counts, streak lengths, training completion flags) is not well-defined upfront, the evaluation service will be built against a moving target, requiring costly migrations and refactors.

Mitigation & Contingency

Mitigation: Define and document the criteria JSON schema in a shared type file before any repository code is written. Review the schema with all three organisations' badge requirements — especially Blindeforbundet's honorar thresholds — and version the JSON schema using a 'criteria_version' field from day one.

Contingency: If the criteria schema must change after services are built, write a Supabase migration to backfill existing rows and add a migration version column. Keep the evaluation service criteria parser isolated behind an interface so only one function needs updating.

medium impact medium prob dependency

Badge icon assets may not yet exist or may fail WCAG 2.2 AA contrast validation (minimum 3:1 for graphical objects) when rendered over design-token backgrounds. Missing or non-compliant icons could block UI epic delivery for Blindeforbundet, for whom screen reader and visual accessibility is non-negotiable.

Mitigation & Contingency

Mitigation: During this epic, implement the contrast-ratio validator in badge-icon-asset-manager and run it as a Flutter test against all candidate icon assets early. Coordinate with the design team to provide WCAG-compliant SVG icons in both locked and unlocked variants before the UI epic begins.

Contingency: If assets are late or fail contrast checks, ship placeholder icons that are guaranteed compliant (solid design-token colour fills with text labels) and swap in final assets post-QA without requiring a code change.