Integrate PartialTranscriptionRepository for crash recovery
epic-speech-to-text-input-state-management-task-005 — Wire the PartialTranscriptionRepository (component 662) into the state manager so that every partial transcript emission during DictationRecording is persisted. On TranscriptionStateManager initialisation for a given field ID, check the repository for any existing partial session and, if found, restore the in-progress transcript and emit DictationProcessing state to signal recovery. Ensure that on successful DictationComplete the partial record is cleared from the repository. This guarantees content survival across phone backgrounding and incoming-call interruptions.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
Implement debounce using a Timer? _persistDebounce field: cancel and reset the timer on each PartialResult, fire the upsert only when the timer fires. Choose 300ms as the debounce interval (configurable via a const). Place the initial recovery check in an async init method called from the StateNotifier constructor body using Future.microtask(() async { ...
}) so it does not block provider instantiation. The abstract interface for PartialTranscriptionRepository (already hinted at in task-004) must have at minimum: Future
Testing Requirements
Unit tests using flutter_test with a fake PartialTranscriptionRepository (in-memory implementation for tests). Test cases: (1) on init with existing partial record → state is DictationProcessing with correct transcript text; (2) on init with no partial record → state is DictationIdle; (3) on init with repository error → state is DictationIdle (safe degradation); (4) PartialResult event triggers debounced upsert — verify upsert called after debounce interval using fake async; (5) DictationComplete → deleteByFieldId called; (6) cancelDictation() → deleteByFieldId called; (7) upsert error does not change state machine state; (8) concurrent partial emissions within debounce window result in exactly one upsert. Use fake_async package for debounce testing. Minimum 90% coverage on repository integration code paths.
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.
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.