critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

LoadSummary event is defined with an optional `period` parameter (defaults to current year) typed as a value object or enum
ChangePeriod event carries a non-nullable `period` field that uniquely identifies the requested time window
ShareSlide event carries the zero-based `slideIndex` integer of the slide to be shared
NavigateSlide event carries a `direction` enum (next/previous) or an explicit `targetIndex` integer
WrappedSummaryLoading state is immutable and has no data fields
WrappedSummaryLoaded state contains: `slides` (typed list), `currentSlideIndex` int, `period` value, and `milestones` list
WrappedSummaryError state carries an `errorCode` string and a human-readable `message` string
WrappedSummaryOffline state carries the stale `slides` list, `period`, `milestones`, and a `cachedAt` DateTime
WrappedSummaryShareError sub-state (or sealed variant) carries `slideIndex` and `failureReason`
All events and states extend Equatable and override `props` correctly — two instances with identical fields compare equal
All classes are barrel-exported from a single `wrapped_summary_bloc.dart` file
No business logic resides in events or states — they are pure data containers

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
Equatable
data models
WrappedSummarySlide
WrappedMilestone
SummaryPeriod
performance requirements
Equatable props lists must not contain mutable collections — use List.unmodifiable or IList
State objects must be lightweight; avoid embedding large byte buffers
security requirements
No PII (personal identifiable information) stored directly in event payloads — use opaque IDs only
States must not expose raw Supabase Row objects

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Use Dart sealed classes (Dart 3+) for both events and states to enable exhaustive pattern matching in the BLoC and in widgets. Define a `SummaryPeriod` value object (year: int, optional quarter) rather than raw integers so the API is self-documenting. Place all files under `lib/features/wrapped_summary/bloc/`. Use `@immutable` annotation on all state classes.

Keep props lists sorted alphabetically to avoid accidental inequality from list ordering. Export everything via `wrapped_summary_bloc.dart` barrel file.

Testing Requirements

Unit tests using flutter_test. Test Equatable equality for every event and state class: (1) two instances with identical fields are equal, (2) instances differing in any field are not equal. Test that `props` lists include all fields. Test sealed-class exhaustiveness if Dart sealed classes are used.

Minimum 100% line coverage on all event/state files.

Component
Wrapped Summary BLoC
service medium
Epic Risks (3)
medium impact medium prob technical

If the device transitions between online and offline states while the user is mid-session in the wrapped screen, the BLoC may emit conflicting state transitions (loaded → error → offline) that cause visual flickering or an inconsistent UI state such as showing the offline banner over an already-loaded summary.

Mitigation & Contingency

Mitigation: Implement a connectivity stream listener in the BLoC that only triggers a state re-evaluation when transitioning from online to offline, not on every connectivity event. Once a summary is in the Loaded state, the BLoC should not transition to error/offline unless the user explicitly requests a refresh. Store the last-loaded data in BLoC state so it survives connectivity changes.

Contingency: If state flickering is observed in testing, add a minimum 3-second debounce on connectivity state changes before the BLoC reacts, and display a non-blocking top banner rather than replacing the entire screen state.

high impact medium prob integration

The push notification deep-link to the wrapped-summary-screen must work correctly whether the app is in the foreground, background, or terminated state. Handling all three app launch states on both iOS and Android is a common source of edge-case bugs, particularly when authentication state must be restored before the deep link can be resolved.

Mitigation & Contingency

Mitigation: Implement deep-link handling through the existing notification-deep-link-handler component which already manages app-state-aware routing. Define the wrapped-summary route in the navigation config early in the epic so the router is ready before notification dispatch is wired. Test all three app states (foreground, background, terminated) explicitly in the QA checklist.

Contingency: If terminated-state deep-linking fails on specific platforms, fall back to launching the app to the home screen with an in-app notification banner prompting the user to open their summary, rather than direct deep-link navigation.

high impact low prob technical

The wrapped-summary-screen manages a large number of AnimationController instances (one or more per slide) via the wrapped-animation-controller. If disposal is not triggered correctly when the user exits mid-flow (e.g., via system back gesture or deep-link away), memory leaks will accumulate across session navigation.

Mitigation & Contingency

Mitigation: Implement screen disposal via Flutter's dispose() lifecycle method calling a single wrapped-animation-controller.disposeAll() method that iterates the named controller registry. Write a test that navigates to the screen, starts animations, then navigates away and verifies no active AnimationController listeners remain using Flutter's test binding.

Contingency: If disposal bugs are detected in production via memory profiling, patch by converting all AnimationControllers to use AutomaticKeepAliveClientMixin false and wrap each slide in a widget that disposes its own controller when removed from the widget tree.