high priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

ActiveChapterCubit exposes a stream of ActiveChapterState (loading, loaded with chapterId, error)
selectChapter(chapterId) updates state to the new chapter and persists the chapterId to secure storage
On app cold start or resume, cubit reads persisted chapterId from secure storage and restores state
If no chapter is persisted, state defaults to the user's primary chapter from the repository
State broadcasts are received by all widgets registered as BlocListeners without requiring manual refresh
clearActiveChapter() removes the persisted value and resets state to unselected
On logout, cubit clears persisted chapter and emits an empty/unauthenticated state
Cubit is accessible app-wide via a top-level BlocProvider (not scoped to a single route)
Multiple simultaneous listeners receive the same state update without duplication

Technical Requirements

frameworks
Flutter
BLoC
apis
flutter_secure_storage (for persistence)
data models
ActiveChapterState
OrganizationUnit
AppUser
performance requirements
State restoration from secure storage completes within 300ms on app resume
selectChapter emits new state within 100ms of being called
security requirements
Chapter ID persisted using flutter_secure_storage (not shared_preferences) to prevent extraction on rooted/jailbroken devices
Secure storage cleared on logout to prevent session leakage
Cubit must validate that the restored chapterId belongs to the current authenticated user before emitting it

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Implement as a Cubit rather than a full BLoC (events not needed for this simple state machine). Inject `FlutterSecureStorage` and `OrganizationUnitRepository` via constructor for testability. Use a constant key (e.g., `'active_chapter_id'`) for secure storage. In the cubit's constructor, call `_restoreActiveChapter()` as an async method that reads storage and then fetches the unit details from the repository to populate the full `OrganizationUnit` object in state (not just the ID).

Listen to Supabase auth state changes via a stream subscription in the cubit and call `clearActiveChapter()` on sign-out — cancel the subscription in `close()`. Register the cubit at the root of the widget tree in `main.dart` using `BlocProvider` so it is available app-wide.

Testing Requirements

Unit tests using flutter_test and bloc_test package. Test: initial state is loading, restoration from persisted storage emits loaded state, selectChapter emits new loaded state and calls secure storage write, clearActiveChapter emits empty state and calls storage delete, logout event clears state and storage, error state emitted when storage read fails. Use MockFlutterSecureStorage and MockOrganizationUnitRepository. Use `blocTest` for all state transition assertions.

Target 100% statement coverage on the cubit.

Component
Chapter Switcher
ui medium
Epic Risks (3)
high impact medium prob security

If the AccessScopeService and the Supabase RLS policies use different logic to determine accessible units, a coordinator could see data in the client that RLS blocks server-side, causing confusing empty states, or worse, RLS could block data the scope service declares accessible.

Mitigation & Contingency

Mitigation: Define the canonical scope computation in a single Supabase Postgres function shared by both the RLS policies and the RPC endpoint called by AccessScopeService. The client-side service calls this RPC rather than reimplementing the logic, ensuring a single source of truth.

Contingency: Add integration tests that execute the same access decision through both the RLS policy path and the AccessScopeService path and assert identical results. Use these as regression guards in the CI pipeline.

medium impact medium prob integration

When a user switches active chapter via the ChapterSwitcher, widgets that are already built may not receive the context-change event if they subscribe incorrectly to the ActiveChapterState BLoC, leading to stale data being displayed under the new chapter context.

Mitigation & Contingency

Mitigation: Use Riverpod's ref.watch on the active chapter provider at the root of each scoped data subtree rather than at individual leaf widgets. Trigger a global data refresh by invalidating all scoped providers when the chapter changes.

Contingency: Add an app-level chapter-change listener that forces a full navigation stack reset to the home screen on chapter switch, guaranteeing all widgets rebuild from scratch with the new context. Accept the UX cost of navigation reset for correctness.

medium impact medium prob scope

Non-technical organization administrators may find the hierarchy management interface too complex for the structural changes they need to make frequently (e.g., chapter renaming, coordinator reassignment), leading to low adoption and continued reliance on manual processes.

Mitigation & Contingency

Mitigation: Conduct usability testing with at least one NHF administrator before finalizing the admin portal screen layout. Prioritize the most common operations (rename, reparent, add child) as primary actions in the UI. Include inline help text and confirmation dialogs with plain-language descriptions of consequences.

Contingency: Provide a simplified 'quick edit' mode that exposes only the three most common operations (rename, deactivate, add child) and hides advanced structural operations behind an 'Advanced' toggle.