critical priority medium complexity backend pending backend specialist Tier 1

Acceptance Criteria

getAffiliationsForContact returns ChapterMembershipSuccess with the full list of ChapterAffiliation objects for a contact who has one or more affiliations
getAffiliationsForContact returns ChapterMembershipSuccess with an empty list for a contact who exists but has no affiliations
getAffiliationsForContact returns ChapterMembershipFailure(ChapterNotFound) when the contactId does not exist in the repository
Contact existence is validated via a dedicated repository call before the affiliation query — a single combined query is not acceptable if it would silently return empty results for non-existent contacts
Repository results are mapped to ChapterAffiliation domain objects inside the service — raw repository DTOs do not leak into the return value
Repository exceptions (network errors, Supabase errors) are caught and wrapped in a typed domain failure rather than propagating as unhandled exceptions
The implementation class is injectable via Riverpod and its constructor accepts the ContactChapterRepository as a dependency (no service locator calls inside the method body)
Unit tests using a mock/fake ContactChapterRepository cover: happy path with affiliations, empty affiliations, contact not found, and repository error scenarios

Technical Requirements

frameworks
Flutter
Riverpod
apis
Supabase REST API (via ContactChapterRepository abstraction)
data models
ChapterAffiliation
ChapterMembershipResult
Contact
performance requirements
Contact existence check and affiliation fetch should be sequential (not parallel) to avoid unnecessary affiliation queries for non-existent contacts
Method must complete within 2 seconds on a standard mobile connection
security requirements
Supabase Row Level Security policies must ensure a coordinator can only retrieve affiliations for contacts within their own organisation — do not bypass RLS
contactId must be validated as a non-empty UUID-format string before any repository call

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Implement the concrete class as MultiChapterMembershipServiceImpl extending or implementing AbstractMultiChapterMembershipService. Use Riverpod Provider or AsyncNotifierProvider to expose the service. The contact existence check should call a contactRepository.exists(contactId) method (or equivalent) — if ContactRepository is not yet abstracted, coordinate with the team to add a lightweight exists() method rather than fetching the full contact object. Handle the mapping from repository DTOs to ChapterAffiliation domain objects in a private static method or a dedicated mapper class to keep the service method readable.

Wrap all repository calls in try/catch and rethrow as ChapterMembershipFailure with an appropriate domain error subtype.

Testing Requirements

Write unit tests with a fake/mock ContactChapterRepository (implement the abstract interface in test, no actual Supabase calls). Test cases: (1) contact exists, returns 3 affiliations → success with list length 3; (2) contact exists, returns 0 affiliations → success with empty list; (3) contact not found → failure with ChapterNotFound error carrying correct contactId; (4) repository throws a network exception → failure with a generic infrastructure error type. Use flutter_test; no widget pump required. Mock the repository using Dart's mockito package or a hand-written fake.

Aim for 100% branch coverage of the getAffiliationsForContact method.

Component
Multi-Chapter Membership Service
service medium
Epic Risks (2)
medium impact medium prob scope

The ±1 day duplicate detection tolerance is specified in the acceptance criteria but timezone handling is not defined. A coordinator in UTC+2 submitting at 23:00 and another in UTC+0 submitting at 01:00 the next calendar day could trigger or miss a duplicate depending on which timezone the comparison uses.

Mitigation & Contingency

Mitigation: Define and document the authoritative timezone for all date comparisons (UTC stored in Supabase, all comparisons performed in UTC). Add timezone boundary unit tests covering the ambiguous ±1 day edges.

Contingency: If false positives or false negatives are reported in production, provide a coordinator-visible audit trail of duplicate detections so erroneous flags can be investigated and cleared manually.

medium impact medium prob technical

The Duplicate Activity Detection Service performs a cross-chapter join query synchronously during the activity submission flow. On slow mobile connections this could cause a perceptible stall on the submission confirmation step, degrading user experience.

Mitigation & Contingency

Mitigation: Pre-fetch the cross-chapter activity dataset for the selected contact immediately when the contact is selected in the activity wizard (not only at submit time), storing the result in the state manager for instant comparison at submission.

Contingency: If latency is still unacceptable, implement a loading indicator on the submit action and add a configurable server-side timeout with graceful degradation: if the check times out, allow submission with a logged 'check skipped' audit entry rather than blocking the user.