high priority low complexity integration pending integration specialist Tier 4

Acceptance Criteria

syncContactsForOrganisation() is called automatically after every successful online fetch of the contact list — no manual trigger required
The sync call is fire-and-forget (unawaited or launched in a separate isolate-safe context) so the UI renders the contact list immediately without waiting for cache write
A sync failure (network error, Drift write error) does not display any error message or snackbar to the user
A sync failure is logged at warning level for debugging but does not propagate to the BLoC/Riverpod state
The integration point is in the data/repository or use-case layer — not directly in a widget or BLoC event handler
The contact list BLoC/Riverpod notifier emits its Loaded state before the sync completes
Calling the contact list fetch multiple times in quick succession does not trigger multiple concurrent syncs (relies on the isSyncing guard from task-006)
The integration is covered by a widget or use-case integration test that verifies the contact list renders before sync completes

Technical Requirements

frameworks
riverpod or bloc for state management integration
flutter_test / mockito for integration testing
apis
ContactCacheSyncRepository.syncContactsForOrganisation()
data models
contact
assignment
performance requirements
UI contact list must render within 100ms of the Supabase fetch completing — sync must not block this render
Async sync must not cause frame drops or jank — use unawaited() with error swallowing or scheduleMicrotask
security requirements
The organisationId passed to syncContactsForOrganisation() must be derived from the authenticated user's session, not from a UI parameter

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Place the sync trigger in the ContactListRepository's fetchContacts() implementation rather than in the BLoC or widget — this keeps the caching concern in the data layer and avoids leaking infrastructure details into the state management layer. Use `unawaited(cacheSyncRepository.syncContactsForOrganisation(orgId).catchError((e) => logger.w('Cache sync failed: $e')))` to fire-and-forget safely. The `unawaited` utility from `package:meta` or `dart:async` signals intent clearly and suppresses the unawaited-future lint. Never use `ignore: unawaited_futures` as a suppression — prefer the explicit `unawaited()` wrapper.

Ensure the repository is provided via dependency injection (Riverpod provider or BLoC constructor) so it can be mocked in tests.

Testing Requirements

Integration tests using mocked ContactCacheSyncRepository and mocked Supabase client. Cover: (1) after a successful contact list fetch the sync repository's syncContactsForOrganisation() is called exactly once, (2) the contact list BLoC/provider emits Loaded state before sync completes (verify with a delayed sync mock), (3) sync throwing an exception does not cause the BLoC/provider to emit an Error state, (4) rapid successive fetches trigger only one concurrent sync (isSyncing guard). Use flutter_test with fake async timers to control async execution ordering.

Component
Contact Cache Sync Repository
data low
Epic Risks (3)
high impact medium prob security

Supabase RLS policies may not correctly scope ilike search results to the authenticated user's organisation and chapter, causing data leakage across organisations or empty result sets for valid queries.

Mitigation & Contingency

Mitigation: Reuse and extend existing RLS query builder patterns from the contact-list-management feature. Write integration tests against a seeded multi-organisation test database to verify cross-org isolation before merging.

Contingency: If RLS scoping is insufficient, add an explicit organisation_id filter in the Dart query builder layer as a defence-in-depth measure while the Supabase policy is corrected.

medium impact medium prob integration

Adding new Drift tables for the contact cache may conflict with existing migrations or schema versions in the contact-list-management feature if both features cache the same contacts table, causing migration failures on user devices.

Mitigation & Contingency

Mitigation: Audit existing Drift schema versions from contact-list-management before writing new migrations. Reuse existing cache tables if the schema already covers required fields; only add missing fields via ALTER or new version.

Contingency: If schema conflict occurs, consolidate into a single shared cache table owned by contact-list-management and expose a DAO interface to the search feature, avoiding duplicated schema ownership.

medium impact medium prob scope

The offline cache may surface significantly stale contact data if sync has not run recently, leading coordinators to act on outdated information (wrong phone numbers, changed assignments).

Mitigation & Contingency

Mitigation: Store and surface the last-sync timestamp prominently in the UI layer. Trigger a background cache refresh on app foreground when connectivity is detected.

Contingency: If staleness becomes a reported UX issue, implement a maximum-age threshold that shows a warning banner when the cache is older than a configurable limit (e.g. 24 hours).