Implement fallback text for missing label keys
epic-dynamic-terminology-and-labels-ui-and-accessibility-task-003 — Add fallback rendering logic to TerminologyAwareTextWidget. When LabelKeyResolver returns null for a key (missing from the active org map), the widget must display the provided fallback string or, if none given, render the raw labelKey formatted as human-readable text. Log missing keys in debug mode without crashing production builds.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Implement a private static helper `_humanize(String key)` that replaces `_` and `-` with spaces, then applies `toUpperCase()` on the first letter of each word (title case). Example: `'peer_mentor_label'.replaceAll(RegExp(r'[_-]'), ' ')` followed by a simple capitalize-words pass. The resolution priority chain is: (1) resolved from map, (2) explicit fallback, (3) humanized key. Guard the debug log with `assert(() { debugPrint('[Terminology] Missing key: $labelKey (org: $orgId)'); return true; }())` — the assert body is stripped in release builds.
Avoid using `if (kDebugMode)` for the log if it introduces a non-trivial code path; the assert pattern is preferred. Do not add a visible debug decorator unless the team explicitly requests it — the log is sufficient for development.
Testing Requirements
Unit tests using flutter_test: (1) Provide a label map where the tested key is absent — assert the rendered text equals the provided `fallback` string. (2) Provide a label map where the key is absent AND no fallback is given — assert the rendered text equals the humanized key ('peer_mentor_label' → 'Peer Mentor Label'). (3) Verify that in debug mode a log warning is captured (use a custom log listener or zone override). (4) Verify no exception is thrown for any null/missing combination.
(5) Test with fallback set to empty string '' — assert empty string is rendered, not the humanized key. Aim for 100% coverage of the fallback resolution branches.
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.
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.
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.