high priority medium complexity frontend pending frontend specialist Tier 5

Acceptance Criteria

Every tree node widget is wrapped in a Semantics widget with: label='[node name], [node type], level [depth], [expanded|collapsed]' (or 'leaf' for childless nodes), hint='Double tap to select', and a button role
Expand/collapse actions announce the new state to screen readers via a SemanticsService.announce() call immediately after the state change ('Oslo Chapter expanded' / 'Oslo Chapter collapsed')
The search field (task-014) has semanticsLabel='Search organisations' and liveRegion=true so result count changes are announced automatically
Tab / arrow-key focus order follows document order (top-to-bottom); FocusNode traversal does not skip any visible node or interactive element
VoiceOver (iOS) and TalkBack (Android) can navigate the full tree, select nodes, and trigger expand/collapse without a pointing device
Flutter accessibility audit (SemanticsController.checkSemantics) passes with zero errors on the full navigator including search and breadcrumb
Minimum contrast ratios enforced: 4.5:1 for node name text, 3:1 for type badge borders and expand chevron icons against their background
Touch targets for all interactive elements are ≥ 48 × 48 dp, verified via debugCheckHasBoundedHeight / layout tests
Keyboard-only users can navigate between breadcrumb segments using Tab and activate them with Enter/Space

Technical Requirements

frameworks
Flutter
flutter_test (SemanticsController)
apis
SemanticsService.announce()
Flutter Semantics widget API
FocusNode / FocusScope
data models
OrgNode (depth, isExpanded, isLeaf derived properties for semantic labels)
performance requirements
Semantics tree construction must not add more than 2 ms per frame on a tree of 100 visible nodes
SemanticsService.announce() calls must be throttled if rapid expand/collapse events occur (debounce 150 ms)
security requirements
Semantic labels must not expose internal node IDs or server-side UUIDs — only human-readable names and types
ui components
AccessibleTreeNode (OrgTreeNode extended with Semantics wrapper and FocusNode)
LiveRegionWrapper (thin Semantics widget with liveRegion=true for search result count)
AccessibleBreadcrumbSegment (Semantics-wrapped InkWell with explicit role and label)

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Flutter does not have a native ARIA treeitem role, so approximate it with Semantics(role: SemanticsRole.button, label: ...). Use the 'expanded' and 'collapsed' semantic properties (Semantics.expanded) where available in the Flutter version in use — fall back to embedding the state in the label string if the property is not supported. For keyboard navigation, wrap the entire tree in a FocusScope and assign FocusNodes to each visible node in list order; use FocusScope.of(context).nextFocus() on Tab keypress captured via a RawKeyboardListener. Be careful not to assign FocusNodes to hidden (collapsed) nodes — rebuild the FocusNode list whenever expansion state changes.

Coordinate with the design system team to confirm all design-token colors pass the 4.5:1 contrast check before implementation; flag any tokens that fail and propose alternatives.

Testing Requirements

Semantics tests (flutter_test SemanticsController): (1) assert every visible node has the correct label pattern including depth and expansion state; (2) assert expand action triggers SemanticsService.announce with correct message; (3) assert search result count is announced via live region; (4) assert breadcrumb segments have button role and descriptive labels. Run Flutter's built-in accessibility audit via FlutterTest.ensureSemantics() and SemanticsController.checkSemantics(). Manual validation: test VoiceOver on iOS Simulator and TalkBack on Android Emulator, navigating through a 3-level hierarchy using only the keyboard/accessibility gestures and confirming correct announcements at each step.

Epic Risks (4)
medium impact high prob technical

OrgHierarchyNavigator rendering NHF's full 1,400-chapter tree in a single widget may cause Flutter frame-rate drops below 60 fps on mid-range devices, making the navigator unusable for NHF national admins.

Mitigation & Contingency

Mitigation: Implement lazy expansion: only load immediate children on node expand rather than the full tree upfront. Use virtual scrolling for long sibling lists. Test with a synthetic 1,400-node dataset on a low-end Android device during development.

Contingency: If lazy expansion is insufficient, replace the tree widget with a paginated drill-down navigator (select level → select child) that avoids rendering more than 50 nodes at a time.

medium impact medium prob dependency

Bufdir may update their required export column structure or file format during or after development. If the AdminExportService hardcodes the current Bufdir schema, any format change requires a code release rather than a config update.

Mitigation & Contingency

Mitigation: Drive the Bufdir column mapping from a configuration repository rather than hardcoded constants. Abstract column definitions into a named schema config so that format changes require only a config update and re-deployment without service logic changes.

Contingency: If Bufdir format changes post-launch, release a config update within one sprint. If the change is structural (new required sections), scope a targeted service update and communicate timeline to partner organisations.

high impact medium prob integration

Role transition side-effects in UserManagementService (e.g., certification expiry removing mentor from chapter listing, pause triggering coordinator notification) may interact with external services like HLF's website sync. Incomplete side-effect handling could leave the system in an inconsistent state.

Mitigation & Contingency

Mitigation: Model side-effects as explicit domain events published after the primary state change is persisted. Implement event handlers as idempotent operations so re-processing is safe. Write integration tests that assert all side-effects fire correctly for each role transition type.

Contingency: If a side-effect fails after the primary change is persisted, log the failure with full context and trigger a manual reconciliation alert to the on-call team. Provide an admin-accessible re-trigger action for failed side-effects.

medium impact medium prob scope

If AdminStatisticsService cache TTL is set too long, org_admin may see significantly stale KPI values (e.g., a mentor newly paused an hour ago still appears as active), undermining trust in the dashboard.

Mitigation & Contingency

Mitigation: Default cache TTL to 5 minutes with a manual refresh action on the dashboard. Implement cache invalidation triggered by UserManagementService write operations that affect counted entities.

Contingency: If staleness causes org admin complaints post-launch, reduce TTL to 60 seconds and introduce a real-time Supabase subscription for high-impact counters (paused mentors, expiring certifications).