Implement accessibility context change notifications
epic-navigation-and-gesture-accessibility-foundation-task-012 — Add a notifyAccessibilityLayer method to TabStateManager that fires whenever the active tab changes. The notification must include the new tab index, the route semantics label from NavigationRouteConfig's metadata, and a context-change reason enum (userTap, sessionRestore, roleSwitch). This stream is the contract the NavigationAccessibilityService will consume to announce context changes to screen readers via live regions.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
Extend setActiveTab to accept an optional ContextChangeReason parameter (defaulting to userTap) so call sites that are not awareness-sensitive do not need to change. Inside setActiveTab, after updating the internal state, look up the semantics label by calling NavigationRouteConfig.getMetadata(index)?.semanticsLabel and emit the AccessibilityContextChange to the broadcast StreamController. Define AccessibilityContextChange as an immutable Dart class (use @immutable annotation and a const constructor) with final fields — this makes it safe to pass through streams and compare in tests. Keep notifyAccessibilityLayer as a private implementation detail; the public API surface is only the accessibilityContextChanges stream getter.
Testing Requirements
Unit tests should be added to the task-011 test suite or as a companion file. Required test cases: (1) tab change with userTap reason emits correct AccessibilityContextChange, (2) sessionRestore reason emits correctly on hydration, (3) roleSwitch reason emits correctly on role change, (4) sub-route push does NOT emit to the stream, (5) null semantics label falls back to default string. Use StreamQueue from async package or manual stream subscription with expectLater and emitsInOrder.
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.
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.