high priority high complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

loadRiveAsset(assetPath, {required String stateMachineName, required String controllerName}) loads the .riv file from Flutter assets, initialises a RiveFile, creates a StateMachineController, and registers it under controllerName
loadRiveAsset completes with a RiveLoadResult.success or RiveLoadResult.failure — it never throws an unhandled exception to the caller
On load failure, WrappedAnimationController logs the error, marks that animation slot as using the Flutter fallback, and continues operating normally
getRiveController(name) returns the StateMachineController if loaded successfully, or null if the asset failed to load or the name is not registered
triggerRiveInput(controllerName, inputName) finds the SMITrigger input by name on the named controller and calls fire(); if the input is not found, it logs a warning without throwing
All .riv asset files are registered under the assets section of pubspec.yaml and the build does not fail on clean flutter build
Rive package license is confirmed as compatible with commercial use and documented in a LICENSES or NOTICES file
dispose() correctly calls StateMachineController.dispose() for all Rive controllers in addition to Flutter AnimationControllers
Rive animations degrade gracefully to equivalent Flutter animations when the .riv asset fails to load — the Wrapped screen must still render
The fallback path is testable by providing a non-existent asset path and asserting the fallback Flutter animation plays instead

Technical Requirements

frameworks
Flutter
rive (Flutter package)
Riverpod
performance requirements
Rive asset loading must happen asynchronously before the Wrapped screen is shown — use a loading state to block premature rendering
Rive animations must sustain 60 fps on mid-range devices — profile with Flutter DevTools before release
Total .riv asset bundle size must not exceed 2 MB to avoid excessive app binary growth
security requirements
Rive assets are bundled in the app binary — do not fetch them from remote URLs at runtime to avoid content injection
No PII is encoded in Rive animation files
ui components
RiveAnimation.direct() widget for embedding in the Wrapped screen
StateMachineController
SMITrigger, SMIBool, SMINumber input types

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Use the rive Flutter package (not the legacy flare package). Load the RiveFile using RiveFile.asset(assetPath) inside an async method. Create the StateMachineController via StateMachineController.fromArtboard(artboard, stateMachineName) — handle the null return (state machine not found) as a failure case. Store Rive controllers in a separate Map alongside the existing Map.

The fallback strategy should be defined at the AnimationConfig level — each config specifies both a Rive asset path (nullable) and a Flutter fallback duration/curve. Before confirming Rive licensing, check rive.app/pricing — the free tier allows commercial use for apps under a revenue threshold; document the threshold and escalate to the team if the app exceeds it.

Testing Requirements

Widget tests: (1) loadRiveAsset with valid path returns success and getRiveController returns non-null, (2) loadRiveAsset with invalid path returns failure and getRiveController returns null, (3) triggerRiveInput with valid input does not throw, (4) triggerRiveInput with invalid input name logs warning without throwing, (5) dispose() on controller with mixed Flutter + Rive controllers does not throw, (6) fallback Flutter animation plays when Rive load fails. Use rootBundle mock or a minimal test .riv fixture for integration tests. Verify no StateMachineController leak using widget pump-and-dispose pattern.

Component
Wrapped Animation Controller
infrastructure high
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.