critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

Abstract class DictationScopeGuard is defined with method bool isDictationPermitted() and ScopeGuardResult canStartDictation()
ScopeGuardResult is a sealed class with three variants: ScopeGuardAllowed, ScopeGuardBlockedActiveSession(String sessionId), ScopeGuardBlockedNoPermission
ActiveSessionState model is defined with fields: sessionId (String), isActive (bool), sessionType (SessionType enum), startedAt (DateTime)
SessionType enum includes at least: peerMentoringConversation, groupSession, adminSession
isDictationPermitted() is documented with explicit compliance note: returns false whenever an active peerMentoringConversation session exists
canStartDictation() returns ScopeGuardAllowed only when both permission is granted AND no active session is present
DictationScopeGuard interface has no dependency on NativeSpeechApiBridge — it is a separate concern
All types are defined in lib/features/speech/dictation_scope_guard.dart with zero platform-specific imports
Dart doc comments on DictationScopeGuard explicitly state the compliance rule: 'Audio capture during peer mentoring conversations is prohibited per organizational policy'
flutter analyze reports zero issues on the interface file

Technical Requirements

frameworks
Flutter
Dart
data models
DictationScopeGuard
ScopeGuardResult
ActiveSessionState
SessionType
performance requirements
isDictationPermitted() must be synchronous — no async operations — as it is called in UI build context
canStartDictation() must be synchronous for the same reason
security requirements
The compliance rule that audio capture is prohibited during active peer mentoring sessions must be enforced at this layer — not just in UI
ScopeGuardBlockedActiveSession must include the sessionId so audit logs can record which session blocked dictation
DictationScopeGuard must be the single authoritative source of truth for dictation permissibility — no other component should duplicate this logic

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Keep this interface strictly as a contract definition file — no implementation logic. The concrete implementation (which reads from Riverpod state and permission provider) is a separate task. Use Dart sealed classes for ScopeGuardResult to enforce exhaustive handling at every call site. The compliance note is not just documentation — it should appear as a static const String complianceRule = '...' field on DictationScopeGuard so the concrete implementation can include it in audit log entries.

The ActiveSessionState model should mirror the session data structure already used in the app's peer mentoring feature — reuse the existing model if one exists rather than defining a duplicate. Coordinate with the team to confirm whether 'active session' means any session record with status='active' in Supabase or an in-memory state held by a BLoC/Riverpod notifier — document this decision in the interface comments.

Testing Requirements

Unit tests must cover: (1) isDictationPermitted() returns false when ActiveSessionState.isActive is true and sessionType is peerMentoringConversation, (2) isDictationPermitted() returns true when no active session exists and permission is granted, (3) canStartDictation() returns ScopeGuardBlockedActiveSession with the correct sessionId when a session is active, (4) canStartDictation() returns ScopeGuardBlockedNoPermission when permission is not granted regardless of session state, (5) canStartDictation() returns ScopeGuardAllowed only when both conditions are met. Use a MockDictationScopeGuard in widget tests to control the guard's output. Assert exhaustive switch handling of ScopeGuardResult sealed class variants at compile time.

Component
Dictation Scope Guard
service low
Epic Risks (3)
high impact medium prob technical

iOS 15 on-device speech recognition has a 1-minute session limit and requires network fallback for longer sessions. Peer mentor way-forward dictation may routinely exceed this limit, causing silent truncation of transcribed content without user feedback.

Mitigation & Contingency

Mitigation: Implement session-chunking logic in NativeSpeechApiBridge that automatically restarts recognition before the limit is reached, preserving continuity via partial concatenation. Document the iOS 15 vs iOS 16 on-device recognition behaviour difference in code comments.

Contingency: If chunking causes user-visible interruptions, surface a non-blocking informational banner on iOS 15 devices informing users that very long dictation sessions may need to be broken into segments, and use PartialTranscriptionRepository to persist each chunk immediately.

high impact medium prob scope

On iOS, speech recognition permission can only be requested once. If the user denies the permission, the app cannot re-request it. A poor first-impression permission flow will permanently disable dictation for those users, impacting the Blindeforbundet blind-user base who rely on dictation most.

Mitigation & Contingency

Mitigation: Design the NativeSpeechApiBridge permission flow to show a clear pre-permission rationale screen before the OS dialog. Implement a graceful degradation path that hides the microphone button and shows a settings deep-link when permission is permanently denied.

Contingency: If users have already denied permission before the rationale screen is added, provide a settings deep-link in DictationScopeGuard's denial message directing users to iOS Settings > Privacy > Speech Recognition to re-enable manually.

medium impact low prob integration

The approved field IDs and screen routes configuration in DictationScopeGuard may fall out of sync with the actual report form schema as new fields are added by org administrators, silently blocking dictation on legitimately approved fields.

Mitigation & Contingency

Mitigation: Source the approved field configuration from the same org-field-config-loader used by the report form, rather than a hardcoded list. Add a developer-time assertion that logs a warning when a dictation-eligible field type is rendered but not in the approved routes map.

Contingency: Provide a runtime override mechanism in the scope guard that coordinators or admins can use to temporarily whitelist a field ID while the config is updated, with an automatic expiry.