high priority medium complexity backend pending backend specialist Tier 3

Acceptance Criteria

requestPreview() appends a preview: true flag in the edge function request body
The edge function response is routed to the PDF or CSV generator based on the requested ExportFormat
BufdirPreviewResult contains: temporaryFileUrl (String), mimeType (String), expiresAt (DateTime), and format (ExportFormat)
No ExportAuditRecord row is inserted into the database during a preview call under any circumstances including partial failures
A prominent Dart doc comment on requestPreview() explicitly states: 'Preview is a read-only operation. ExportAuditRecord MUST NOT be persisted here.'
The temporary file URL from Supabase Storage has a signed expiry of 1 hour maximum
If the edge function returns an error, BufdirPreviewResult is not constructed; the exception propagates to the caller
Unit test confirms zero database writes occur for a successful preview invocation
Unit test confirms zero database writes occur when the edge function returns an error during preview

Technical Requirements

frameworks
Flutter
Riverpod
Supabase Edge Functions (Deno)
Supabase Storage
apis
Supabase Edge Functions REST API
Supabase Storage signed URL API
data models
bufdir_export_audit_log
performance requirements
Signed URL generation must complete within 2 seconds
Preview file must not be stored permanently — use Supabase Storage temp bucket with 1-hour TTL
security requirements
Signed URLs expire after maximum 1 hour per Supabase Storage security policy
Preview files stored in a dedicated preview/ prefix in the private Supabase bucket
Preview files must not be accessible cross-organisation — RLS enforced on storage bucket
No PII written to audit log for preview operations

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Implement requestPreview() as a distinct code path from requestFinalExport() — do not share a single method with a boolean flag. The audit-record exclusion invariant is easier to verify in code review and testing when it is structurally impossible (i.e., the preview method simply has no reference to the audit repository). Pass preview: true in the BufdirEdgeFunctionRequest DTO. After receiving the FileGeneratorResult from task-008 routing, create a signed URL via supabase.storage.from('bufdir-previews').createSignedUrl() with a 3600-second expiry.

Construct BufdirPreviewResult from the signed URL and return. Do not catch exceptions here — let them propagate for the UI layer to display.

Testing Requirements

Unit tests with flutter_test: (1) successful preview returns BufdirPreviewResult with correct URL, mimeType, and expiresAt, (2) no audit record created on success (mock repository's persistAuditRecord and assert zero calls), (3) no audit record created when edge function throws BufdirServerException, (4) PDF format routes to PDF generator, (5) CSV format routes to CSV generator, (6) expiresAt is within 1 hour of test execution time. Use mockito to stub invokeEdgeFunction and the file generator dependencies.

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.