Integrate DictationScopeGuard into state transitions
epic-speech-to-text-input-state-management-task-003 — Wire the DictationScopeGuard (component 661) into the TranscriptionStateManager so that any attempt to transition from DictationIdle to DictationRequestingPermission first invokes the scope guard's context validation. If the guard rejects the calling context (e.g., field is not post-activity), the state machine must transition to DictationError with a scope violation error code rather than proceeding. This ensures two-layer enforcement of the post-activity-only constraint.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Modify the startDictation() method added in task-002 to call guard.validate(fieldId) as its first async step, before any state transition. If the guard is synchronous, use a simple if-check; if it is async (returns Future
Document in a code comment why both the guard integration (here) and the internal secondary check (task-002) exist — they serve different layers of defence.
Testing Requirements
Unit tests extending the task-002 test suite. New test cases: (1) DictationScopeGuard returns false → startDictation() results in DictationError with scopeViolation code (not DictationRequestingPermission); (2) DictationScopeGuard returns true → startDictation() proceeds to DictationRequestingPermission; (3) DictationScopeGuard throws ScopeViolationException → same DictationError result; (4) guard is called exactly once per startDictation() invocation (use mock verification). Use a MockDictationScopeGuard (Mockito or manual fake) injected via constructor. All task-002 tests must remain green.
Target: 90% coverage on the guard integration code path.
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.