critical priority medium complexity backend pending backend specialist Tier 1

Acceptance Criteria

BufdirExportService is defined as an abstract class (Dart interface pattern) with exactly two abstract methods: Future<BufdirPreviewResult> requestPreview(BufdirExportRequest request) and Future<BufdirExportResult> requestFinalExport(BufdirExportRequest request, ExportFormat format)
BufdirExportRequest data class contains: scope (ExportScope), period (DateTimeRange), requestedBy (String userId), and organisationId (String) — all required, no nullable fields
BufdirPreviewResult data class contains: estimatedActivityCount (int), periodLabel (String), scopeLabel (String), categoryBreakdown (Map<String, int>), and generatedAt (DateTime)
BufdirExportResult data class contains: fileUrl (String), fileName (String), auditRecordId (String), generatedAt (DateTime), and expiresAt (DateTime)
ExportAuditRecord data class contains: id (String), requestedBy (String), scope (ExportScope), period (DateTimeRange), format (ExportFormat), status (ExportStatus), createdAt (DateTime), completedAt (DateTime?)
ExportFormat enum is defined with at least: csv, xlsx values
ExportStatus enum is defined with: pending, processing, completed, failed values
All data classes have toJson() and fromJson() factory constructors that round-trip without data loss
All data classes are immutable with copyWith() methods
The JSON field names in toJson() match exactly the keys expected by the Supabase edge function API contract (confirm with edge function spec)
Unit tests verify toJson/fromJson round-trip for all data classes with non-trivial values (non-empty maps, non-null optional fields)
All files are placed in lib/features/bufdir_export/models/ with a barrel export file

Technical Requirements

frameworks
Dart
Flutter (for DateTimeRange if used)
Riverpod (if any providers reference these models directly)
apis
Supabase Edge Function — BufdirExport function (confirm JSON contract)
data models
BufdirExportRequest
BufdirPreviewResult
BufdirExportResult
ExportAuditRecord
ExportScope (from task-001)
ExportFormat
ExportStatus
performance requirements
Serialisation/deserialisation must be synchronous and complete in microseconds — no async operations in toJson/fromJson
Models must not hold references to heavy objects (no widget references, no Supabase client references)
security requirements
BufdirExportRequest must validate that requestedBy matches the authenticated user at service call time — the interface contract should document this requirement
fileUrl in BufdirExportResult must be a signed URL — document in code comments that it expires at expiresAt
ExportAuditRecord must not be mutable after creation — the completed state is immutable

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Check whether the project already uses a code generation tool like json_serializable or freezed for data classes — if so, use it here for consistency rather than writing manual toJson/fromJson. If json_serializable is used, run build_runner and commit the generated .g.dart files. The DateTimeRange serialisation needs special handling — Flutter's DateTimeRange does not have built-in JSON support, so write a custom serialiser that stores start and end as ISO 8601 strings. Confirm the exact JSON field names with the edge function team before finalising — a mismatch here will cause silent failures at runtime.

Create an interface contract document at docs/bufdir-export-api-contract.md that the edge function team and app team both sign off on.

Testing Requirements

Write unit tests for all toJson/fromJson methods. For each data class: (1) create an instance with all fields populated, (2) call toJson(), (3) call fromJson() on the result, (4) assert equality with the original. Test edge cases: empty categoryBreakdown map, null completedAt in ExportAuditRecord. Test that fromJson throws a descriptive exception (not a null pointer) when required fields are missing.

Verify that ExportScope values serialise to the string keys defined in task-001 (not enum index integers). 100% line coverage expected on all model files.

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.