medium priority medium complexity testing pending testing specialist Tier 6

Acceptance Criteria

Integration test starts dictation on fieldId 'field-A' and 'field-B' concurrently and verifies both reach recording state independently
After interruption on 'field-A', the test reads PartialTranscriptionRepository for 'field-A' and confirms persisted text matches the last partial emit from the fake NativeSpeechApiBridge
After interruption, 'field-B' remains in recording state and continues to receive transcript updates — test confirms state divergence between the two fields
Re-opening 'field-A' session restores partial text as the initial state value — test reads initial state on provider re-creation and confirms non-empty partialTranscript
WCAG live-region announcer (component 664) receives at least one notification for each of the following transitions: idle→recording, recording→processing, processing→complete, and any→error
Live-region notification content matches the expected human-readable announcement string for each transition (e.g., 'Recording started', 'Processing speech', 'Transcription complete')
Integration test uses a FakeNativeSpeechApiBridge that does not invoke platform channels — test passes on CI without a physical device
Test uses flutter_test's pumpWidget/pump cycle with a real ProviderScope wrapping a minimal widget scaffold

Technical Requirements

frameworks
Flutter
Riverpod
flutter_test
apis
NativeSpeechApiBridge (fake implementation)
PartialTranscriptionRepository (in-memory fake)
WCAG live-region announcer API (component 664)
data models
TranscriptionState
PartialTranscript
AccessibilityAnnouncement
performance requirements
Integration test must complete in under 30 seconds on CI
Fake bridge must not introduce real delays — use synchronous stream controllers
security requirements
No platform channel invocations in CI — fake bridge must stub all native calls
In-memory repository must clear state between test runs
ui components
Minimal report field scaffold widget hosting two DictationMicrophoneButton instances

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

The key challenge is making the fake bridge controllable from the test body. Expose `pushTranscript(String fieldId, String text)` and `triggerError(String fieldId, Exception e)` methods on `FakeNativeSpeechApiBridge`. Inject via ProviderScope override: `transcriptionBridgeProvider.overrideWithValue(fakeBridge)`. For the live-region verification, the announcer should be a Riverpod provider itself (or an inherited widget), so it can be overridden with `FakeAccessibilityAnnouncer` in tests.

Use `pump()` calls between state mutations to allow the widget tree to rebuild and subscriptions to fire. Avoid `pumpAndSettle()` if animations are involved — pump a fixed number of frames instead to keep the test deterministic.

Testing Requirements

Use flutter_test's testWidgets() for this integration test so the full ProviderScope + widget tree is exercised. Create `FakeNativeSpeechApiBridge` that exposes a `StreamController` for pushing transcript segments programmatically. Create `InMemoryPartialTranscriptionRepository` for verifiable in-process persistence. Spy on component 664's announcer by injecting a `FakeAccessibilityAnnouncer` that records all announced strings.

Assert announcement history after each pump cycle. Organize into a single test file `dictation_integration_test.dart` under `test/integration/`.

Component
Transcription State Manager
service medium
Epic Risks (2)
medium impact low prob technical

If a peer mentor rapidly switches between dictation-enabled fields while a session is still processing, the Riverpod family provider may share state or the SpeechRecognitionService may receive conflicting start/stop commands, causing orphaned recording sessions or state corruption.

Mitigation & Contingency

Mitigation: Design the state manager to enforce a single active dictation session globally via a shared active-field-key notifier. When a new field requests dictation while another session is active, automatically issue a stop command to the existing session and await completion before starting the new one. Test this concurrency scenario explicitly.

Contingency: If concurrency issues persist in integration testing, add a global dictation mutex at the SpeechRecognitionService level that serialises all start requests, with a short debounce to handle rapid field switching.

medium impact medium prob scope

The recovery flow for interrupted dictation sessions (app restart with persisted partials) has ambiguous UX requirements. If recovery is automatic and the user has already typed different content into the field, the merge logic may overwrite or duplicate user input, causing data loss.

Mitigation & Contingency

Mitigation: Define the recovery behaviour explicitly: recovery should surface as an opt-in prompt ('You have unsaved dictation — insert or discard?') rather than an automatic merge. The state manager emits a dedicated recoverable-partial state that the UI layer renders as a non-blocking action sheet.

Contingency: If the recovery UI adds too much complexity for the initial release, scope recovery to display only the partial text in the preview field on re-open without auto-insertion, letting the user manually copy if needed, and defer the automatic recovery prompt to a subsequent iteration.