critical priority medium complexity backend pending backend specialist Tier 1

Acceptance Criteria

NavigationStateRepository implements NavigationStateRepositoryInterface from task-001
saveTabIndex(TabIndex) serializes to String and stores under a namespaced key (e.g., 'nav_state.tab_index')
loadTabIndex() returns null (not an exception) when no value is stored (cold start)
saveStackSnapshot(TabIndex, NavigationStackSnapshot) serializes snapshot to JSON string and stores under a per-tab key (e.g., 'nav_state.stack.2')
loadStackSnapshot(TabIndex) returns null when no snapshot is stored for that tab
clearAll() removes all keys with the 'nav_state.' prefix and completes without error
Repository calls clearAll() when it receives a logout lifecycle event from the auth stream
Repository calls clearAll() when it receives a role-switch event
All five public methods are async and return typed Futures — no synchronous blocking calls
SharedPreferences instance is injected via constructor (not obtained via getInstance() inside methods) to support testing

Technical Requirements

frameworks
Flutter
Dart
shared_preferences
apis
Supabase Auth stream (for logout/role-switch event detection)
data models
TabIndex
NavigationStackSnapshot
NavigationStateRepositoryInterface
performance requirements
read/write operations complete in under 50ms on a mid-range device
clearAll() must complete before the logout navigation transition begins
security requirements
Keys are namespaced to avoid collisions with other shared_preferences consumers in the app
clearAll() must complete atomically — partial clear is not acceptable (use removeAll by prefix or a set of explicit keys)
No auth tokens or personal data stored — only navigation path strings

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Use a constant prefix map or a static const List of all managed keys to ensure clearAll() is exhaustive — avoid relying on prefix scanning as shared_preferences does not natively support key prefix listing. Instead, maintain the set of known keys as a class-level constant. Inject SharedPreferences through the constructor and register the repository as a singleton in the dependency injection layer (Riverpod provider or BLoC service locator) so the same instance is shared across the app. For lifecycle event integration, subscribe to Supabase.instance.auth.onAuthStateChange inside an init() method and call clearAll() on AuthChangeEvent.signedOut and on role change events — dispose the subscription in a dispose() method.

Serialize snapshots with jsonEncode(snapshot.toJson()) and deserialize with NavigationStackSnapshot.fromJson(jsonDecode(stored)) wrapped in try/catch to handle corrupted data gracefully (return null on parse error).

Testing Requirements

Unit tests in task-003 cover all persistence methods using SharedPreferences.setMockInitialValues(). This task should include integration smoke tests that run against a real SharedPreferences instance on a test device (or via integration_test package) to verify actual disk persistence across app restart simulation. Specifically test: save tab index → restart simulation → load tab index returns the saved value. Cover all 5 tab indices.

Test clearAll() by saving data for all tabs then calling clearAll() and asserting every loadX() returns null.

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.