high priority medium complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

All label keys registered in LabelKeyRegistry for the active organization are rendered in a scrollable list
Each list row displays: label key identifier, resolved organization-specific value, and default fallback value as three distinct visual elements
Rows where the resolved value equals the fallback value (i.e., using default) are visually distinguishable from rows with a custom override
A search/filter TextField at the top of the list filters rows in real-time as the user types — matching on label key AND resolved value
Clearing the search field restores the full unfiltered list
List is virtualized (ListView.builder) — renders correctly with 200+ label keys without frame drops
Switching active organization clears the search field and reloads the list for the new org
No results state renders a 'No matching labels found' message when the search filter returns zero rows
Accessibility: each row is a semantically labeled ListTile with Semantics node readable by VoiceOver/TalkBack

Technical Requirements

frameworks
Flutter
Riverpod
apis
LabelKeyRegistry (enumerate all keys)
TerminologyRepository (resolve value per org)
OrganizationLabelsNotifier (active org)
data models
LabelKeyEntry
OrganizationLabelMap
ResolvedLabelRow (view model)
performance requirements
List must maintain 60fps scroll with up to 500 label key rows
Search filter must debounce input by 150ms to avoid per-keystroke rebuilds
Resolved values must be pre-computed into a view model list on provider load, not computed per row during build
security requirements
Label values must be displayed as plain text — no HTML/markdown rendering that could introduce XSS-equivalent injection
Search input must be sanitized; do not pass raw user input to Supabase queries — filtering is purely client-side on the pre-loaded map
ui components
ListView.builder (virtualized list)
LabelKeyRow widget (key + resolved value + fallback, three-column layout)
Search TextField with clear button
EmptySearchResultWidget
Semantics wrapper per row

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Extract a LabelKeyListViewModel (or equivalent Riverpod derived provider) that combines LabelKeyRegistry enumeration with TerminologyRepository resolution into a flat List. This keeps build() pure and makes the filter logic unit-testable. The search filter should be a local StateProvider scoped to the screen — do not pollute the global provider graph. Use ListView.builder, not ListView(children: []) — the label key count can be large.

The three-column row layout should use a Row with Expanded widgets; avoid Table widget for accessibility reasons. The default fallback column should use a de-emphasized text style (design token: text-tertiary equivalent) to visually separate it from the resolved value.

Testing Requirements

Write flutter_test widget tests for: (1) list renders correct number of rows from mock LabelKeyRegistry, (2) each row displays key/resolved/fallback correctly, (3) search filter reduces visible rows matching the query, (4) clearing search restores full list, (5) no-results state renders when filter matches nothing. Write a unit test for the view model projection function that converts raw LabelKeyRegistry + OrganizationLabelMap into ResolvedLabelRow list. Test with both empty map and fully populated map. Target 90%+ line coverage on filter logic.

Epic Risks (3)
high impact medium prob integration

WcagSemanticsLabelResolver's Semantics wrappers may conflict with Semantics nodes already defined by existing accessible widgets (e.g., accessible-bottom-navigation, activity-wizard-semantics), causing duplicate or contradictory screen reader announcements that fail WCAG 2.2 AA criteria.

Mitigation & Contingency

Mitigation: Audit all existing Semantics-annotated widgets in the accessibility feature before implementing WcagSemanticsLabelResolver. Define a clear hierarchy rule: WcagSemanticsLabelResolver always merges with, never replaces, existing Semantics nodes. Use Flutter's debugSemantics output in CI to detect conflicts automatically.

Contingency: If conflicts are discovered in testing, introduce a resolverMode parameter to WcagSemanticsLabelResolver allowing it to operate in 'override' or 'merge' mode per call site; coordinate with the Screen Reader Support feature team to align Semantics strategies.

medium impact medium prob technical

If TerminologyAwareTextWidget subscribes to the full terminology map provider rather than the per-key labelProvider family, a single label update will trigger a full widget-tree rebuild across all screens simultaneously, causing jank on devices used by older peer mentors.

Mitigation & Contingency

Mitigation: Implement TerminologyAwareTextWidget using ref.watch(labelProvider(key)) on the per-key family provider from TerminologyRiverpodProviders so that only widgets bound to the changed key rebuild. Verify with Flutter DevTools 'rebuild tracking' in widget tests.

Contingency: If full-map subscriptions slip through code review, add a Riverpod lint rule that flags direct organizationLabelsNotifierProvider subscriptions inside widget build methods and enforces the per-key family pattern.

medium impact low prob security

The TerminologyAdminPreviewScreen requires coordinator-level access, but if role checks rely solely on client-side guard logic without matching Supabase RLS policies, a peer mentor could potentially access the admin preview by manipulating navigation state.

Mitigation & Contingency

Mitigation: Protect the admin preview route with a server-validated role guard that re-fetches the user's role from Supabase on screen initialization, not just from local state. Add a Supabase RLS policy that restricts label map read access for the admin preview endpoint to coordinator and admin roles only.

Contingency: If unauthorized access is discovered in testing, immediately add a middleware role assertion that redirects non-coordinators to the no-access screen and logs the unauthorized navigation attempt for audit.