critical priority medium complexity backend pending backend specialist Tier 3

Acceptance Criteria

NavigationAccessibilityService has a verifyBackControls() method (or equivalent) that returns List<AccessibilityViolation>
A non-root route that includes a PersistentBackButton (or AppBar with automaticallyImplyLeading:true and a back destination) produces zero violations
A non-root route that lacks any detectable back control produces exactly one AccessibilityViolation with violationType indicating missing back control
Root routes are excluded from this check — no false positives on root-level screens
Modal routes are excluded from this check — they are covered by the close-action verifier (task-003)
The verifier uses the same registration or introspection pattern established in task-003 for consistency
AccessibilityViolation records use the shared ViolationType (BackNavigationMissing) defined in task-003
remediationHint field contains actionable plain-English instruction (e.g., 'Add a PersistentBackButton to the AppBar leading slot')
verifyBackControls() and verifyModalCloseActions() can both be called on the same NavigationAccessibilityService instance without state interference
Unit tests pass for all compliant, non-compliant, and edge-case scenarios

Technical Requirements

frameworks
Flutter
GoRouter (go_router package)
apis
NavigationRouteConfig (task-002)
RouteDescriptor.isNonRoot filter
PersistentBackButton widget reference
AppBar.automaticallyImplyLeading
data models
AccessibilityViolation (shared from task-003)
RouteDescriptor
ViolationType.BackNavigationMissing
performance requirements
Verification of up to 30 non-root routes must complete in under 100ms
Combined call to both verifiers (modal + back) must complete in under 200ms total
security requirements
No route widget arguments logged in any violation record
Output only emitted in debug builds (enforced in task-006)
ui components
PersistentBackButton (existing reusable widget — reference only)

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Filter RouteDescriptor list by isNonRoot=true and isModal=false before running verification. This prevents root and modal routes from generating false positives. If using the registration approach from task-003, back controls should self-register their route association similarly to close actions. If using widget introspection, check for: PersistentBackButton type, AppBar widget with a non-null leading that is a back-navigation widget, or IconButton with Icons.arrow_back / Icons.arrow_back_ios semantics label.

The workshop requirement (likeperson.md) explicitly mandates 'tilbakeknapp fremfor sidelengs-sveip' — back button over swipe — making this check a direct WCAG 2.2 AA accessibility compliance gate. The verifier should be added as a second method on the existing NavigationAccessibilityService class, not a separate class, to keep violation collection centralised.

Testing Requirements

Unit tests using flutter_test. Scenarios: (1) non-root route with PersistentBackButton → zero violations, (2) non-root route with AppBar(automaticallyImplyLeading:true) → zero violations, (3) non-root route with no back control → one violation with BackNavigationMissing type, (4) root route → excluded, zero violations regardless of back control presence, (5) modal route → excluded from this check, (6) mixed tree with multiple non-root routes, some compliant → correct violation count only for non-compliant routes, (7) calling verifyBackControls() after verifyModalCloseActions() on same instance returns independent results without cross-contamination.

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.