high priority medium complexity backend pending backend specialist Tier 4

Acceptance Criteria

routeToFileGenerator() is a private method on BufdirExportService accepting EdgeFunctionResponse and ExportFormat
ExportFormat.pdf dispatches to PdfGenerationService and returns a FileGeneratorResult with mimeType 'application/pdf'
ExportFormat.csv dispatches to CsvGenerationService and returns a FileGeneratorResult with mimeType 'text/csv; charset=utf-8'
An unsupported format (e.g., a future enum value not handled) throws BufdirUnsupportedFormatException with the format name included in the message
FileGeneratorResult is a sealed/freezed data class containing: fileUrl (String), mimeType (String), fileSizeBytes (int), and format (ExportFormat)
The routing method does not perform I/O itself — it delegates entirely to the injected generator services
Unit tests cover: PDF routing, CSV routing, unsupported format exception
All switch/if branches covering ExportFormat are exhaustive (Dart exhaustive switch or lint enforced)

Technical Requirements

frameworks
Flutter
Riverpod
performance requirements
Routing logic is synchronous and completes in under 1 ms — all I/O is deferred to generator services
No memory allocation beyond constructing the dispatch arguments
security requirements
Routing logic must not log the edge function payload as it may contain personal data
MIME type must be set explicitly — never derived from file extension alone to prevent content-type spoofing

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Implement as a private _routeToFileGenerator() method in BufdirExportService to keep the interface clean. Use a Dart exhaustive switch expression (available in Dart 3): `return switch (format) { ExportFormat.pdf => await _pdfGenerator.generate(response), ExportFormat.csv => await _csvGenerator.generate(response), _ => throw BufdirUnsupportedFormatException(format) }`. Inject both PdfGenerationService and CsvGenerationService via Riverpod into BufdirExportService's constructor to allow mocking in tests. Define FileGeneratorResult as a freezed data class in its own file.

Keep BufdirUnsupportedFormatException in the shared exceptions file created in task-005.

Testing Requirements

Unit tests with flutter_test: (1) ExportFormat.pdf → PdfGenerationService.generate() called, CSV service not called, FileGeneratorResult has application/pdf, (2) ExportFormat.csv → CsvGenerationService.generate() called, PDF service not called, FileGeneratorResult has text/csv, (3) unsupported format enum value → BufdirUnsupportedFormatException thrown, (4) FileGeneratorResult fields correctly populated from generator return value. Use mockito stubs for both generator services. Test that adding a new ExportFormat without updating the switch causes a compile-time exhaustiveness warning.

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

The scope selector must accurately reflect each coordinator's access rights within the org hierarchy. If a coordinator can select a scope broader than their authorised access, the edge function's RLS enforcement must catch the attempt — but a permissive RLS policy or a bug in the scope resolver could allow unauthorised data to be exported.

Mitigation & Contingency

Mitigation: Implement permission enforcement at two independent layers: (1) the scope selector only renders options permitted by the user's role record, and (2) the edge function re-validates the requested scope against the user's JWT claims before executing any queries. Write integration tests that attempt to invoke the edge function with a scope beyond the user's permissions and assert rejection.

Contingency: If a permission bypass is discovered post-launch, immediately disable the export feature via the org-level feature flag while the fix is deployed. Review all audit records for exports that may have included out-of-scope data and notify affected organisations.

medium impact medium prob technical

The export workflow has 7+ discrete states (idle, scope selected, period selected, preview loading, preview ready, confirming, exporting, complete, failed) and several conditional transitions. An incomplete BLoC state machine could allow duplicate submissions, stale preview data to be confirmed, or error states to be unrecoverable without a restart.

Mitigation & Contingency

Mitigation: Model the state machine explicitly as a sealed class hierarchy before coding. Review the state diagram against all user story acceptance criteria. Write bloc unit tests for every valid and invalid state transition, including the happy path and all documented error states.

Contingency: If the BLoC grows too complex to test reliably, decompose it into two cooperating blocs: one for configuration (scope + period selection) and one for execution (preview + confirm + export), linked by a coordinator object.