high priority medium complexity frontend pending frontend specialist Tier 4

Acceptance Criteria

When errorCount increases, SemanticsService.announce() is called with a message in the format '{n} validation errors found' using TextDirection.ltr and assertive=true equivalent
When warningCount increases and errorCount is unchanged, an announcement is made with '{m} warnings found' at polite priority
When all issues are resolved (isEmpty becomes true), an announcement 'All validation issues resolved' is made at polite priority
Announcements are triggered by a Riverpod ref.listen on the validation summary provider — not on every widget rebuild
No announcement fires on initial widget mount if data is already empty
No duplicate announcements when the same data is set twice without change (equality guard on previous vs new value)
The Semantics widget wrapping the banner container has liveRegion: true set on the node that updates its label when counts change
Tested on VoiceOver (iOS) simulator: announcements audible without user focus on the banner
Tested on TalkBack (Android) emulator: assertive announcements interrupt current speech
WCAG 2.2 AA criterion 4.1.3 (Status Messages) satisfied: count changes communicated without receiving focus

Technical Requirements

frameworks
Flutter
Riverpod
performance requirements
Announcement logic runs outside the build method — inside ref.listen callback only
No additional setState calls triggered by announcement logic
security requirements
Announcement strings must not include user PII — counts and structural labels only
speech_to_text microphone guard: announcements must not activate microphone — use SemanticsService only
ui components
BufdirValidationSummaryBanner (extended with semantics)

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Flutter's SemanticsService.announce() is the correct cross-platform API for screen reader interrupts — do not use platform channels directly. Use ref.listen(validationProvider, (previous, next) { ... }) inside the build method with early-return guard for previous == next. For the liveRegion Semantics node, set liveRegion: true and update the label string reactively — Flutter will trigger the platform accessibility announcement automatically on label change on most platforms, but SemanticsService.announce() provides explicit control for assertive messages.

Test on real device if possible: iOS Simulator VoiceOver and Android Emulator TalkBack both support announcement testing but may differ from physical device behavior. Document any platform-specific quirks in code comments.

Testing Requirements

Widget tests using flutter_test semantics testing APIs: (1) verify SemanticsNode with liveRegion: true exists in tree when banner is visible; (2) use tester.pumpAndSettle and SemanticsController to assert label updates after provider state change; (3) inject a mock SemanticsService to verify announce() is called with correct string and assertiveness level on error count increase; (4) verify no announcement on initial mount with empty data; (5) verify equality guard: pumping identical state twice results in single announcement. Run tests with tester.ensureSemantics() enabled. Manual VoiceOver/TalkBack verification required before task sign-off.

Component
Validation Summary Banner
ui low
Epic Risks (2)
high impact medium prob technical

Implementing the tap-to-scroll-and-focus behavior from the validation banner to a specific field row in a long scrollable list is complex in Flutter. If focus management is incorrectly implemented, VoiceOver users who navigate to the banner and select an issue will not be moved to the relevant field row, breaking the accessibility workflow and violating WCAG 2.4.3 (Focus Order).

Mitigation & Contingency

Mitigation: Use BufdirAccessibilityUtils focus management utilities (built in the foundation epic) with explicit GlobalKey-based scroll anchors on each field row. Test with a real iOS device running VoiceOver during widget development, not only in the Flutter accessibility inspector.

Contingency: If programmatic scroll-to-focus cannot be reliably achieved before the TestFlight deadline, fall back to a navigation approach where tapping a banner issue opens a modal detail sheet for that field row rather than scrolling in place, and file a follow-up ticket for the inline scroll implementation.

medium impact low prob technical

The validation summary banner must reactively update its issue count as underlying aggregated data changes (e.g., if the coordinator has navigated away and data was refreshed). If the banner's Riverpod provider is not correctly scoped, it may display stale issue counts or fail to disappear when all issues are resolved, eroding coordinator trust in the validation system.

Mitigation & Contingency

Mitigation: Drive the banner exclusively from the same Riverpod provider that powers the full preview model — do not maintain a separate local state for issue counts. Write a widget test that simulates a data refresh mid-review and asserts the banner updates within one frame.

Contingency: If stale state reaches production, add a manual refresh button to the banner as a short-term workaround while the provider scoping is corrected in the next release cycle.