high priority medium complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

DuplicateComparisonPanel accepts `incoming` and `candidate` of type CandidateDuplicate (or equivalent model) as required parameters
Side-by-side layout is used on screens ≥600dp wide; stacked (vertical) layout is automatically used on narrower screens (responsive breakpoint)
Fields displayed for each activity: peer mentor name, activity type, date (formatted as locale-aware date string), duration in minutes, chapter name, and notes (truncated at 3 lines with 'Show more')
Fields that differ between incoming and candidate are visually highlighted using the project's design token accent color — identical fields are rendered without highlight
Visual diff highlight does NOT rely solely on color (satisfies WCAG 2.2 Success Criterion 1.4.1): differing fields also show a distinct icon or border pattern
Each field has a Semantics widget with `label` combining the column header and field name (e.g., 'Incoming activity: date, 15 March 2026') for screen reader navigation
The widget passes flutter_test accessibility checks: no missing semantic labels on interactive or informational elements
Font size, spacing, and contrast ratios use design tokens exclusively — no hardcoded values
Widget is exported from the component library and documented with a DartDoc comment explaining its parameters
Golden test snapshot is committed for both side-by-side and stacked layouts at standard font size

Technical Requirements

frameworks
Flutter
flutter_test
data models
CandidateDuplicate
performance requirements
Widget builds in < 16ms (single frame) with no jank on mid-range devices — verified with Flutter DevTools frame timeline
No async operations inside build() — all data is passed in as parameters
security requirements
Notes field content must be HTML-escaped before rendering to prevent injection if notes are sourced from user input stored in database
Do not render peer mentor personal information (phone, email, address) in this comparison panel — restrict to the fields listed in acceptance criteria
ui components
DuplicateComparisonPanel (new)
ComparisonFieldRow (internal sub-widget: label + incoming value + candidate value + diff highlight)
Design token: AppColors, AppTypography, AppSpacing, AppBorderRadius
AppButton (existing reusable widget for action buttons below the panel)

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Build ComparisonFieldRow as a private internal widget within the same file to keep the component self-contained. Use Flutter's `LayoutBuilder` or `MediaQuery` to switch between side-by-side and stacked layouts at the 600dp breakpoint — avoid platform-specific breakpoint logic. For diff highlighting, compute the diff in a pure function outside the widget tree (e.g., `bool _fieldsDiffer(String? a, String?

b)`) to keep build() declarative. Use design tokens for the highlight color (e.g., `AppColors.warningSubtle`) and the indicator icon (`Icons.warning_amber_rounded` or equivalent from the design system). Pitfall: screen readers read each Semantics node in DOM order — ensure the Semantics tree interleaves fields in a logical reading order (incoming name → candidate name → incoming date → candidate date) rather than reading all incoming fields then all candidate fields, which breaks comprehension for VoiceOver users. This is NHF-critical: their user base includes people with cognitive disabilities and the comparison must be scannable, not confusing.

Testing Requirements

Widget tests using flutter_test: (1) renders both 'Incoming' and 'Candidate' column headers; (2) differing date field is highlighted, matching activity_type field is not highlighted; (3) responsive layout renders stacked at 400dp width, side-by-side at 700dp width — use `tester.binding.setSurfaceSize`; (4) Semantics tree contains labels for all displayed fields — use `tester.getSemantics(find.byType(DuplicateComparisonPanel))` and assert labels; (5) notes field truncates at 3 lines and 'Show more' expands it; (6) null notes field renders gracefully (empty state, no overflow); (7) golden tests for both layouts stored in `test/golden/`. Accessibility audit: run `tester.pump()` followed by `SemanticsHandle` checks. No unit tests needed (pure UI widget). Manual VoiceOver test on a physical iOS device should be performed as part of PR review.

Component
Duplicate Detection BLoC
infrastructure medium
Epic Risks (2)
medium impact high prob technical

For bulk registration with many participants, running duplicate checks sequentially before surfacing the consolidated summary could introduce a multi-second delay as each peer mentor is checked individually against the RPC. This degrades the bulk submission UX significantly.

Mitigation & Contingency

Mitigation: Issue all duplicate check RPC calls concurrently using Dart's `Future.wait` or a bounded parallel executor (max 5 concurrent calls to avoid Supabase rate limits). The BLoC collects all results and emits a single BulkDuplicateSummary state with the consolidated list.

Contingency: If concurrent RPC calls hit Supabase connection limits or rate limits, implement a batched sequential approach with a progress indicator showing 'Checking participant N of M' so the coordinator understands the delay is expected and bounded.

high impact medium prob integration

In proxy registration, the peer mentor's ID must be used as the duplicate check parameter, not the coordinator's ID. If the proxy context is not correctly threaded through the BLoC and service layer, duplicate checks will silently run against the wrong person, missing actual duplicates.

Mitigation & Contingency

Mitigation: Define a `SubmissionContext` model that carries the effective `peer_mentor_id` (distinct from `submitter_id`) and pass it explicitly through the BLoC event payload. The DuplicateDetectionService always reads peer_mentor_id from SubmissionContext, never from the authenticated user session.

Contingency: If SubmissionContext threading proves difficult to retrofit into the existing proxy registration BLoC, add an assertion in DuplicateDetectionService that throws a descriptive error when peer_mentor_id is null or matches the coordinator's own ID in a proxy context, making the bug immediately visible in testing.