critical priority medium complexity infrastructure pending infrastructure specialist Tier 4

Acceptance Criteria

AccessibilityAuditReportSerializer.serialize(AuditResult result, String outputPath) writes a valid UTF-8 JSON file to outputPath; the output path is read from the ACCESSIBILITY_REPORT_PATH environment variable with a fallback default of build/accessibility-audit-report.json
The JSON report conforms to the defined schema: { runTimestamp, appVersion, totalViolations, violationsByCategory: { traversal, keyboard, touchTarget, navigationContract }, violations: [{ id, type, severity, routeName, widgetIdentifier, description, wcagCriteria }] }
severity field uses enum values: critical | high | medium | low — mapped from the task priority of the originating audit step
wcagCriteria field maps each violation type to its WCAG 2.2 AA criterion (e.g., touchTarget → '2.5.8', keyboard → '2.1.1', traversal → '1.3.2')
If the output directory does not exist, the serializer creates it; if the file already exists, it is overwritten
The report is valid JSON parseable by standard CI tooling (jq, Python json module, Node.js JSON.parse)
A run with zero violations produces a valid report with totalViolations=0 and an empty violations array — not an absent file
The serializer handles the case where an individual audit category threw an exception — that category is recorded with status: 'error' and an errorMessage field rather than crashing the entire report

Technical Requirements

frameworks
Flutter
dart:io
dart:convert
apis
File (dart:io)
jsonEncode (dart:convert)
Platform.environment (dart:io)
Directory.create(recursive: true)
data models
AccessibilityAuditResult
AccessibilityViolationReport
ViolationReportEntry
CategoryBreakdown
performance requirements
Serialization and file write must complete within 2 seconds for reports with up to 1000 violations
JSON output must be human-readable with 2-space indentation for developer inspection
security requirements
The output file must not contain any end-user personal data — only widget/route identifiers
Output path must be validated to prevent path traversal (reject paths with '..' components)

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Define the report schema as a Dart model class (AccessibilityViolationReport) with a toJson() method — do not build the JSON map manually. Use jsonEncode with a custom indent encoder or the json_serializable package if already in the project. Map violation severity to WCAG criteria using a static const map in a WcagCriteriaMapper helper. Read ACCESSIBILITY_REPORT_PATH via Platform.environment['ACCESSIBILITY_REPORT_PATH'] ??

'build/accessibility-audit-report.json'. Wrap each category's serialization in a try/catch and populate the error fields on failure. The runTimestamp should be ISO-8601 UTC. Include appVersion from the package_info_plus package if available, otherwise default to 'unknown'.

This report format will be used by the CI gate in task-012 and must be stable once defined — treat it as a public API.

Testing Requirements

Unit tests: verify schema correctness by deserializing the output and checking all required fields; verify the environment variable path override works; verify directory creation when path does not exist; verify graceful handling of a category with an error (no crash, error field present). Integration test: run a full AccessibilityAuditRunner execution against a test app, call the serializer, read the output file, parse JSON, and assert violation counts match the in-memory AuditResult. Coverage target: 95% of serialization logic. Edge case: test with 0 violations, test with 1000 violations (performance), test with a null wcagCriteria mapping (should default to 'Unknown').

Component
Accessibility Audit Runner
infrastructure 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.