high priority medium complexity backend pending backend specialist Tier 6

Acceptance Criteria

When a user selects a new chapter, the active chapter ID is persisted to secure local storage immediately (within the same event handler)
The ActiveChapterState BLoC emits a new state within 100ms of receiving the SwitchChapterEvent
All downstream widgets subscribed to ActiveChapterState (stats, contacts, activities) rebuild and fetch scoped data for the new chapter automatically
On app relaunch, the last persisted chapter ID is read during app initialization and used to seed the initial ActiveChapterState
If the persisted chapter ID is no longer valid (deleted or access revoked), the app falls back to the user's first available chapter and clears the stale persisted value
If no chapter has been previously selected, the app defaults to the first chapter in the user's list
Persisted chapter ID is stored securely and cannot be read by other apps (using flutter_secure_storage or equivalent)
Chapter switch does not cause a full app reload — only affected data layers re-fetch
All active chapter-scoped Supabase queries (contacts, stats, activities) include the current chapter ID as a filter parameter

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
Riverpod (if used alongside BLoC for DI)
apis
Supabase RLS policies scoped to chapter_id
Supabase Realtime (if chapter-scoped live queries are needed)
data models
Chapter
ActiveChapterState
UserChapterAccess
performance requirements
Persist chapter ID to storage in under 50ms (non-blocking, fire-and-forget acceptable)
Session restore completes before first frame of the home screen renders
Downstream data re-fetches are triggered in parallel, not sequentially
security requirements
Use flutter_secure_storage (iOS Keychain / Android Keystore) — do not use SharedPreferences for chapter ID
Validate that the restored chapter ID belongs to the authenticated user before applying it
Supabase RLS must enforce chapter-scoped access independently of client-side state

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Model the ActiveChapterCubit with three states: ChapterLoading, ChapterLoaded(activeChapter, availableChapters), ChapterError. Initialize the cubit by calling a loadInitialChapter() method in the constructor that reads from secure storage and validates against the Supabase chapters table. Use flutter_secure_storage with the key 'active_chapter_id_{userId}' to namespace per user and avoid cross-user contamination. Broadcast via the BLoC stream — downstream widgets use BlocListener or BlocBuilder scoped to ActiveChapterCubit.

Do not use a custom event bus or StreamController for broadcast; the BLoC stream already provides this. For Supabase queries that are chapter-scoped, inject the active chapter ID via a repository layer (ChapterScopedRepository) rather than reading it directly from the BLoC in widgets.

Testing Requirements

Write unit tests for the ActiveChapterCubit/Bloc covering: (1) SwitchChapterEvent persists correct chapter ID, (2) initial state loads from storage on construction, (3) invalid persisted ID falls back to first chapter, (4) no chapter in storage falls back to first chapter. Write integration tests verifying downstream BLoC/provider rebuilds when chapter switches. Mock flutter_secure_storage in tests. Verify that removing user access to a chapter and relaunching the app correctly handles the stale stored ID.

Aim for 95%+ unit test coverage on the ActiveChapterCubit and the persistence layer.

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.