critical priority low complexity backend pending backend specialist Tier 3

Acceptance Criteria

put(summary) stores the AnnualSummary in the Hive box keyed by '${summary.peerMentorId}_${summary.year}' and completes without error for valid inputs
get(mentorId, year) returns the stored AnnualSummary if present, or null if absent; never throws for a cache miss
getAll(mentorId) returns a List<AnnualSummary> containing all years cached for the given mentor, returning an empty list if none exist
delete(mentorId, year) removes the entry if present; calling delete on a non-existent key is a no-op and does not throw
clear() removes all entries from the box and completes successfully on an empty box
isCached(mentorId, year) returns true if and only if get(mentorId, year) would return a non-null value
On a corrupted entry (HiveError or CastError during read), the method logs the error at warning level, deletes the corrupted key, and returns null — it does not rethrow
Cache-hit is logged at debug level with mentorId and year; cache-miss is logged at debug level with the same fields
SummaryOfflineCache is exposed as a Riverpod Provider<SummaryOfflineCache> (not a StateNotifier) for synchronous injection
SummaryOfflineCache has no direct dependency on Supabase or network layers

Technical Requirements

frameworks
Flutter
Hive
hive_flutter
Riverpod
data models
annual_summary
performance requirements
getAll() for a mentor with 10 years of data completes within 10 ms
put() and delete() are O(1) operations via Hive keyed access
security requirements
Class must not log PII fields (peer_mentor_id exposed only as truncated UUID in debug logs)
clear() must only be callable with coordinator or admin role context — enforce at call site, not inside the cache class

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Keep SummaryOfflineCache as a plain Dart class (not a ChangeNotifier) — it is a thin synchronous wrapper over the already-opened Hive box. Accept the Box as a constructor parameter for testability. The Riverpod provider reads the box from HiveService. Use a consistent key builder private method _key(mentorId, year) to avoid key string drift across methods.

For the corrupted-entry pattern, wrap the get() body in try/catch (HiveError, TypeError) — delete the bad key and return null. Logging should use the project's existing logger abstraction, not print().

Testing Requirements

Unit tests (flutter_test) with an in-memory Hive box mock or a temporary test directory: (1) put then get returns same object, (2) get on absent key returns null, (3) getAll returns correct subset for a given mentorId when multiple mentors are stored, (4) delete removes entry and subsequent get returns null, (5) delete on absent key does not throw, (6) clear empties box, (7) isCached returns correct boolean, (8) corrupted entry is silently deleted and null returned. Aim for 100% branch coverage on SummaryOfflineCache.

Component
Summary Offline Cache
data low
Epic Risks (3)
medium impact medium prob dependency

Rive animation files may not be available at implementation time, blocking the wrapped-animation-controller from being fully tested. If asset delivery is delayed, the controller cannot be validated for memory-leak-free disposal.

Mitigation & Contingency

Mitigation: Implement the animation controller with stub/placeholder AnimationController instances first so the lifecycle and disposal logic can be unit-tested independently of Rive assets. Define a named animation registry interface early so UI components can reference animations by name without coupling to specific Rive files.

Contingency: If Rive assets are not delivered before Epic 3 begins, replace Rive animations with Flutter implicit animations (AnimatedOpacity, ScaleTransition) as a drop-in and schedule Rive integration as a follow-on task once assets arrive.

high impact medium prob technical

The annual_summaries Supabase RPC aggregating 12 months of activity records per mentor may exceed acceptable query latency (>2s) for mentors with high activity volumes such as the HLF mentor with 380 registrations cited in workshop notes.

Mitigation & Contingency

Mitigation: Design the RPC to materialise summary results into the annual_summaries table via a scheduled edge function rather than computing on demand. The repository reads pre-computed rows, keeping query latency constant regardless of activity volume.

Contingency: If on-demand queries are required for real-time period switching, add a PostgreSQL partial index on (mentor_id, activity_date) and implement a client-side loading skeleton so slow queries degrade gracefully rather than blocking the UI.

medium impact low prob technical

iOS requires photo library permission before saving a screenshot to the gallery. If the permission prompt is triggered at an unexpected point in the share flow, the UX breaks and users may deny permission permanently, making gallery save unavailable.

Mitigation & Contingency

Mitigation: Trigger the permission request only when the user explicitly chooses 'Save to gallery' in the share overlay, not on screen load. Implement a pre-prompt explanation screen following Apple HIG so users understand why the permission is needed before the system dialog appears.

Contingency: If permission is denied, gracefully fall back to clipboard copy and system share sheet options which do not require photo library access, and surface a non-blocking snackbar explaining the limitation.