critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

RouteAccessibilityContract is defined as a Dart abstract class or interface with: semanticsLabel (required, non-nullable String), focusTarget (the widget key that receives focus on route entry), and liveRegionPolicy (enum: off, polite, assertive)
ModalRouteContract extends or composes RouteAccessibilityContract and adds: explicitCloseAction (required VoidCallback or equivalent) and closeButtonSemanticLabel (required String) — satisfying WCAG 2.2 SC 2.1.2 (no keyboard trap)
BackNavigationContract extends or composes RouteAccessibilityContract and adds: onBackRequested (required callback) and backButtonSemanticLabel (required String) — enforces that non-root routes always expose a back control
All three contracts are pure Dart (no Flutter widget imports) so they can be used by both the service layer and the audit runner without pulling in the widget tree
A ConcreteRouteConfig value object implementing all three contracts is provided as a reference implementation and used in existing NavigationRouteConfig tests
dart analyze reports zero errors or warnings on the new files
The contracts are exported from a single barrel file (e.g., navigation_contracts.dart) so consumers have one import
A short inline doc comment (///) explains the WCAG criterion each contract enforces

Technical Requirements

frameworks
Flutter (dart:core only for the interfaces)
Riverpod (contracts are plain Dart — no provider dependency)
apis
NavigationRouteConfig (existing — ConcreteRouteConfig should implement the new contracts)
NavigationAccessibilityService (downstream consumer)
Audit runner (downstream consumer)
data models
RouteAccessibilityContract (new)
ModalRouteContract (new)
BackNavigationContract (new)
LiveRegionPolicy enum (new)
ConcreteRouteConfig (new reference implementation)
performance requirements
Interfaces are pure data contracts — no runtime overhead beyond object allocation
security requirements
semanticsLabel and backButtonSemanticLabel must not accept empty strings at construction time — enforce via assert() in ConcreteRouteConfig constructor to prevent silent screen reader failures
No route-level PII (user names, IDs) should appear in semantics labels — document this constraint in the interface's doc comment

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Keep the interfaces as pure abstract classes rather than mixins — mixins complicate the audit runner's reflection-based validation. Use Dart's required keyword on all mandatory fields in ConcreteRouteConfig's constructor so missing fields are caught at compile time, not runtime. Define LiveRegionPolicy as a top-level enum in the same barrel file for discoverability. The distinction between ModalRouteContract and BackNavigationContract should be treated as composition (a modal route can also have back navigation), so design ConcreteRouteConfig to implement both simultaneously without ambiguity.

Consider adding a factory constructor ConcreteRouteConfig.tab() and ConcreteRouteConfig.modal() as named constructors to guide consumers toward correct usage patterns without requiring them to set every field manually.

Testing Requirements

Unit tests should verify the ConcreteRouteConfig reference implementation: (1) construction with valid arguments succeeds, (2) construction with an empty semanticsLabel triggers an AssertionError in debug mode, (3) a ModalRouteContract instance without an explicitCloseAction cannot be constructed (enforced by required parameter — verified by compile-time check), (4) a BackNavigationContract for a root route correctly represents the back action as a no-op via a documented sentinel value. Use flutter_test; no widget pump required. Test file: test/navigation/navigation_contracts_test.dart.

Component
Navigation Accessibility Service
service medium
Epic Risks (2)
high impact medium prob dependency

Flutter's SemanticsController used in integration tests is an internal or semi-internal API that can break between Flutter stable releases. If the audit runner relies heavily on undocumented semantics tree traversal, a Flutter upgrade could silently disable the audit checks without a build failure, creating false confidence.

Mitigation & Contingency

Mitigation: Use only the public flutter_test accessibility APIs (meetsGuideline, SemanticsController.ensureSemantics) and wrap all SemanticsController calls in a versioned helper class with explicit assertions that the expected semantics tree shape is still available. Pin the Flutter SDK range in pubspec.yaml.

Contingency: If SemanticsController APIs break on a Flutter upgrade, fall back to widget-level golden tests that include the semantics tree snapshot, combined with manual Switch Access and VoiceOver QA checklists executed before each release.

low impact medium prob resource

Flutter integration tests that simulate Switch Access traversal on multiple screens can be slow (30–120 seconds per test flow), which may make the audit runner impractical to run on every CI commit if the test suite already has long run times.

Mitigation & Contingency

Mitigation: Scope the audit runner to a dedicated integration test target that runs on pull requests targeting main and on nightly builds, not on every push. Parallelise test shards in CI to keep wall-clock time acceptable. Profile audit run times during development and trim any flows that duplicate coverage.

Contingency: If CI run times exceed acceptable thresholds, split the audit runner into a fast smoke suite (touch targets and semantic labels only, runs on every PR) and a thorough traversal suite (Switch Access simulation, runs nightly), with the nightly failure blocking the release branch rather than every PR.