critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

A Dart sealed class DictationState is declared and annotated with @freezed; the freezed code generator produces the corresponding .freezed.dart file without errors
All six concrete subtypes exist: DictationIdle, DictationRequestingPermission, DictationRecording, DictationProcessing, DictationComplete, DictationError
DictationIdle carries no payload (const factory DictationIdle())
DictationRequestingPermission carries no payload
DictationRecording carries a String interimTranscript (nullable or empty-string default) representing live partial results
DictationProcessing carries a String partialTranscript representing the last known text before processing began
DictationComplete carries a String finalTranscript (non-nullable, non-empty)
DictationError carries a DictationErrorCode enum value and a String message; DictationErrorCode must include at minimum: permissionDenied, scopeViolation, recognitionFailed, sessionInterrupted, timeout
A switch expression on DictationState without a default/wildcard branch compiles with no 'non-exhaustive switch' warnings, confirming sealed exhaustive matching
All types are exported from a single barrel file (e.g., dictation_state.dart) so consumers import one path
flutter analyze produces zero warnings on the generated and hand-written files

Technical Requirements

frameworks
Flutter
Freezed (freezed_annotation, build_runner)
performance requirements
All state subtypes must be immutable (const constructors where possible) to enable value equality checks in BLoC/Riverpod without custom equals implementations
Freezed-generated copyWith must be available on all subtypes carrying mutable payload for efficient state updates
security requirements
DictationError must not expose raw platform error messages that could leak internal implementation details; the message field should be a localisation key or a sanitised human-readable string

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Place the file at lib/features/dictation/models/dictation_state.dart. Follow the project's existing Freezed pattern — check whether the project uses @Freezed() or @freezed and match. Use Dart 3 sealed class syntax (sealed class DictationState with _$DictationState) combined with Freezed's union factory pattern. Run dart run build_runner build --delete-conflicting-outputs after generating.

If the project uses a custom lint ruleset, ensure the generated file is excluded via analysis_options.yaml exclude pattern. The DictationErrorCode enum should be defined in the same file or a co-located error_codes.dart — do not scatter it across unrelated files. Avoid making DictationState extend any Flutter framework class; it is a pure domain model.

Testing Requirements

Unit tests only. Write one test file (dictation_state_test.dart) with the following cases: (1) assert DictationIdle == DictationIdle() value equality; (2) assert DictationComplete('hello') != DictationComplete('world'); (3) assert exhaustive switch compiles and routes each subtype to the correct branch; (4) assert DictationError.copyWith can update the message field; (5) assert DictationErrorCode contains all required enum values. Minimum coverage: all six state classes and DictationErrorCode exercised. All tests must pass with flutter test.

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.