high priority low complexity integration pending backend specialist Tier 2

Acceptance Criteria

DictationScopeGuard is injected into TranscriptionStateManager via the constructor; the state manager does not instantiate it directly (dependency inversion)
Before any DictationIdle → DictationRequestingPermission transition, TranscriptionStateManager calls DictationScopeGuard.validate(fieldId) (or equivalent method signature from component 661)
If DictationScopeGuard.validate() returns false / throws a ScopeViolationException, the state machine immediately transitions to DictationError(code: DictationErrorCode.scopeViolation, message: <localised string>) without ever reaching DictationRequestingPermission
If DictationScopeGuard.validate() returns true, the transition proceeds normally to DictationRequestingPermission
The integration does not add any new public methods to TranscriptionStateManager — scope validation is an internal concern of startDictation()
Calling startDictation() on a field that is in the middle of an active activity (not post-activity) always results in DictationError state with scopeViolation code
Calling startDictation() on a valid post-activity field proceeds to DictationRequestingPermission and beyond normally
All existing unit tests from task-002 continue to pass after integration; new tests cover the two guard paths
flutter analyze produces zero warnings

Technical Requirements

frameworks
Flutter
Riverpod (flutter_riverpod)
apis
DictationScopeGuard internal API (component 661)
performance requirements
DictationScopeGuard.validate() must complete synchronously or return a Future that resolves in under 50ms — it must not block the UI thread on a database call
security requirements
Two-layer enforcement: DictationScopeGuard is the primary guard (component 661 owns the context rules); TranscriptionStateManager's internal check is a secondary fallback — both must independently reject invalid contexts
Scope validation failure must never silently fail — it must always result in an observable DictationError state so the UI can display feedback to the user
Microphone must never be requested from the OS if scope validation fails — the OS permission dialog must not appear for invalid contexts

Execution Context

Execution Tier
Tier 2

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), await it and keep the method async. Do not catch every exception broadly — only catch ScopeViolationException (or whatever the guard's rejection type is) and map it to DictationError; let unexpected exceptions propagate so they are visible in crash reporting. Update the Riverpod provider to also inject dictationScopeGuardProvider into the TranscriptionStateManager constructor.

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.

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.