high priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

After ContactEditService emits ContactEditSuccess, ContactDetailService automatically re-fetches the contact record from Supabase without any user action
The detail screen displays updated field values within one render cycle after the re-fetch completes
ContactEditService does NOT hold a direct reference to ContactDetailService (no direct service-to-service call)
Coordination mechanism is via Riverpod provider invalidation OR a shared event/notifier — documented in a comment explaining the chosen pattern
If the re-fetch fails, ContactDetailService emits its own error state independently; the edit success state is NOT rolled back
No duplicate Supabase reads are triggered (e.g., listener fires only once per save event)
If the user navigates away from the detail screen before the re-fetch completes, the re-fetch completes silently and the cached state is updated for the next visit
Integration test verifies that ContactDetailService state contains the updated data after a mocked successful save

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
Supabase select() with eq() filter for single contact re-fetch
data models
ContactDetailState
ContactDetailLoaded
ContactEditSuccess
Contact
performance requirements
Re-fetch must complete within 2 seconds on a standard mobile connection
Detail screen must not show a full loading skeleton on refresh — prefer optimistic display of old data with a subtle refresh indicator
security requirements
Re-fetch must use the same RLS-enforced Supabase client as the initial load
No contact data should be passed directly between services — re-fetch from source of truth

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Preferred pattern with Riverpod: after ContactEditService confirms success, call ref.invalidate(contactDetailProvider(contactId)). This decouples the services — ContactEditService only needs access to the Riverpod Ref, not a reference to ContactDetailService. With BLoC: dispatch a ContactRefreshRequested event to the ContactDetailBloc via the shared event bus or by injecting the bloc's Sink (not the full bloc). Avoid passing the updated Contact object directly from edit to detail — always re-fetch from Supabase to guarantee server-side state (computed fields, updated_at, etc.

may differ). Add a brief shimmer or spinner overlay to the detail screen during the re-fetch to signal freshness without a full rebuild. Guard against race conditions: if the user triggers a second save before the first re-fetch completes, cancel the in-flight re-fetch and restart.

Testing Requirements

Write integration tests using flutter_test and a mocked Supabase client: (1) verify ContactDetailService.state transitions to ContactDetailLoading then ContactDetailLoaded after a ContactEditSuccess event is emitted, (2) verify the loaded contact contains the updated field values, (3) verify that no re-fetch is triggered when ContactEditService emits ContactEditError, (4) verify re-fetch completes without error even when the ContactDetail widget is not mounted. Unit test the coordination mechanism in isolation to confirm it does not create a circular dependency.

Component
Contact Edit 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.