critical priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

BenefitMultiplierConfigRepository exposes a getConfig(String organisationId, {bool forceRefresh = false}) method returning Future<BenefitMultiplierConfig>.
On a fresh fetch, the repository queries Supabase table benefit_multiplier_config with .eq('organisation_id', organisationId).single() and returns a deserialized BenefitMultiplierConfig.
After a successful fetch, the result and a timestamp are persisted to the local cache (SharedPreferences or Hive box named 'benefit_config_cache').
On subsequent calls within 24 hours (without forceRefresh), the cached value is returned without making a Supabase network call.
Calling with forceRefresh: true bypasses TTL and re-fetches from Supabase, then updates the cache.
When the device is offline and the cache is populated, the cached BenefitMultiplierConfig is returned regardless of TTL.
When the device is offline and the cache is empty, BenefitMultiplierConfig.defaults(organisationId) is returned and the caller is notified via a documented exception or Result type.
Cache keys are namespaced by organisationId to support multi-org scenarios.
Unit tests cover: cache hit within TTL, cache miss (first call), force refresh, offline with cache, offline without cache.
Repository is abstracted behind an IBenefitMultiplierConfigRepository interface to support mocking in BLoC tests.

Technical Requirements

frameworks
Flutter
BLoC
flutter_test
mocktail
apis
Supabase REST API — benefit_multiplier_config table
SharedPreferences or Hive local storage API
data models
BenefitMultiplierConfig
performance requirements
Cache read completes in under 10ms.
Supabase fetch completes within 3 seconds under normal network conditions.
No blocking synchronous I/O on the main thread.
security requirements
Supabase RLS policy must restrict benefit_multiplier_config rows to the authenticated user's organisation — do not expose configs from other organisations.
Cache keys must include organisationId to prevent cross-organisation data leakage on shared devices.
Do not cache Supabase JWT tokens alongside the config.

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Define abstract class IBenefitMultiplierConfigRepository with the getConfig signature, then implement BenefitMultiplierConfigRepository. Inject both the Supabase client and the cache provider via constructor for testability. For cache TTL, store a cached_at ISO-8601 string alongside the serialized config JSON in SharedPreferences using a key like 'bmc_{organisationId}'. On read, parse the timestamp and compare with DateTime.now() — if difference exceeds 24 hours, treat as stale.

For the offline detection, wrap the Supabase call in try/catch and check for SocketException or PostgrestException with status 0 to distinguish network errors from Supabase data errors. Use Hive over SharedPreferences if the project already uses Hive for other caches (check existing dependencies). Register the repository in the dependency injection layer (Riverpod provider or service locator).

Testing Requirements

Unit tests using flutter_test and mocktail. Mock both the Supabase client and the cache storage layer (SharedPreferences or Hive). Test file: test/repositories/benefit_multiplier_config_repository_test.dart. Scenarios: (1) first call fetches from Supabase and writes cache, (2) second call within TTL reads from cache only, (3) forceRefresh bypasses cache, (4) offline with valid cache returns cached value, (5) offline with empty cache returns defaults and throws/signals ConfigUnavailableException, (6) organisationId namespacing verified in cache key.

Integration test (optional, network): verify correct Supabase row is returned for a known test organisation.

Epic Risks (2)
medium impact medium prob integration

Supabase organisation configuration table may not yet have the five multiplier columns, requiring a migration. If the migration is not coordinated with other teams touching the same table, schema conflicts could delay delivery.

Mitigation & Contingency

Mitigation: Add multiplier columns in a dedicated, non-destructive ALTER TABLE migration script. Review the organisation config table schema with the backend team before writing the migration. Use nullable columns with defaults so existing rows are unaffected.

Contingency: If the migration cannot be deployed in time, stub the repository to return hardcoded default multiplier values from a local config file, allowing parallel development. Swap in the real Supabase fetch once the migration is live.

low impact medium prob scope

The ActivitySummaryAggregator depends on activity records already persisted in the database. For newly onboarded peer mentors with no activity history, the aggregator will return zero counts, which could make the calculator appear broken on first use.

Mitigation & Contingency

Mitigation: Design the pre-fill value object to distinguish between 'no data yet' and 'zero activities'. The calculator input panel should display empty inputs (not zero) when no history exists, with placeholder text guiding the user to enter values manually.

Contingency: If the distinction cannot be surfaced cleanly in the UI timeline, fall back to always showing empty inputs and document the manual-entry path as the primary UX until activity data accumulates.