critical priority medium complexity backend pending backend specialist Tier 4

Acceptance Criteria

getAssignmentsForUser(userId) returns all active UnitAssignment records for the user including the unit's display name and type
addAssignment(userId, unitId) creates a new assignment record and calls RlsPolicyManager.refreshSessionClaims() before returning; duplicate assignments are rejected with DuplicateAssignmentException
removeAssignment(userId, unitId) soft-deletes the assignment (sets status to inactive, preserves record), refreshes session claims, and throws CannotRemovePrimaryUnitException if the target is the user's primary unit with no other active assignments remaining
setPrimaryUnit(userId, unitId) sets the specified unit as primary and clears the primary flag on any previously primary unit atomically
bulkAssign(userId, List<unitId>) applies addAssignment logic for each unit, rolls back all changes if any single assignment fails validation, and returns a summary of success/failure per unit
Assignment changes are validated against the hierarchy: a coordinator may only be assigned to a unit within their own organization's hierarchy
All assignment mutations emit an AssignmentChanged event on a shared event bus so dependent services (e.g. AccessScopeService) can react without polling
NHF use case: a user assigned to up to 5 different local chapters is supported without error

Technical Requirements

frameworks
Flutter
Dart
Riverpod
BLoC
apis
Supabase PostgreSQL 15
data models
assignment
contact_chapter
contact
performance requirements
getAssignmentsForUser completes in under 500ms for users with up to 5 assignments
bulkAssign for up to 20 units completes within 3 seconds including RLS claim refresh
security requirements
Admin performing assignment changes must have coordinator or admin role verified from JWT claims — service rejects calls from peer mentor role
userId parameter must be within the caller's organization scope; cross-organization assignment attempts return UnauthorizedException
Assignment records are never hard-deleted to preserve audit trail

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Implement as UnitAssignmentService with Riverpod Provider dependency on OrganizationUnitRepository, RlsPolicyManager (task-006), and HierarchyService (task-007). The service should NOT directly query Supabase — delegate all database operations to the repository layer. For bulkAssign atomicity, wrap the operations in a Supabase transaction via an RPC function (bulk_assign_units) to ensure all-or-nothing semantics — client-side loop with rollback logic is insufficient for concurrent request scenarios. Emit AssignmentChanged events via a Riverpod StateNotifier or a simple StreamController that other providers can listen to.

The RlsPolicyManager.refreshSessionClaims() call should be fire-and-forget with error logging — do not block the assignment result on claim refresh latency. NHF's requirement of handling members in up to 5 local chapters means the primary unit concept is critical: always ensure exactly one primary unit per user with active assignments.

Testing Requirements

Unit tests with mocked Supabase client and mocked RlsPolicyManager covering: add assignment (success), add duplicate (exception), remove primary unit with no fallback (exception), remove primary unit with another active unit (success, primary transferred), bulk assign partial failure (rollback). Integration tests against local Supabase verifying RLS claim refresh is triggered after mutation. flutter_test with mockito. Test the NHF scenario: user with 5 chapter assignments, remove one, verify remaining 4 assignments intact.

Coverage target: 95% of public method branches.

Component
Unit Assignment Panel
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.