critical priority medium complexity frontend pending frontend specialist Tier 4

Acceptance Criteria

When the app is cold-started from a notification tap, FirebaseMessaging.instance.getInitialMessage() is called during initialization and its result is stored
Deep link navigation is deferred until both the go_router initial route and the auth state are confirmed as loaded
If the target resource (assignment_id, contact_id, etc.) does not exist in Supabase, navigation falls back to the home route with no crash
If the user is unauthenticated after cold start, the pending deep link is preserved and applied after successful login
Role guard violations (e.g., peer mentor receiving a coordinator-only route) redirect to the role-appropriate home screen
Cold-start navigation is tested on both iOS (TestFlight) and Android device builds
getInitialMessage() is called exactly once per app lifecycle — not on every hot restart
No duplicate navigation events occur when both getInitialMessage() and onMessageOpenedApp stream both fire

Technical Requirements

frameworks
Flutter
go_router
BLoC
Riverpod
firebase_messaging
apis
FirebaseMessaging.instance.getInitialMessage()
FirebaseMessaging.onMessageOpenedApp stream
Supabase REST API (resource existence check)
data models
NotificationPayload
DeepLinkRoute
AuthState
performance requirements
Deep link resolution must not delay app first-frame render — defer navigation asynchronously
Resource existence check must complete before navigation push to avoid navigating to a 404 screen
Auth state readiness check must use a stream listener with a timeout of 5 seconds before falling back to home
security requirements
Validate that the route resolved from the payload corresponds to a resource the current user has permission to view
Never trust the notification payload's role_slug alone — re-verify against the authenticated user's actual role from Supabase
ui components
LoadingOverlay (while resource existence is verified)
HomeScreen (fallback destination)

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Use a PendingDeepLink notifier (Riverpod StateNotifier or BLoC) to hold the cold-start payload. In main.dart, after WidgetsFlutterBinding.ensureInitialized(), call getInitialMessage() and store the result in the notifier before runApp(). In your auth listener (StreamSubscription on Supabase auth state changes), check PendingDeepLink after the user is confirmed authenticated and the router has completed its initial navigation. Use a completer or ChangeNotifier flag for 'router ready' signaling.

Avoid calling context.go() before the router's first frame — use addPostFrameCallback or a router listener. On iOS, getInitialMessage() only fires if the app was fully terminated (not backgrounded), so test both states during QA.

Testing Requirements

Unit tests (flutter_test) using mocked FirebaseMessaging and go_router: valid cold-start payload resolves and navigates to correct route; missing resource_id in payload navigates to home; resource not found in Supabase navigates to home; unauthenticated state defers link and applies after login; role guard violation redirects to role home; getInitialMessage() returning null is handled gracefully. Widget tests verifying no duplicate navigation when both getInitialMessage() and onMessageOpenedApp both have values. Manual validation on a physical TestFlight device to confirm cold-start behavior, as emulators do not fully replicate FCM cold-start.

Component
Notification Deep Link Handler
service medium
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.