critical priority medium complexity database pending database specialist Tier 1

Acceptance Criteria

getAssignmentsByUser(userId) returns all unit assignments for a user, including primary unit flag
getAssignmentsByUnit(unitId) returns all users assigned to a given unit
setPrimaryUnit(userId, unitId) sets the specified unit as primary and unsets any existing primary for that user atomically
batchUpdateAssignments(userId, assignments) replaces all assignments for a user in a single transaction
All read operations check the hierarchy cache first; cache miss triggers Supabase fetch and populates cache
RLS policies on the Supabase `unit_assignments` table restrict reads/writes to authorized roles only; repository does not bypass RLS
Repository throws typed exceptions (e.g., AssignmentNotFoundException, PermissionDeniedException) on Supabase errors
Unit assignments for a user belonging to multiple local chapters (up to 5, per NHF requirement) are all returned correctly
batchUpdateAssignments is idempotent: calling it twice with the same data produces the same result

Technical Requirements

frameworks
Flutter
Riverpod
apis
Supabase PostgREST
Supabase RLS
data models
UnitAssignment
OrganizationUnit
AppUser
performance requirements
getAssignmentsByUser returns within 500ms on a cold cache (network included)
batchUpdateAssignments for up to 10 units completes within 2 seconds
security requirements
All queries executed with the authenticated user's JWT; no service-role key used on client
RLS on unit_assignments table enforced at database level — repository must not add client-side filtering as a security boundary
Data from one organization must never be visible to users of another organization (multi-tenancy isolation)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Define a `UnitAssignmentRepository` abstract class and a `SupabaseUnitAssignmentRepository` concrete implementation. Inject `HierarchyCache` via constructor for cache-aside reads. For `setPrimaryUnit`, use a Supabase RPC (PostgreSQL function) to perform the unset+set atomically rather than two sequential client calls — this avoids race conditions. For `batchUpdateAssignments`, use a Supabase upsert with `onConflict` handling.

Map Supabase `PostgrestException` error codes to typed Dart exceptions in a private `_mapError()` helper. Register the repository as a Riverpod `Provider` so it can be overridden in tests.

Testing Requirements

Unit tests with MockSupabaseClient to cover all 5 repository methods. Integration tests (optional, run in CI against a local Supabase instance) covering RLS: verify that a user from org A cannot read org B's assignments. Test setPrimaryUnit atomicity: inject a fault between the unset and set operations and verify rollback. Test batchUpdateAssignments with 0, 1, and 10 assignments.

Test typed exception mapping from Supabase error codes (403 → PermissionDeniedException, 404 → AssignmentNotFoundException).

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.