high priority medium complexity frontend pending frontend specialist Tier 4

Acceptance Criteria

Outlier rows have a visible background tint (design token color, e.g., warning surface or error surface) distinguishing them from normal rows
Each outlier row includes an OutlierBadge widget displaying the outlier_reason label (e.g., 'At Risk', 'Overdue', 'No Activity')
Badge text and background color combination passes WCAG 2.2 AA contrast ratio (minimum 4.5:1 for normal text)
Row background tint and any text on that tint passes WCAG 2.2 AA (minimum 3:1 for large text / UI components)
A 'Show only outliers' filter toggle (Switch or FilterChip) is present above the list and dispatches FilterByOutlier(true/false) to the BLoC
When the filter is active, only outlier rows are rendered; the toggle label changes to 'Showing outliers only (N)' where N is the count
When the filter is active and there are no outliers, an informative empty state message is shown (e.g., 'No mentors at risk this period')
All OutlierBadge widgets include a Semantics widget with a label attribute (e.g., Semantics(label: 'Outlier: At Risk', child: ...))
Row background tints use Semantics(excludeSemantics: false) so screen readers do not announce decorative color
Accessible tap targets for the filter toggle are at least 44×44 logical pixels
No additional network calls are made during outlier highlighting — all data comes from existing BLoC state

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
Dart
data models
MentorSummaryModel (isOutlier, outlier_reason fields)
OutlierSeverity enum
performance requirements
Filter toggle state change must produce a re-render in under 16ms (single frame) — no async operations
Filtering is performed in the BLoC (already filtered list exposed) — UI must not re-filter in build()
security requirements
Outlier reason labels must not expose raw internal scoring data or thresholds to the UI layer
ui components
OutlierBadge — chip widget with severity-mapped color tokens and Semantics label
OutlierRowDecoration — BoxDecoration helper using design token surface colors
OutlierFilterToggle — FilterChip or Switch with active count label

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Define an OutlierSeverity enum (none, warning, error) derived from outlier_reason in a presentation mapper function, not in the BLoC or data layer. Map: 'At Risk' → warning, 'Overdue' → error, others → warning by default. OutlierBadge should use a Container with a design token background color and a Text with contrasting foreground token. Wrap the badge in Semantics(label: 'Outlier status: ${reason}', child: badge) to provide meaningful screen reader output — the visual label alone may be insufficient for low-vision users.

For the row tint, use a ColoredBox or DecoratedBox wrapping the MentorSummaryRow — avoid setting color on the ListTile directly as it has poor semantics. Keep the filter toggle state in the BLoC (FilterByOutlier event), not in local widget state, so that navigating away and back preserves the filter selection.

Testing Requirements

Widget tests: verify badge is rendered only when isOutlier is true; verify badge is absent for non-outlier rows; verify filter toggle dispatches FilterByOutlier event; verify empty state message appears when filter is active with zero outliers; verify Semantics label on badge is present and non-empty. Accessibility tests: use flutter_test's SemanticsHandle to verify semantic tree includes outlier badge labels. Manual QA checklist: run WCAG color contrast checker on badge color pairs (text + background) for both warning and error severity in light and dark modes. Golden tests: captured renders for rows with and without outlier highlighting, both themes.

Component
Coordinator Team Summary View
ui medium
Epic Risks (3)
medium impact medium prob technical

If the cached summary is from a prior period (e.g., previous quarter's card is still cached), the digest card could appear on the home screen with outdated numbers after a new period begins, confusing users who have already seen that data.

Mitigation & Contingency

Mitigation: In the SummaryCacheRepository, store the period_start and period_end alongside cached data. In the PeriodicSummaryCard BLoC, compare the cached record's period against the current date using PeriodCalculatorService. Only render the card if the cached summary belongs to the active period.

Contingency: If stale cards appear in production, push a hotfix that adds the period validity check to the BLoC and clears all cached summaries older than the current period boundary via a forced cache flush on next app launch.

medium impact medium prob technical

Using colour alone to distinguish underactive from overloaded mentors in the coordinator view would fail WCAG 1.4.1 (use of colour). This is especially critical for organisations serving users with colour vision deficiency.

Mitigation & Contingency

Mitigation: Always pair colour indicators with a text label or icon (e.g., a downward arrow + 'Underactive' text alongside the amber colour). Use the contrast-safe-color-palette design tokens throughout. Run the contrast-ratio-validator on all new colour combinations in CI.

Contingency: If an accessibility audit flags colour-only indicators post-launch, introduce icon-based indicators as a patch release and update the design token definitions to enforce the paired-indicator pattern going forward.

low impact medium prob integration

Inserting a new card into the role-based home screen requires changes to shared home screen widget composition, which may conflict with parallel feature branches also modifying the home screen layout, causing merge conflicts and integration delays.

Mitigation & Contingency

Mitigation: Implement the PeriodicSummaryCard as a fully self-contained widget that the home screen conditionally renders based on a BLoC state flag. Minimise changes to the home screen itself to a single conditional insertion point, reducing the surface area for merge conflicts.

Contingency: If integration conflicts block the PR, isolate the card behind the organisation-scoped feature flag so it can be toggled independently and merged without affecting other home screen changes.