critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

TabIndex is a Dart value object (immutable class or freezed) wrapping an int with valid range 0–4 (matching the 5-tab bottom nav)
TabIndex.fromInt() factory validates range and throws ArgumentError for out-of-range values
NavigationStackSnapshot is an immutable class holding a tabIndex (TabIndex) and a list of route path strings representing the sub-route stack for that tab
NavigationStateRecord holds a Map<TabIndex, NavigationStackSnapshot> and a lastUpdated DateTime
All three classes implement == and hashCode (via freezed or manual)
All three classes provide a toJson() / fromJson() serialization pair that round-trips without data loss
An abstract NavigationStateRepositoryInterface is defined with the method signatures: saveTabIndex, loadTabIndex, saveStackSnapshot, loadStackSnapshot, clearAll — typed as Future<void> / Future<T?>
All models and the interface are exported from a single barrel file (e.g., lib/navigation/navigation_state_models.dart)
No implementation code or Supabase/SharedPreferences imports are present in this task's files — models are pure Dart

Technical Requirements

frameworks
Flutter
Dart
freezed (optional)
json_serializable (optional)
data models
TabIndex
NavigationStackSnapshot
NavigationStateRecord
NavigationStateRepositoryInterface
performance requirements
toJson/fromJson must handle serialization of a 5-tab snapshot in under 1ms
security requirements
Route path strings must not contain user PII — validate that only path segments (no query params with IDs) are stored in snapshots

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Prefer using freezed for value object boilerplate (== , copyWith, toJson) to reduce hand-written code and future maintenance. If freezed is not already a project dependency, a simple immutable class with const constructor and manual == / hashCode override is acceptable. Define the abstract interface using Dart abstract class (not mixin) so it can be implemented by multiple storage backends in future. Use List for the route stack — avoid storing NavigatorState or BuildContext references.

Keep the barrel file import-clean: only export public types, not internal helpers. These models become the contract between the repository (task-002) and the BLoC/Cubit (task-004 onward), so agree on the serialization format with the team before merging.

Testing Requirements

Write pure Dart unit tests (no Flutter widgets needed) covering: (1) TabIndex.fromInt(0..4) succeeds, (2) TabIndex.fromInt(-1) and fromInt(5) throw ArgumentError, (3) NavigationStackSnapshot.toJson() / fromJson() round-trip, (4) NavigationStateRecord.toJson() / fromJson() round-trip with all 5 tabs populated, (5) equality: two instances with identical data are equal (==). No mocking required. Target 100% line coverage for the models file.

Component
Navigation State Repository
data low
Epic Risks (2)
high impact medium prob technical

StatefulShellRoute branch navigator state can interact unexpectedly with GoRouter's imperative navigation (go, push, replace), causing state snapshots to desync from actual route stacks. This could manifest as a user returning to a tab and seeing a different screen than expected, breaking the core motor-fatigue promise.

Mitigation & Contingency

Mitigation: Write integration tests that simulate cross-tab navigation with nested pushes before any UI layer is built. Pin go_router to a tested minor version and review the StatefulShellRoute changelog before upgrading.

Contingency: If branch navigator state consistently desyncs, fall back to a manual stack snapshot strategy using a custom NavigatorObserver that records and replays navigation events independently of StatefulShellRoute internals.

medium impact medium prob technical

Persisted navigation stacks in shared_preferences can become stale or corrupt if route paths are renamed during development, causing app crashes or infinite redirect loops on cold start for users who have an old snapshot.

Mitigation & Contingency

Mitigation: Version the persisted schema with a format key. On app start, validate that all stored route paths exist in the current route config before restoring; silently discard invalid entries rather than crashing.

Contingency: Implement a safe-mode cold start that skips state restoration after a detected crash (via a dirty-launch flag written at startup and cleared on successful first frame), falling back to the default root tab.