critical priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

NavigationAccessibilityService has a verifyModalCloseActions() method (or equivalent) that returns List<AccessibilityViolation>
AccessibilityViolation is a typed value class with fields: routePath (String), violationType (enum or sealed class), description (String), remediationHint (String)
A modal route that includes a ModalCloseButton (or registered equivalent) produces zero violations
A modal route that lacks any close action widget produces exactly one AccessibilityViolation with violationType indicating missing close action
The check covers at minimum: ModalCloseButton widget presence, IconButton with Icons.close semantics label, and any widget registered in a configurable close-action registry
The verifier does not require pumping a live widget tree — it works against widget builder introspection or a registration-based pattern
Empty modal route list returns an empty violations list without error
AccessibilityViolation records include the route path so violations are unambiguous
The remediationHint field contains a plain-English instruction (e.g., 'Add a ModalCloseButton to the AppBar or top of the modal body')
Unit tests pass for both compliant and non-compliant modal route scenarios

Technical Requirements

frameworks
Flutter
GoRouter (go_router package)
apis
NavigationRouteConfig (from task-002)
RouteDescriptor
WidgetBuilder introspection or widget registry pattern
data models
AccessibilityViolation (new typed value class)
RouteDescriptor
ViolationType enum (ModalMissingCloseAction, BackNavigationMissing, HorizontalSwipeDetected)
performance requirements
Verification of up to 20 modal routes must complete in under 100ms
No widget pumping or async operations during verification
security requirements
Violation records must not include widget constructor argument values that could contain user data
All verification output behind kDebugMode in the warning surface (handled in task-006)
ui components
ModalCloseButton (existing reusable widget — must be referenced, not duplicated)

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

The primary challenge is detecting close-action widget presence without pumping a live tree. Two recommended approaches: (A) Registration pattern — routes register their close-action widget type at startup via a CloseActionRegistry, and the verifier queries the registry; (B) Widget builder introspection — call the route's builder with a mock BuildContext and walk the returned widget tree looking for known close-action widget types. Approach A is simpler to test and avoids BuildContext complexity. Define ViolationType as a sealed class or enum shared across all verifier methods (task-003, task-004, task-005) so violation records are uniform.

The AccessibilityViolation class should be immutable and implement == and hashCode for straightforward test assertions. Coordinate with the team on the canonical list of 'acceptable close action' widget types to avoid false positives.

Testing Requirements

Unit tests using flutter_test. Scenarios: (1) modal route with ModalCloseButton → zero violations, (2) modal route with IconButton(icon: Icon(Icons.close)) with correct semantics → zero violations, (3) modal route with no close widget → one AccessibilityViolation with correct violationType and non-empty remediationHint, (4) multiple modal routes, mix of compliant and non-compliant → correct count of violations, (5) zero modal routes → empty list, no exception. Test AccessibilityViolation equality for deterministic output. Aim for 100% branch coverage on the verifier logic.

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.