high priority medium complexity backend pending backend specialist Tier 3

Acceptance Criteria

Cache layer is implemented as a separate CachingScenarioRuleRepository that wraps ScenarioRuleRepository or as a decorator pattern — not by modifying the base repository
fetchRulesForChapter returns cached data immediately if cache is valid (age < 24 hours) and triggers a background refresh
If no cache exists, data is fetched from Supabase, returned, and stored in cache
Cache TTL is 24 hours — entries older than 24h are treated as expired and a fresh fetch is performed synchronously
When the chapter context changes (user switches chapter or logs in to a different chapter), all cached rules are invalidated
Cache key includes chapterId to prevent cross-chapter cache contamination
Background refresh updates the cache and notifies listeners via a Riverpod stream or state
If background refresh fails (no network), the app continues using stale cache without crashing
Hive or flutter_secure_storage is used consistently with the existing codebase storage approach
Cache stored data is JSON-serialized ScenarioRule list — not raw Supabase response maps
Unit tests cover: cache hit, cache miss, expired cache, and chapter change invalidation scenarios

Technical Requirements

frameworks
Flutter
Riverpod
Hive or flutter_secure_storage
Supabase Flutter SDK
apis
Supabase PostgREST API (background refresh)
Local device storage API
data models
ScenarioRule
TriggerCondition
CachedRuleSet (wrapper with timestamp)
performance requirements
Cache read must complete in <10ms on device (local storage, no network)
Background refresh must not block the UI thread — use Future or Isolate
Cache serialization/deserialization must handle lists of 50+ rules without noticeable delay
security requirements
If using flutter_secure_storage, data is encrypted at rest by the OS keychain/keystore
If using Hive, consider enabling encryption for the box storing scenario rules since rules contain notification content
Cache must be cleared on logout to prevent rule data leakage to next user on shared devices

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Check the existing codebase for any established caching utilities or storage abstractions before introducing a new pattern. If Hive is already a dependency, use a dedicated Hive box named 'scenario_rules_cache'. If only flutter_secure_storage is available, serialize the List to JSON string and store under key 'scenario_rules_{chapterId}'. Implement a CachedRuleSet wrapper model: `{ rules: List, cachedAt: DateTime, chapterId: String }`.

For the cache-first + background refresh pattern, return a Stream> from fetchRulesForChapter — yield cached value immediately, then yield refreshed value when background fetch completes. Alternatively, expose an explicit refreshRules() method and rely on Riverpod state for reactivity. The chapter change invalidation can be triggered by listening to an authentication state change provider. Ensure the 24-hour TTL is calculated server-time-agnostic using DateTime.now() on device.

Testing Requirements

Unit tests with flutter_test: (1) when cache is valid, fetchRulesForChapter returns cached rules without calling Supabase; (2) when cache is empty, Supabase is called and result is cached; (3) when cache is expired (timestamp > 24h), Supabase is called synchronously and cache is refreshed; (4) when chapter changes, old cache is invalidated; (5) background refresh failure does not throw — stale cache is retained; (6) cache key is chapter-scoped (rule set for chapter A not returned for chapter B request); (7) logout clears all cached rules. Use a fake/mock for the underlying storage (Hive or flutter_secure_storage) and a mock for the network repository. Integration test: install app, fetch rules while online, enable airplane mode, restart app, assert rules still available.

Component
Scenario Rule Repository
data medium
Epic Risks (2)
high impact medium prob security

Supabase RLS policies for chapter-scoped rule access may interact unexpectedly with service-role keys used by the Edge Function, potentially blocking backend reads or leaking cross-chapter data.

Mitigation & Contingency

Mitigation: Write and review RLS policies in isolation with automated policy tests before merging; define a dedicated service-role bypass policy scoped to the edge function's Postgres role.

Contingency: If RLS blocks the edge function, temporarily use a bypass policy with audit logging while a permanent fix is implemented; escalate to a Supabase security review.

medium impact high prob integration

FCM device tokens become invalid when users reinstall the app or revoke permissions; stale tokens cause silent delivery failures that are hard to detect without explicit error handling.

Mitigation & Contingency

Mitigation: Implement token invalidation handling in PushNotificationDispatcher that removes stale tokens from the database on FCM 404/410 responses; log all delivery failures with structured output.

Contingency: If token hygiene proves unreliable, add a periodic token refresh job that re-registers all active users' tokens via the FCM registration endpoint.