critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

A `ContactListService` class exists in the correct package path with a single constructor accepting `ContactRepository` and `ContactSearchService` as required named parameters
The class exposes at minimum three public method signatures: `getContactsForCoordinator`, `getContactsForOrgAdmin`, and `getContactsForPeerMentor`, each returning `Future<List<Contact>>` or a stream equivalent
Each method's signature includes the caller's user ID (or equivalent identity context) as a required parameter for scoping the query
An abstract interface (`IContactListService` or equivalent) is defined separately from the implementation class, enabling mock injection in tests
Method signatures are documented with inline comments describing which role they serve and what the expected result set is
The constructor parameters use Dart's `required` keyword and are typed to their interfaces (not concrete classes) to support dependency injection
The file compiles without errors (`npm run build` / `dart analyze` returns no issues in this file)
No business logic or database calls are implemented in this task — only the structural skeleton

Technical Requirements

frameworks
Flutter
Riverpod
BLoC
apis
ContactRepository (internal)
ContactSearchService (internal)
data models
Contact
UserRole
UserIdentity
performance requirements
Method signatures must support async/await pattern to allow non-blocking database calls in implementations
security requirements
User identity context (caller's role and ID) must be a required parameter — no global state for role resolution
The interface must not expose raw SQL or Supabase query builders — only domain-level return types

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Define the abstract interface first (`abstract class IContactListService`) then have `ContactListService` implement it. This pattern enables Riverpod to provide a `Provider` that can be overridden in tests. Use Dart's abstract methods with `Future>` return types. The three role branches should have distinct method names rather than a single method with a role parameter — this makes the call sites explicit and avoids switch-on-enum anti-patterns in the service layer.

Name the methods after the actor, not the filter (e.g. `getContactsForCoordinator(coordinatorId)` is clearer than `getContacts(role: Role.coordinator, userId: id)`). Do not implement Riverpod providers in this task — that belongs to the DI wiring task.

Testing Requirements

Write unit tests asserting: (1) ContactListService can be constructed with mock ContactRepository and ContactSearchService, (2) the class implements IContactListService, (3) calling each method with valid parameters does not throw a compile-time or runtime error (stub implementations return empty lists). These are structural tests to enforce the contract before implementation begins. Use flutter_test with mockito or manual fake classes.

Component
Contact List Service
service medium
Epic Risks (2)
medium impact medium prob technical

For organizations with large contact lists (NHF has 1,400 local chapters and potentially thousands of contacts), local in-memory filtering may be too slow and Supabase ILIKE queries without supporting indexes may exceed acceptable response times or accumulate excessive read costs, degrading search usability for power users.

Mitigation & Contingency

Mitigation: Define and document the list-size threshold in ContactSearchService before implementation. Confirm that indexes on name and notes columns exist in the Supabase schema before enabling server-side search. Profile ContactSearchService against realistic data volumes in the staging environment using the largest expected org.

Contingency: If response times are unacceptable in staging, introduce result-count pagination in ContactListService and add a user-visible 'showing top N results — refine your search' indicator, deferring full pagination to a follow-up task.

high impact low prob security

In NHF's multi-chapter context, when a user switches organization, Riverpod providers may emit a brief window of stale contact data scoped to the previous organization before the invalidation cycle completes, transiently exposing contacts from the wrong chapter.

Mitigation & Contingency

Mitigation: Model organization context as a Riverpod provider dependency so that any context change immediately marks contact providers as stale. Render a loading skeleton instead of the stale list during the re-fetch transition. Cover this scenario in integration tests with explicit org-switch sequences.

Contingency: If race conditions are observed during QA, add an explicit organization_id equality check in ContactListService that compares each fetched record's scope to the active session org, discarding any mismatched batch before returning results to the provider.