critical priority medium complexity backend pending backend specialist Tier 0

Acceptance Criteria

A sealed class or equivalent hierarchy for ContactDetailState is defined with at minimum four subtypes: ContactDetailInitial, ContactDetailLoading, ContactDetailLoaded, and ContactDetailError
ContactDetailLoaded carries typed fields for contact profile, activity history, assignment status, and a list of encrypted field keys
ContactDetailError carries a typed ContactDetailErrorType enum covering networkFailure, notFound, and permissionDenied
A BLoC (ContactDetailBloc) or Riverpod StateNotifier (ContactDetailNotifier) skeleton is created with clearly named events or public methods matching the required public API surface
Provider or BLoC registration is wired via Riverpod ProviderScope or BlocProvider so that ContactDetailRepository and AssignmentRepository are injected via constructor and not created inline
All state classes are immutable (use final fields and const constructors where possible)
The file structure follows the existing feature-layer conventions in the codebase (e.g., contact_detail_state.dart, contact_detail_bloc.dart or contact_detail_notifier.dart)
No business logic or data-fetching code is present in this task — skeleton only
All public symbols are exported from the feature barrel file

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
Riverpod (flutter_riverpod)
data models
Contact
ActivityHistory
AssignmentStatus
OrganisationConfig
performance requirements
State classes must be immutable to allow efficient equality checks and prevent unintended rebuilds
Provider must be scoped appropriately to avoid unnecessary global state retention
security requirements
No sensitive contact data should be stored in state beyond what is required for the current screen render
Encrypted field keys list must not expose the actual encrypted values — only the field identifiers

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Choose BLoC if the existing codebase already uses flutter_bloc for similar features; otherwise use Riverpod StateNotifier for consistency with newer feature modules. Use sealed classes (Dart 3+) for the state hierarchy to get exhaustive pattern-matching in the UI layer — this eliminates runtime type-check errors. Define the ContactDetailErrorType as a standalone enum in its own file to keep it reusable. The provider/BLoC should accept repositories via constructor injection (not read them internally) to allow easy mocking in tests.

Do not use abstract classes with extends for states if the codebase is on Dart 3+ — sealed classes are strictly better here.

Testing Requirements

Unit tests using flutter_test: (1) Verify initial state is ContactDetailInitial. (2) Verify each state subtype carries the correct typed fields via copyWith or constructor assertions. (3) Verify provider/BLoC can be instantiated with mocked repositories without throwing. (4) Verify that ContactDetailErrorType enum covers all required cases.

No integration tests needed for this skeleton task.

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

Parallel fetching of profile, activity history, and assignment status from contact-detail-service may produce race conditions where partial state is emitted to the UI before all fetches complete, resulting in flickering or incorrect loading indicators.

Mitigation & Contingency

Mitigation: Use Future.wait or a single composed BLoC event that only emits a loaded state once all three futures resolve. Define a strict state machine: initial → loading → loaded/error with no intermediate partial-loaded states emitted to the UI.

Contingency: If parallelism proves unreliable in testing, fall back to sequential fetching with a combined loading indicator. The 500ms target may need to be renegotiated with stakeholders if sequential fetching exceeds it on slow connections.

high impact low prob integration

The partial-field update pattern in contact-edit-service assumes the contact record has not changed between when the edit screen was loaded and when the save is submitted. Concurrent edits by another coordinator could cause the earlier editor's save to silently overwrite the later one.

Mitigation & Contingency

Mitigation: Include an updated_at timestamp in the PATCH request and configure Supabase to reject updates where the server-side timestamp differs from the client's version. Return a 409-equivalent error that the service maps to a user-readable conflict message.

Contingency: If optimistic locking is too complex for initial delivery, implement a simple 'reload and retry' flow: on save error, reload the contact detail and prompt the coordinator to re-apply their changes manually.