high priority low complexity infrastructure pending infrastructure specialist Tier 0

Acceptance Criteria

NotificationAccessibilityConfiguration defines a semantic label and a screen-reader description for each notification category: new_assignment, deadline_approaching, status_change
Semantic labels are concise (≤ 40 characters) and screen-reader descriptions are informative full sentences explaining the notification purpose
flutter_local_notifications Android notification channels are created with: channel ID, channel name, channel description, importance level, sound enabled/disabled flag, vibration enabled/disabled flag, and LED colour
Channel importance levels: deadline_approaching → Importance.max; new_assignment → Importance.high; status_change → Importance.defaultImportance
iOS notification categories are configured with matching identifiers and action options
All channel descriptions and semantic labels are retrievable via a static method getAccessibilityConfig(NotificationCategory) to allow UI components to reference them
Configuration is a pure Dart class with no Flutter UI dependency — it can be tested without a widget tree
Verified manually on iOS (VoiceOver) and Android (TalkBack) that notifications are announced with the correct semantic description
Channel setup is idempotent — calling initializeChannels() multiple times does not create duplicate channels or throw errors

Technical Requirements

frameworks
Flutter
flutter_local_notifications
apis
flutter_local_notifications AndroidNotificationChannel
flutter_local_notifications DarwinNotificationCategory
flutter_local_notifications FlutterLocalNotificationsPlugin
data models
NotificationCategory (enum)
NotificationAccessibilityConfig (value object)
performance requirements
initializeChannels() completes synchronously or within one async tick — no network calls
getAccessibilityConfig() is a pure synchronous lookup — O(1) map access
security requirements
No user data is included in channel configuration — descriptions are generic category-level labels only
Notification channel setup does not request additional permissions beyond those declared in AndroidManifest.xml

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Define NotificationAccessibilityConfig as an immutable value object (const constructor, all final fields) with: semanticLabel, screenReaderDescription, androidImportance, soundEnabled, vibrationEnabled, ledColor. Store the category-to-config mapping as a static const Map inside NotificationAccessibilityConfiguration to keep it readable and avoid factory complexity. For flutter_local_notifications channel init, call resolvePlatformSpecificImplementation()?.createNotificationChannel() for each channel inside initializeChannels(). This must be called after FlutterLocalNotificationsPlugin.initialize() and before the first notification is shown — typically in main.dart or an app-lifecycle-aware service.

Per the workshop notes, Blindeforbundet users heavily depend on VoiceOver/JAWS and NHF users include people with cognitive impairments — keep semantic labels plain-language and avoid technical jargon. Deadline notifications should be highest importance (Importance.max with heads-up display) given the time-sensitive nature of assignment deadlines.

Testing Requirements

Unit tests (no widget environment needed): (1) getAccessibilityConfig returns non-null config for every defined NotificationCategory; (2) each config has non-empty semanticLabel (≤ 40 chars) and non-empty screenReaderDescription; (3) Android channel configuration has correct importance level per category; (4) initializeChannels() can be called twice without throwing. Manual QA checklist: enable VoiceOver (iOS) and TalkBack (Android), trigger a test notification for each category, verify the announced text matches the configured screenReaderDescription. Document QA results in PR description.

Component
Notification Accessibility Configuration
infrastructure low
Epic Risks (4)
high impact high prob technical

Flutter's background message handler for FCM must run in a separate Dart isolate. Incorrect dependency initialization in the isolate (e.g., attempting to access Riverpod providers or Supabase before initialization) will cause silent crashes on Android when the app is terminated, resulting in missed notifications that are invisible in crash reporting.

Mitigation & Contingency

Mitigation: Use a minimal top-level background handler function annotated with @pragma('vm:entry-point') that only stores the raw RemoteMessage payload to a platform channel or shared preferences. Process the payload in the main isolate on next app launch. Write an explicit test for terminated-state message handling on Android.

Contingency: If isolate crashes are observed, implement a native Android FirebaseMessagingService subclass that handles background messages without Flutter isolate complexity, falling back to a database-insert-only approach for terminated-state notifications.

medium impact medium prob technical

Supabase Edge Functions can experience cold-start latency of 1–3 seconds after periods of inactivity. For high-frequency events like assignment creation, cumulative cold starts could cause dispatch delays exceeding the 30-second SLA, reducing the perceived reliability of the notification system.

Mitigation & Contingency

Mitigation: Configure the Edge Function with a keep-warm ping mechanism or use Supabase database webhooks that invoke the function directly on row insert to minimize cold-start frequency. Batch preference lookups within the function to reduce per-invocation Supabase round-trips.

Contingency: If latency SLA is consistently breached, move to a polling or Realtime-subscription architecture within the Edge Function, or pre-compute dispatch targets at preference-save time to eliminate per-dispatch preference queries.

high impact low prob security

If the deep link handler does not perform server-side role validation before rendering the target screen, a peer mentor who receives a mis-configured notification payload containing a coordinator-only route could access restricted data, violating the role-based access control invariants.

Mitigation & Contingency

Mitigation: The deep link handler must check the user's current role from the RoleStateManager before constructing the navigation route. Coordinator-only routes must be listed in a deny-list checked against the current role. The go_router route guard is a second line of defence.

Contingency: If a role bypass is discovered in testing, immediately add the affected route to the deep link handler deny-list and add a regression test. Audit all notification payload types for route targets that could expose cross-role data.

medium impact low prob dependency

FCM v1 HTTP API enforces per-project send quotas. For large organisations with many active peer mentors receiving simultaneous assignment notifications, batch dispatch events (e.g., bulk coordinator assignments) could approach quota limits and result in dropped notifications with 429 errors logged silently.

Mitigation & Contingency

Mitigation: Implement exponential backoff retry logic in the Edge Function for 429 responses. Design bulk assignment flows to dispatch notifications in batches with a configurable delay between batches. Monitor FCM console quotas during load testing.

Contingency: If quota limits are hit, implement a notification queue table in Supabase and a separate Edge Function that processes the queue with rate limiting, ensuring eventual delivery without exceeding FCM quotas.