critical priority high complexity backend pending backend specialist Tier 0

Acceptance Criteria

SensitiveFieldPrivacyGuard is implemented as a Riverpod service (StateNotifier or Notifier) injectable across the widget tree without relying on global singletons
The service exposes a shouldShowWarning(fieldKey: String) method that returns true if: (a) the field is registered in SensitiveFieldConfiguration as requiring a warning, AND (b) ScreenReaderDetectionService reports a screen reader is active, AND (c) the warning has not been dismissed in the current session
Session-level suppression is stored in memory only (not persisted to disk or Supabase); it resets automatically when the app is cold-started
dismissWarning(fieldKey: String) marks the warning as suppressed for the session; subsequent calls to shouldShowWarning for the same fieldKey return false without accessing ScreenReaderDetectionService
dismissWarning called with an unregistered fieldKey is a no-op and does not throw
ScreenReaderDetectionService is injected as a dependency (not instantiated inside the service) to allow mocking in tests
SensitiveFieldConfiguration is injected as a dependency; the service does not hardcode any field keys
If ScreenReaderDetectionService.isScreenReaderActive() throws or returns an error, shouldShowWarning defaults to false (fail-safe: do not block normal app usage)
The service is stateless between sessions: no database writes, no shared preferences reads/writes
Unit tests cover all branches of shouldShowWarning (screen reader off, screen reader on + not dismissed, screen reader on + dismissed, missing field config, service error)
The service compiles without errors and is wired into the Riverpod provider graph with correct scoping (app-level provider)

Technical Requirements

frameworks
Flutter
Riverpod
apis
ScreenReaderDetectionService — isScreenReaderActive() → bool
SensitiveFieldConfiguration — requiresWarning(fieldKey: String) → bool
data models
SensitiveFieldKey (String typedef or enum of registered field identifiers)
SessionSuppressionState (internal: Map<String, bool> dismissed fields)
performance requirements
shouldShowWarning must resolve synchronously (no async gaps) when ScreenReaderDetectionService returns synchronously; if the detection service is async, cache its result at service initialisation
Session suppression map lookups must be O(1); use a HashSet or Map, not a List
security requirements
The service must never log, transmit, or store the values of sensitive fields — only the fieldKey identifiers (non-PII metadata)
Session suppression state must not be exposed via a public getter that reveals which sensitive fields exist; expose only shouldShowWarning(fieldKey) to limit surface area
The warning flow exists specifically because organisations (NHF, Blindeforbundet) have users who rely on screen readers and must be warned before sensitive personal data (name, address, medical summary) is announced aloud; failing silently is safer than blocking the user

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Implement using Riverpod Notifier (not StateNotifier, which is deprecated in Riverpod 2.x) for forward compatibility. Keep the service as a pure Dart class with no Flutter import dependencies — this makes it trivially unit-testable without pumpWidget overhead. Cache ScreenReaderDetectionService.isScreenReaderActive() result at service build() time and re-evaluate on accessibility change events if the detection service exposes a stream; otherwise re-query on each shouldShowWarning call but wrap in a try/catch. The session suppression map should be a private final HashSet _dismissed; dismissWarning adds to it, shouldShowWarning checks containment.

Do not use a boolean per-field map initially — a HashSet is simpler and sufficient since undismiss is not a use case. Place the service in lib/features/accessibility/services/sensitive_field_privacy_guard.dart following the existing feature-layer directory convention. Register the provider in the accessibility providers barrel file. This service is the foundation for the screen reader support epic — correctness and testability here are more important than optimisation.

Testing Requirements

Write unit tests using flutter_test (no UI pump required) covering all shouldShowWarning branches: (1) screen reader inactive → returns false regardless of config; (2) screen reader active + field not in config → returns false; (3) screen reader active + field in config + not dismissed → returns true; (4) screen reader active + field in config + dismissed → returns false; (5) ScreenReaderDetectionService throws → returns false (fail-safe); (6) dismissWarning with unknown key is no-op, no exception. Mock both ScreenReaderDetectionService and SensitiveFieldConfiguration using mocktail. Verify that session state does not bleed between test cases (create a fresh provider container per test). Aim for 100% branch coverage on the service logic.

No widget or integration tests in this task — those are handled by downstream tasks in the epic.

Component
Sensitive Field Privacy Guard
service high
Epic Risks (3)
high impact high prob integration

Flutter does not natively enforce a focus trap within a bottom sheet or modal dialog in the semantic tree — VoiceOver and TalkBack can navigate outside the sheet to background content. Implementing a reliable focus trap requires overriding the semantic tree, which may conflict with the existing modal helper infrastructure in the app and require changes to shared components beyond this feature's scope.

Mitigation & Contingency

Mitigation: Prototype the focus trap on the first modal sheet implementation before building the remaining sheets. Evaluate Flutter's ExcludeSemantics and BlockSemantics widgets as the trap mechanism, and coordinate with the team owning the shared modal helpers to agree on a non-breaking integration point before writing production code.

Contingency: If a complete semantic focus trap cannot be implemented without breaking existing modal patterns, implement a partial solution using FocusScope with autofocus on the modal's first element and a prominent 'Return to main content' semantic action, documenting the deviation from WCAG 2.4.3 with a scheduled remediation item.

high impact medium prob technical

The activity wizard uses BLoC state management and the UI rebuilds the entire step widget subtree on transition. If the semantic tree is traversed by VoiceOver before the build cycle settles, focus may land on a stale or partially rendered step, causing the wrong step label or progress value to be announced. This is particularly problematic for blind users who cannot visually verify the announcement against the screen.

Mitigation & Contingency

Mitigation: Coordinate ActivityWizardStepSemantics with FocusManagementService (from the core services epic) to delay focus placement until the post-build callback confirms the new step's semantic tree is complete. Write integration tests using the AccessibilityTestHarness that assert the full announcement sequence across all five wizard steps.

Contingency: If post-build focus delay is insufficient due to async BLoC emission timing, add an explicit semantic notification barrier in the wizard cubit that emits a 'step ready' event only after the new widget tree has been marked as built, decoupling the announcement trigger from the raw state transition.

medium impact medium prob scope

Automated WCAG contrast ratio checking on widget tree snapshots may produce false positives for gradient backgrounds, dark-mode overrides, or design token overrides that are resolved at runtime but appear as unresolvable colours at static analysis time. Excessive false positives would erode team trust in the CI gate, leading to suppression rules that also mask real violations.

Mitigation & Contingency

Mitigation: Scope the WCAGComplianceChecker to check only solid-colour backgrounds in the first iteration, explicitly excluding gradients from contrast checks with documented rationale. Design the check output to distinguish 'undetermined' (gradient/unknown) from 'fail' (solid colour below threshold) so the team can take targeted action on genuine failures only.

Contingency: If false positive rates exceed 20% of reported violations during initial CI runs, switch the CI gate from a hard build failure to a warning annotation on the pull request, combined with a mandatory manual review step, until the checker's rule set has been tuned to match actual design token values.