high priority low complexity backend pending backend specialist Tier 4

Acceptance Criteria

bufdirColumnMapperProvider is a Provider<BufdirColumnMapper> that reads its OrganisationColumnConfigRepository dependency from a previously defined repository provider
bufdirExportAuditServiceProvider is a Provider<BufdirExportAuditService> that reads its IBufdirAuditRepository dependency from the repository provider
bufdirExportOrchestratorProvider is an AsyncNotifierProvider<BufdirExportOrchestratorNotifier, ExportResult?> that reads bufdirColumnMapperProvider, bufdirExportAuditServiceProvider, and all other required sub-service providers via ref.watch
All three providers are defined in a single file (bufdir_export_providers.dart) for discoverability
The UI trigger screen can call ref.read(bufdirExportOrchestratorProvider.notifier).startExport(request) to initiate an export
While an export is in progress, ref.watch(bufdirExportOrchestratorProvider) returns AsyncLoading so the UI can display a progress indicator
On success, the provider state transitions to AsyncData(ExportSuccess) and the UI can extract the downloadUrl
On failure, the provider state transitions to AsyncError and the UI can display the ExportFailure.errorMessage
No provider uses ProviderScope.overrides at the app level for these services — all dependencies are resolved via the provider graph, not manual instantiation
Providers are scoped appropriately: services that hold no mutable state use Provider (not StateProvider or StateNotifierProvider)

Technical Requirements

frameworks
Flutter
Riverpod
Dart
apis
Supabase client provider (existing supabaseClientProvider)
BufdirActivityQueryService provider (existing)
OrganisationColumnConfigRepository provider (existing)
data models
ExportRequest
ExportResult
ExportSuccess
ExportFailure
performance requirements
Provider graph must resolve synchronously at app startup with no async initialisation required for the service layer
startExport must not block the UI thread — delegate to a background isolate within the notifier
security requirements
Providers must not expose the raw Supabase client directly — only service abstractions should be accessible from the UI layer
The authenticated user's organisationId must be sourced from an existing auth provider (e.g., currentUserProvider) and passed into ExportRequest by the UI, not hardcoded in the provider

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Follow the project's existing Riverpod conventions — check whether the codebase uses code-generation (@riverpod annotation) or manual provider definitions and match that style. Define BufdirExportOrchestratorNotifier as an AsyncNotifier with an initial state of null. The build() method should return null (no auto-start). The startExport(ExportRequest request) method sets state = const AsyncLoading() then awaits the orchestrator service and sets state = AsyncData(result) or AsyncError(failure).

Keep providers in a dedicated feature folder (lib/features/bufdir_export/providers/) consistent with the project's feature-first folder structure. Document each provider with a one-line dartdoc comment explaining its responsibility.

Testing Requirements

Unit tests (flutter_test with ProviderContainer): verify that bufdirExportOrchestratorProvider resolves without throwing when all dependencies are overridden with fakes. Test that calling startExport transitions the provider through AsyncLoading → AsyncData(ExportSuccess) for a happy-path mock. Test that a mock failure transitions to AsyncError with the correct ExportFailure. Use ProviderContainer with overrides to inject mock implementations — no real Supabase connection needed.

Test that the provider does not reset its state between multiple startExport calls (i.e., second call replaces first result correctly).

Component
Bufdir Export Orchestrator Service
service high
Epic Risks (2)
medium impact medium prob scope

Bufdir's column schema may have per-field business rules (conditional required fields, cross-field validation, organisation-specific category taxonomies) that cannot be expressed in a simple key-value mapping configuration. If the configuration model is too simple, supporting NHF's specific requirements will require hardcoded organisation logic, undermining the configuration-driven design.

Mitigation & Contingency

Mitigation: Design the column configuration schema as a full JSON document supporting field-level transformation rules, conditional expressions, and org-specific value enumerations. Validate the design against a real NHF Bufdir Excel template before implementation begins.

Contingency: If the configuration model cannot express all required rules, implement a thin transformation plugin interface where org-specific logic can be added as a named Dart class registered against the organisation ID, with the JSON config covering only the common cases.

high impact medium prob technical

For large organisations like NHF with potentially tens of thousands of activity records, the full export pipeline (query + map + generate + bundle + upload) may exceed Supabase Edge Function execution time limits (typically 150s), causing silent timeouts that leave audit records in a pending state indefinitely.

Mitigation & Contingency

Mitigation: Implement the orchestrator as a background Dart isolate with progress streaming rather than a synchronous Edge Function call. Use chunked processing for the query and mapping phases to reduce peak memory usage. Profile against realistic NHF data volumes in a staging environment.

Contingency: If processing time cannot be reduced below the timeout threshold, implement an asynchronous job model where the export is queued, processed in the background, and the user is notified via push notification when the download is ready — treating it as an eventual rather than synchronous operation.