high priority medium complexity frontend pending frontend specialist Tier 5

Acceptance Criteria

PeerMentorDetailScreen is registered as a GoRoute with path '/contacts/peer-mentor/:mentorId' nested within the Contacts branch of the StatefulShellRoute
Route parameters are correctly typed: mentorId is extracted as a non-empty String from GoRouterState.pathParameters['mentorId'] and validated before BLoC initialization
Navigating to the detail screen via context.push('/contacts/peer-mentor/$mentorId') from the contacts list works without errors on both iOS and Android
Back navigation (system back button on Android, swipe-back on iOS, or AppBar back button) returns the user to the contacts list screen
The Contacts tab remains selected in the bottom navigation bar after returning from the detail screen — StatefulShellRoute tab state is preserved
No other tab's navigation stack is affected when the Contacts tab navigates to and back from the detail screen
PeerMentorDetailBLoC is provided via BlocProvider scoped inside the route builder, so it is created fresh for each navigation to the detail screen
PeerMentorDetailBLoC is disposed when the route is popped — confirmed by adding a debug print in BLoC close() and observing it fires on pop
Deep-linking to the path '/contacts/peer-mentor/:mentorId' from outside the app launches the correct screen with the correct tab selected
Invalid or missing mentorId in the path triggers a redirect to the contacts list with an optional error snackbar rather than a runtime exception

Technical Requirements

frameworks
Flutter
GoRouter
BLoC (flutter_bloc)
data models
PeerMentorDetailBLoC
GoRouterState
performance requirements
Route transition animation must complete within 300ms (default Flutter push transition)
BLoC initialization and first event dispatch must occur synchronously in the route builder to avoid a blank frame
security requirements
mentorId path parameter must be validated as a UUID or known ID format before being used in any Supabase query — prevent path traversal or injection via crafted deep links
Deep link handling must respect authentication state — unauthenticated deep links must redirect to the login screen, not the detail screen

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Define the route inside the Contacts ShellRoute branch by adding a GoRoute entry: `GoRoute(path: 'peer-mentor/:mentorId', builder: (context, state) { final mentorId = state.pathParameters['mentorId'] ?? ''; if (mentorId.isEmpty) return const ContactsListScreen(); return BlocProvider(create: (_) => PeerMentorDetailBLoC(mentorId: mentorId)..add(LoadPeerMentorDetail()), child: const PeerMentorDetailScreenWidget()); })`. Use a relative path (without leading slash) since it is nested. The BlocProvider must be inside the builder callback (not at the app level) to ensure it is scoped per route instance and disposed automatically when the route leaves the navigator stack.

Do not use BlocProvider.value for this — use the create factory so the BLoC lifecycle is tied to the route. Verify that the bottom navigation bar's StatefulShellRoute index does not reset to 0 when navigating within the Contacts branch by testing with Navigator.of(context).canPop().

Testing Requirements

Write GoRouter integration tests using GoRouter's test utilities. Assert that navigating to '/contacts/peer-mentor/test-id-123' renders PeerMentorDetailScreenWidget with mentorId 'test-id-123'. Assert that popping the route renders the contacts list. Test that the BLoC is disposed on pop by subclassing PeerMentorDetailBLoC with a dispose callback in tests.

Test deep-link navigation with a mock router setup. Test that an invalid mentorId (empty string) redirects rather than crashing. Run on both iOS and Android simulators to verify StatefulShellRoute tab state persistence.

Component
Peer Mentor Detail Screen
ui medium
Epic Risks (3)
medium impact medium prob technical

The parallel Future.wait aggregation pattern may produce race conditions or incorrect merged state when some repositories resolve significantly faster than others, particularly if the BLoC receives a RefreshDetail event while a prior fetch is still in flight.

Mitigation & Contingency

Mitigation: Implement cancellation token pattern in the aggregation service to abort in-flight fetches on new events. Add BLoC test scenarios for rapid successive refresh events to validate state consistency.

Contingency: If race conditions persist, switch to a sequential-with-timeout fetch strategy for the first release and profile the performance impact before deciding whether parallel fetch optimization is worth the complexity.

medium impact medium prob integration

Integrating PeerMentorDetailScreenWidget into the existing StatefulShellRoute navigation structure may conflict with the Contacts tab's existing route hierarchy, requiring changes to navigation-route-config that could affect other teams' features.

Mitigation & Contingency

Mitigation: Coordinate with the Contact List and Contact Detail feature teams before adding the new route. Review the existing StatefulShellRoute configuration and confirm the peer mentor detail route can be nested under the Contacts branch without path conflicts.

Contingency: If route conflicts arise, temporarily implement the peer mentor detail as a modal overlay (push route) rather than a shell route child, preserving functionality while the navigation architecture conflict is resolved.

low impact high prob dependency

The course enrollment screen that the certification alert banner links to may not yet exist or may be implemented in a separate feature epic, leaving a broken navigation tap for HLF users in the initial release.

Mitigation & Contingency

Mitigation: Check the certification management feature implementation status before finalizing Epic 4 scope. If the enrollment screen is not available, design the tap action to open the HLF course enrollment URL in an external browser as an interim solution.

Contingency: Implement the CTA as a configurable action: if the enrollment route exists in the router, push it; otherwise, launch the configured org-specific enrollment URL via url_launcher, ensuring HLF users can always take action on expired certifications.