critical priority medium complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

WcagSemanticsLabelResolver consumes OrganizationLabelsNotifier via `ref.watch` and resolves labelKey and hintKey through LabelKeyResolver on every build
The resolved label string is passed to `Semantics(label: resolvedLabel, ...)` — never the raw key
When hintKey is provided and resolves successfully, the hint is passed to `Semantics(hint: resolvedHint, ...)`
When hintKey resolves to null (missing key or hintKey is null), the `hint` property is omitted from the Semantics node — not set to empty string, which would produce an audible empty pause for screen reader users
The `excludeSemantics: true` flag is NOT set on the wrapper Semantics node — child semantics are preserved and merged correctly
The Semantics node does not duplicate labels already present in child nodes — the resolver provides the outer accessibility label only
When the active organization changes, the Semantics node updates its label/hint in the next frame automatically (same reactivity guarantee as TerminologyAwareTextWidget)
A fallback for missing labelKey follows the same logic as TerminologyAwareTextWidget: explicit fallback → humanized key (configurable via a `fallbackLabel` parameter)
The widget does not introduce additional render objects beyond what Semantics itself adds
The implementation passes Flutter's SemanticsUpdateCallback correctly — no orphaned Semantics nodes remain after widget disposal

Technical Requirements

frameworks
Flutter
Riverpod
data models
OrganizationLabelMap
LabelKey
SemanticsNode
performance requirements
Semantics resolution must be synchronous within build — no async gaps that could cause the accessibility tree to be momentarily unlabeled
The added Semantics widget must not trigger a full subtree rebuild — it should be placed as a thin wrapper that Flutter's render layer handles efficiently
security requirements
Semantics labels are announced to assistive technologies — ensure no personally identifiable information (names, addresses) is accidentally included in label keys or fallback strings
Organization-specific label values must not be logged at info level or above in production
ui components
WcagSemanticsLabelResolver (ConsumerWidget, fully implemented)
Flutter Semantics widget
OrganizationLabelsNotifier (Riverpod, via ref.watch)
LabelKeyResolver (service, consumed via provider)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

The critical design decision is whether to use `Semantics(label: x, child: child)` directly or `MergeSemantics`. Use direct `Semantics` wrapping with `label` and optional `hint`, and do NOT set `excludeSemantics: true` — this would silence all child semantics (e.g., button click actions) which is incorrect for WCAG. The `label` property on a Semantics node replaces the node's own label but child nodes with their own labels/actions remain independent and traversable. For the hint: use `hint: resolvedHint` when non-null, and omit the parameter entirely (or use null) when the hint is absent — Flutter's Semantics widget will not produce an empty hint node if the value is null.

Pattern for omitting hint: `Semantics(label: resolvedLabel, hint: resolvedHint, child: child)` where `resolvedHint` is `String?` — Flutter handles null correctly. Ensure the widget disposes of nothing explicitly (Riverpod ConsumerWidget handles cleanup).

Testing Requirements

Widget tests using flutter_test semantics APIs: (1) Pump WcagSemanticsLabelResolver with a known labelKey in a ProviderScope with a mocked label map — use `tester.getSemantics(find.byType(WcagSemanticsLabelResolver))` to assert the SemanticsNode has the expected label. (2) Test with a hintKey that resolves — assert hint is present. (3) Test with hintKey that does not resolve — assert the hint property is null/absent on the SemanticsNode, not empty string. (4) Test org switch — assert Semantics label updates.

(5) Test that child Text semantics are still reachable (not excluded). Use `SemanticsController` or `tester.semantics` APIs from flutter_test. Full WCAG compliance testing is handled in task-007.

Component
WCAG Semantics Label Resolver
infrastructure low
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.