high priority medium complexity frontend pending frontend specialist Tier 6

Acceptance Criteria

Tapping the CTA button on ScenarioPromptNotificationCard invokes ScenarioDeepLinkHandler.resolve(scenarioId, activityId) to obtain the target GoRouter route
GoRouter.go() or GoRouter.push() is called with the resolved route path and the required query parameters: peerId, scenarioId, activityId
Activity wizard screen opens pre-populated with the peer mentor selection and scenario context fields filled from the notification parameters
If ScenarioDeepLinkHandler.resolve() returns null (unknown scenario or route), a non-fatal error is logged and a fallback navigation to the home activity creation screen is triggered
CTA tap is debounced — rapid double-taps do not trigger duplicate navigations
Navigation is tested with GoRouter's test utilities — not relying on widget tree navigation side effects
After CTA tap, the notification card is marked as 'read' by calling the notification read-state update method (Riverpod provider or repository)
Accessibility: CTA button tap is triggerable via keyboard/switch access in addition to touch
Widget test: mock ScenarioDeepLinkHandler, assert GoRouter.go called with correct path on CTA tap
Widget test: mock returns null route — assert fallback navigation triggered and no exception thrown

Technical Requirements

frameworks
Flutter
GoRouter
Riverpod
data models
activity
assignment
performance requirements
Route resolution via ScenarioDeepLinkHandler must be synchronous — no async I/O in the tap handler critical path
Navigation transition must begin within one frame of CTA tap (no loading spinners for route resolution)
security requirements
Deep link parameters (peerId, scenarioId, activityId) must be validated as non-empty UUIDs before constructing the route — prevent malformed navigation
Do not encode PII (contact names, activity notes) in the route path or query parameters — use IDs only
GoRouter route must be defined with authentication guard — unauthenticated users redirected to login before reaching the wizard
ui components
AppButton (CTA — already implemented in task-006)
Activity wizard screen (pre-existing, receives query params)
GoRouter route definition for scenario deep link target

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Inject GoRouter via context.go() using the standard go_router BuildContext extension — do not store a GoRouter reference in the widget. Pass onTap callback from parent to keep the card widget testable without a full routing environment: ScenarioPromptNotificationCard(onCtaTap: () => handler.navigate(context, notification)). Implement debounce with a bool _navigating flag set on first tap and reset after navigation or 1 second timeout. ScenarioDeepLinkHandler.resolve(scenarioId) should return a named GoRouter route string like '/activity/new?peerId=...&scenarioId=...&activityId=...'.

For the activity wizard pre-population, the wizard should read these params from GoRouterState.uri.queryParameters in its build method and initialize the relevant BLoC/Riverpod state. Mark notification as read by calling ref.read(notificationRepositoryProvider).markRead(notification.id) in the tap handler — not in didChangeDependencies.

Testing Requirements

Widget tests with flutter_test and GoRouter test helpers (GoRouter.setLogging, MockGoRouter or RoutingConfig). Test cases: (1) tap CTA — assert GoRouter.go called with path containing scenarioId and activityId as query params; (2) ScenarioDeepLinkHandler returns null — assert fallback route called (e.g. '/activity/new'), no exception; (3) double-tap — assert GoRouter.go called exactly once (debounce); (4) after tap, assert notification read-state provider updated to isRead=true; (5) verify peerId and scenarioId in route params are valid UUID format via regex assertion in test. Integration test: use Flutter integration_test package to tap card in full app, assert activity wizard screen title is visible and peer mentor field is pre-filled.

Component
Scenario Prompt Notification Card
ui medium
Epic Risks (2)
high impact medium prob technical

If the scheduler runs concurrently (e.g., two overlapping cron invocations due to edge function retry), duplicate prompts could be dispatched before the first run's history records are committed, breaking the deduplication guarantee.

Mitigation & Contingency

Mitigation: Use a Postgres advisory lock or unique constraint on (user_id, scenario_id, activity_ref) in the prompt history table to make concurrent writes idempotent; design the scheduler to check history inside a transaction.

Contingency: If concurrency issues persist in production, add a distributed lock via Supabase Edge Function concurrency limit (max_instances=1) for the evaluation function as a hard guard.

medium impact medium prob scope

Coordinators may find scenario configuration unclear if trigger conditions are expressed as raw JSON or technical terminology, leading to misconfiguration and irrelevant prompts being sent to peer mentors.

Mitigation & Contingency

Mitigation: Design the ScenarioConfigurationScreen to display human-readable descriptions of each template's trigger condition (e.g., 'Send 3 days after first contact if wellbeing concern was flagged') rather than raw rule properties; validate with an HLF coordinator in a design review before implementation.

Contingency: If coordinators still misconfigure rules after launch, add a preview mode that shows a simulated prompt based on a test activity before the rule is enabled.