critical priority medium complexity integration pending backend specialist Tier 2

Acceptance Criteria

TranscriptionStateManager subscribes to SpeechRecognitionService.eventStream (or equivalent) in its constructor or on first startDictation() call; the subscription is stored and cancelled in dispose()
SpeechStarted event → state transitions from DictationRequestingPermission to DictationRecording with empty interimTranscript
PartialResult(text) event → state updates to DictationRecording(interimTranscript: text); if current state is not DictationRecording, the event is ignored with a debug log
FinalResult(text) event → state transitions to DictationComplete(finalTranscript: text); if text is empty, transition to DictationError(recognitionFailed)
RecognitionError(code, message) event → state transitions to DictationError(code: DictationErrorCode.recognitionFailed, message: message)
SessionInterrupted event → partial transcript is persisted via PartialTranscriptionRepository before any state transition; after persist completes, state transitions to DictationError(sessionInterrupted) or DictationIdle (configurable)
All event-to-state mappings are documented with inline comments referencing the event source
No event causes a transition that bypasses the DictationScopeGuard (already enforced in startDictation, not re-evaluated on events)
Disposing the provider while in DictationRecording persists partial transcript via PartialTranscriptionRepository
flutter analyze produces zero warnings on changed files

Technical Requirements

frameworks
Flutter
Riverpod (flutter_riverpod)
Freezed
apis
SpeechRecognitionService event stream (component 659)
PartialTranscriptionRepository (component 662 — referenced but full integration in task-005)
performance requirements
PartialResult events may fire at up to 10Hz on some devices; the subscription handler must not perform synchronous I/O or heavy computation — persist operations must be async and non-blocking
FinalResult handling must complete within 200ms from event receipt to DictationComplete state emission
security requirements
Transcript content must never be logged at INFO level or above — debug logging only, and must be feature-flagged off in production builds
SessionInterrupted persist must use PartialTranscriptionRepository's secure local storage — do not write transcript to plain SharedPreferences or app logs

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Add a private _subscribeToSpeechEvents() method called from startDictation() after the guard passes and permission is requested. Store the subscription as StreamSubscription? _speechEventSubscription and cancel it in dispose(). Use a switch expression on the event type for exhaustive event mapping — this prevents silent misses when new event types are added.

For SessionInterrupted, call await _partialRepo.save(fieldId, currentTranscript) inside the event handler before transitioning state; use a try/catch around the persist so a repository failure does not prevent the error state transition. PartialTranscriptionRepository injection at this stage can be a stub/placeholder until task-005 provides the full implementation — define the interface contract (abstract class) so tests can mock it. Avoid using scheduleMicrotask or Timer.run in the event handler; prefer direct state transitions to keep the state machine deterministic.

Testing Requirements

Unit tests using flutter_test with a fake/mock SpeechRecognitionService that exposes a StreamController. Test cases: (1) SpeechStarted → DictationRecording; (2) PartialResult in valid state → DictationRecording with updated interimTranscript; (3) PartialResult in invalid state (e.g., DictationIdle) is ignored; (4) FinalResult with non-empty text → DictationComplete; (5) FinalResult with empty text → DictationError(recognitionFailed); (6) RecognitionError → DictationError; (7) SessionInterrupted → PartialTranscriptionRepository.save() called then DictationError; (8) dispose() while recording → repository.save() called. Use expectLater with emitsInOrder for stream assertions. Minimum 90% line coverage on the event subscription handler code.

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.