high priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

Repository exposes an abstract interface IBenefitMultiplierConfigRepository with at minimum a getConfig({bool forceRefresh}) async method returning BenefitMultiplierConfig
Concrete implementation fetches multiplier rows from Supabase (table: benefit_multiplier_configs or equivalent) and maps them to a typed BenefitMultiplierConfig Dart object
On first successful fetch, config is written to a local cache (SharedPreferences or Hive) with a timestamp for TTL validation
Subsequent calls within the TTL return the cached config without a network request
When Supabase is unreachable (no internet, timeout), the cached config is returned if available; if no cache exists, a typed failure (Result/Either or exception) is returned — the app must not crash
forceRefresh: true bypasses TTL and fetches fresh data, then overwrites the cache
BenefitMultiplierConfig contains at minimum: societalValuePerHour (double), reachMultiplier (double), economicImpactFactor (double), and a configVersion string for invalidation
Repository is registered in the dependency injection layer (Riverpod provider or BLoC service locator)
No direct Supabase calls exist outside this repository — all data access is through the interface

Technical Requirements

frameworks
Flutter
Riverpod
Supabase Flutter SDK
apis
Supabase PostgREST — benefit_multiplier_configs table
data models
BenefitMultiplierConfig
performance requirements
Cache read completes in under 10ms
Network fetch has a 10-second timeout with graceful fallback to cache
Config object size is small (<1KB) — no pagination needed
security requirements
Supabase table must have Row Level Security (RLS) set to public read-only for config rows
No user-identifying data stored in the config table
Cache storage key must be namespaced to prevent collision with other cached data

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Define the abstract interface first so the BLoC/use case can be written and tested against it independently. For caching, use SharedPreferences with a JSON-encoded string and a separate TTL key (e.g., 'benefit_config_fetched_at'). Keep the TTL at 24 hours — config data is stable. Use Supabase's .from('benefit_multiplier_configs').select().single() pattern.

If the table may have multiple rows (e.g., per-org configs), add an org_id filter. Expose the repository via a Riverpod Provider so it can be overridden in tests. Consider a bundled JSON asset as a hard fallback if both Supabase and cache are unavailable — load via rootBundle.

Testing Requirements

Unit tests using flutter_test and mocktail covering: successful fetch and mapping, cache hit (no network call), cache miss (network call + cache write), offline fallback to cache, offline with empty cache returns failure, and force-refresh behavior. Integration test (optional, CI-gated) verifying the Supabase table schema matches the Dart mapping. Aim for 90%+ branch coverage.

Component
Benefit Calculator Screen
ui low
Epic Risks (3)
high impact medium prob technical

The RepaintBoundary PNG capture approach for sharing the results card may produce blurry or oversized images on high-DPI devices, or may silently fail on certain Android OEM configurations that restrict off-screen rendering. A failed share would break one of the core use cases (recruitment tool).

Mitigation & Contingency

Mitigation: Implement the capture using the established screenshot-capture-utility pattern already present in the Wrapped summary feature (component 542-screenshot-capture-utility). Test on a range of iOS and Android devices including Samsung and Huawei OEM builds during development. Set explicit pixel ratio (3.0) when calling toImage() to guarantee resolution.

Contingency: If image capture fails on a platform, the BenefitShareService falls back to sharing the plain-text summary only, with a user-facing message explaining the image could not be generated. This ensures the share flow never fully blocks.

high impact medium prob technical

Implementing full WCAG 2.2 AA compliance for the results card and metric tiles — including live regions, focus management, and semantic labels that read naturally in Norwegian — requires deep familiarity with Flutter semantics APIs. Gaps may only surface during screen reader testing on physical devices, late in the sprint.

Mitigation & Contingency

Mitigation: Use the existing semantics-wrapper-widget (606) and live-region-announcer (608) components from the accessibility feature rather than implementing custom semantics. Assign screen reader testing on a physical iPhone with VoiceOver as a mandatory acceptance gate, not an afterthought. Write widget tests using Flutter's AccessibilityGuideline matchers early in development.

Contingency: If screen reader issues are found late, the pure semantic markup approach (no Canvas numbers, all Semantics wrappers) limits the blast radius to label text corrections. Escalate to the accessibility feature team for a pairing session to resolve complex focus management issues.

medium impact low prob integration

The BenefitResultsCard must match the Wrapped design language used in the annual summary feature. If design tokens or widget patterns are inconsistent between the two features, the results card will look out of place and undermine the intended emotional impact for sharing.

Mitigation & Contingency

Mitigation: Review the existing Wrapped summary screen (529-wrapped-summary-screen) and stat card widget (530-stat-card-widget) implementations before building the results card. Reuse design tokens from the existing design-token-theme (200) system. Involve the designer in a review of the results card mock-up against the Wrapped language before implementation begins.

Contingency: If design parity issues are discovered post-implementation, isolate visual adjustments to the BenefitResultsCard widget. The feature's business logic and accessibility compliance are unaffected by visual polish changes.