high priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

When a validation error appears on any wizard step field, the error message is announced via the live region within 150ms of the error becoming visible
The announcement format is '<field label>: <error message>' (e.g., 'Date: Date cannot be in the future')
If multiple errors appear simultaneously (e.g., user submits step with two empty required fields), all errors are announced in DOM order with a 500ms gap between each announcement
The error announcement uses assertive priority only for critical blocking errors (required field empty); non-blocking warnings use polite priority
The corresponding field's Semantics widget has its errorText property set, satisfying WCAG 2.2 criterion 3.3.1 (Error Identification)
When a validation error is cleared (field becomes valid), a brief 'resolved' announcement is NOT made — only errors are announced, not their clearance, to avoid announcement spam
Errors are NOT re-announced on widget rebuild if the error message has not changed
The implementation correctly handles all 5 wizard steps and their respective validation rules (contact required, date required and not future, time required, duration required and positive, summary character limit)
On the Contact step, the contact entity's required fields (first_name, last_name) produce correct error labels
WCAG 2.2 AA criterion 3.3.3 (Error Suggestion) is partially satisfied: error messages are descriptive enough to guide correction

Technical Requirements

frameworks
Flutter Semantics with errorText property
BLoC for validation state
Riverpod for LiveRegionAnnouncer injection
flutter_form_builder or reactive_forms (whichever is used in the project)
apis
Semantics(label: ..., errorText: ...) for WCAG error identification
SemanticsService.announce for live region
BlocListener for validation state changes
data models
activity
activity_type
contact
performance requirements
Error detection must use a BLoC state diff, not a timer poll — O(1) detection on state change
Multiple error announcements must be queued, not fired simultaneously, to avoid speech engine cutoff
security requirements
Validation error messages must not echo back raw user input in the announcement — e.g., say 'Invalid phone number' not 'Phone number 123abc is invalid'
Error messages for sensitive fields (contact names) must not include the actual field value in the announcement
ui components
ValidationErrorAnnouncer (composable service wrapping LiveRegionAnnouncer)
Per-step field error map (Map<String fieldKey, String? errorMessage>)
Semantics-wrapped form fields with errorText binding

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Listen to the wizard's validation BLoC state in a BlocListener inside ActivityWizardStepSemantics. Maintain a Map _previousErrors to diff against the new validation state — only announce errors that are new or changed. For queuing multiple announcements: use a simple Queue processed by a recursive Future chain with 500ms inter-announcement delay. Do NOT use Timer.periodic for this — a Future chain is cleaner and easier to test.

Assign each form field a stable string key (e.g., 'wizard_date', 'wizard_duration') that maps to a human-readable label in the localisation file. The Semantics errorText property on each field widget handles the WCAG static accessibility tree requirement; the live region announcement handles the dynamic audible alert. Both are required — do not assume one replaces the other. For the activity data model: date cannot be in the future (business rule from requirements).

Duration must be positive and non-zero per the activity entity constraints.

Testing Requirements

Unit tests: verify ValidationErrorAnnouncer.announceErrors(Map errors) queues announcements in field order with correct format; verify no re-announcement when called with identical error map twice. Widget tests: mount each wizard step with a mock validation BLoC; trigger a validation failure state and assert SemanticsService.announce was called with the correct message; assert Semantics.errorText is set on the failing field. Test multiple simultaneous errors and assert both are announced with gap. Test error clearance does not trigger an announcement.

Integration test: simulate a full wizard completion attempt with missing required fields, assert all errors are announced before the user can advance. Coverage target: 85%.

Component
Activity Wizard Step Semantics
ui medium
Epic Risks (3)
high impact high prob integration

Flutter does not natively enforce a focus trap within a bottom sheet or modal dialog in the semantic tree — VoiceOver and TalkBack can navigate outside the sheet to background content. Implementing a reliable focus trap requires overriding the semantic tree, which may conflict with the existing modal helper infrastructure in the app and require changes to shared components beyond this feature's scope.

Mitigation & Contingency

Mitigation: Prototype the focus trap on the first modal sheet implementation before building the remaining sheets. Evaluate Flutter's ExcludeSemantics and BlockSemantics widgets as the trap mechanism, and coordinate with the team owning the shared modal helpers to agree on a non-breaking integration point before writing production code.

Contingency: If a complete semantic focus trap cannot be implemented without breaking existing modal patterns, implement a partial solution using FocusScope with autofocus on the modal's first element and a prominent 'Return to main content' semantic action, documenting the deviation from WCAG 2.4.3 with a scheduled remediation item.

high impact medium prob technical

The activity wizard uses BLoC state management and the UI rebuilds the entire step widget subtree on transition. If the semantic tree is traversed by VoiceOver before the build cycle settles, focus may land on a stale or partially rendered step, causing the wrong step label or progress value to be announced. This is particularly problematic for blind users who cannot visually verify the announcement against the screen.

Mitigation & Contingency

Mitigation: Coordinate ActivityWizardStepSemantics with FocusManagementService (from the core services epic) to delay focus placement until the post-build callback confirms the new step's semantic tree is complete. Write integration tests using the AccessibilityTestHarness that assert the full announcement sequence across all five wizard steps.

Contingency: If post-build focus delay is insufficient due to async BLoC emission timing, add an explicit semantic notification barrier in the wizard cubit that emits a 'step ready' event only after the new widget tree has been marked as built, decoupling the announcement trigger from the raw state transition.

medium impact medium prob scope

Automated WCAG contrast ratio checking on widget tree snapshots may produce false positives for gradient backgrounds, dark-mode overrides, or design token overrides that are resolved at runtime but appear as unresolvable colours at static analysis time. Excessive false positives would erode team trust in the CI gate, leading to suppression rules that also mask real violations.

Mitigation & Contingency

Mitigation: Scope the WCAGComplianceChecker to check only solid-colour backgrounds in the first iteration, explicitly excluding gradients from contrast checks with documented rationale. Design the check output to distinguish 'undetermined' (gradient/unknown) from 'fail' (solid colour below threshold) so the team can take targeted action on genuine failures only.

Contingency: If false positive rates exceed 20% of reported violations during initial CI runs, switch the CI gate from a hard build failure to a warning annotation on the pull request, combined with a mandatory manual review step, until the checker's rule set has been tuned to match actual design token values.