high priority medium complexity frontend pending frontend specialist Tier 7

Acceptance Criteria

Each list item in DeduplicationQueueScreen has a single merged semanticsLabel that includes: both activity dates, both participant names (or 'unknown participant' if null), pair status, and a hint such as 'Double tap to review'
Screen reader does not read individual child widgets of a list card separately (use MergeSemantics or a single Semantics label)
Filter controls (date range, status chips, activity type chips, sort dropdown) are fully navigable by screen reader swipe and have descriptive labels indicating both the control type and its current value (e.g., 'Status filter, currently Unresolved')
When a filter is changed and the list updates, a live region or polite announcement informs the screen reader user of the result count (e.g., 'Showing 3 unresolved pairs')
Resolution action buttons in the detail view have semanticsLabels announcing the consequence (e.g., 'Merge — combine both activities into one', 'Dismiss — mark as not a duplicate')
The navigation tab badge count is wrapped in a Semantics widget with liveRegion: true so that badge value changes are announced without user navigation
Badge tab label reads as a complete phrase: e.g., 'Deduplication queue, 5 unresolved pairs' not just '5'
All interactive elements in both screens have minimum 44×44 dp touch targets
All text meets WCAG 1.4.3 contrast requirements (≥ 4.5:1 normal, ≥ 3:1 large) verified against design tokens
Diff-highlighted fields in the comparison panel have a non-color indicator (e.g., bold text or an icon) in addition to the color highlight, satisfying WCAG 1.4.1
Flutter widget tests assert all semantic labels using tester.getSemantics() and matchesSemantics
Manual test checklist completed on physical iOS (VoiceOver) and Android (TalkBack) devices

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
DeduplicationQueueService (existing)
DuplicateResolutionHandler (existing)
data models
DuplicatePair
Activity
DeduplicationQueueFilter
performance requirements
Semantics tree augmentation (MergeSemantics, Semantics wrappers) must not cause frame drops; verify with Flutter performance overlay that frame budget is not exceeded during list scroll
security requirements
Participant names in semanticsLabels must only be included if the current user's role is authorized to see them; do not leak names to screen-reader output for roles that lack contact-data access
ui components
DeduplicationQueueListItem (remediated with MergeSemantics)
FilterControlsPanel (remediated with value-announcing Semantics)
ResultCountLiveRegion widget
ResolutionActionBar (remediated semanticsLabels, from task-010)
BadgeOverlay (remediated with liveRegion, from task-009)
DuplicateComparisonPanel (non-color diff indicator added)

Execution Context

Execution Tier
Tier 7

Tier 7 - 84 tasks

Can start after Tier 6 completes

Implementation Notes

For list item semantics, wrap each DuplicateQueueListItemCard in a Semantics widget with a hand-crafted label string built from the pair data: avoid MergeSemantics if child widgets contain redundant or confusing text that should be excluded — explicit label is more reliable. Use a value callback pattern: semanticsLabel: _buildPairLabel(pair) where _buildPairLabel is a pure function testable in isolation. For filter controls, add a Semantics value: parameter reflecting the current selection alongside the label — e.g., Semantics(label: 'Status filter', value: 'Unresolved', child: ...). For the result count live region, emit count updates through a Riverpod/BLoC state field and render it in a visually hidden (zero-size) Semantics widget with liveRegion: true positioned at the top of the list.

For non-color diff indicators in DuplicateComparisonPanel, add a small Icon(Icons.edit, size: 12) or FontWeight.bold to differing fields — this satisfies WCAG 1.4.1 Use of Color without being visually intrusive. Coordinate with the task-009 implementer to ensure the badge liveRegion is already in place before this task marks the badge audit as complete.

Testing Requirements

Flutter widget tests must: assert each list item's merged semantics node contains date strings and participant name strings; assert filter chip semantics includes current selected value; assert resolution buttons contain consequence phrases in their labels; assert badge semantics node has liveRegion flag and label includes count. Use tester.pumpWidget, tester.getSemantics, and find.bySemanticsLabel for coverage. Verify MergeSemantics suppresses child nodes using SemanticsController.nodesWith. For non-color diff: golden test or widget test confirming a non-color indicator (icon or font weight) is present on differing fields.

Manual checklist must include: linear navigation order test (swipe through entire list without losing context); filter result count announcement test; badge change announcement test; full resolution flow narration test from list item to action confirmation.

Component
Deduplication Queue Screen
ui medium
Epic Risks (3)
high impact medium prob technical

The merge resolution path requires identifying which fields from the draft differ from the existing record, applying those differences to the existing record, and cancelling the draft — all as an atomic operation. Partial failures (e.g., update succeeds but draft cancellation fails) could leave the system in an inconsistent state.

Mitigation & Contingency

Mitigation: Implement the merge path as a Supabase RPC transaction that updates the existing record and soft-deletes the draft in a single atomic call. The DuplicateResolutionHandler should never attempt field-level merge at the application layer.

Contingency: If the atomic RPC approach proves too complex for the initial release, simplify the merge path to: mark existing record as the canonical record and cancel the new submission without field merging, displaying a message to the user to manually verify the existing record's fields. Log a follow-up ticket for full field-merge in a later sprint.

medium impact medium prob integration

The DuplicateWarningBottomSheet must intercept the activity wizard's save action without disrupting the wizard's existing navigation stack. If the bottom sheet is implemented as a separate route rather than an overlay, back navigation could break the wizard's step state.

Mitigation & Contingency

Mitigation: Use a `showModalBottomSheet` overlay pattern so the bottom sheet sits above the wizard route without pushing a new route onto the Navigator stack. The wizard's CuBit/BLoC retains all draft state while the sheet is visible. Test this integration with the existing activity-registration-cubit before merging.

Contingency: If the overlay approach causes Z-order or focus issues with the wizard's keyboard-aware layout, route the duplicate check result back to the wizard as a state event (DuplicateDetected), and let the wizard render a local inline warning banner instead of a bottom sheet.

medium impact medium prob technical

The bottom sheet and comparison panel involve complex layouts with multiple interactive elements. Screen reader users (particularly relevant for Blindeforbundet) may struggle with the side-by-side comparison layout if semantics are not carefully ordered.

Mitigation & Contingency

Mitigation: Design the comparison panel with a single-column semantic order (read record A fully, then record B fully) regardless of visual layout. Use Flutter's `Semantics` widget with `sortKey` to enforce correct traversal order. Test with TalkBack and VoiceOver against the WCAG 2.2 AA reading order criteria.

Contingency: If side-by-side layout cannot achieve acceptable screen reader ordering, switch to a stacked tab layout (Tab A / Tab B) for the comparison panel that is semantically simple even if less visually immediate for sighted users.