high priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

attachFileRef method accepts storagePath (String), mimeType (String), fileSizeBytes (int), and sha256Checksum (String) and persists them to the bufdir_export_audit table row identified by auditId
Export metadata fields — rowCount (int), columnSchemaVersion (String), and organisationConfigSnapshot (Map<String, dynamic>) — are serialised as JSONB and stored in an export_metadata column on the same audit row
attachFileRef is idempotent: calling it twice with the same auditId updates the existing record rather than inserting a duplicate
If the audit record referenced by auditId does not exist, attachFileRef throws AuditRecordNotFoundException with a descriptive message
If the audit record is already in a terminal status (failed, cancelled), attachFileRef throws InvalidAuditStateException and does not mutate the row
All fields written by attachFileRef are readable back via getAuditRecord without data loss or type coercion errors
The organisationConfigSnapshot captures the exact org-level column schema config at the time of export so that the schema can be reconstructed independently of future config changes
sha256Checksum is validated to be a 64-character hex string before persistence; invalid values throw ChecksumFormatException
All database writes use Supabase transactions so that partial writes cannot leave the audit record in an inconsistent state
Unit tests achieve ≥90% line coverage on attachFileRef and related private helpers

Technical Requirements

frameworks
Flutter
Riverpod
Dart
apis
Supabase Database REST API
Supabase Storage API (path reference only, no upload here)
data models
BufdirExportAuditRecord
ExportFileReference
ExportMetadata
OrganisationColumnConfig
performance requirements
attachFileRef must complete the Supabase upsert within 2 seconds under normal network conditions
organisationConfigSnapshot serialisation must not block the main isolate; use compute() if snapshot size exceeds 50 KB
security requirements
Audit records are append-only; the method must never issue DELETE or hard-overwrite of previously stored file references
organisationConfigSnapshot must strip any personally identifiable information (member names, contact details) before persistence
Supabase Row Level Security (RLS) must be enforced so only authenticated users with the bufdir_exporter role can call the underlying upsert
Checksum verification must use constant-time comparison to prevent timing attacks if used for integrity checks later

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Model ExportFileReference and ExportMetadata as immutable Dart value objects (use freezed or plain final fields). The attachFileRef method should perform a Supabase `.update()` scoped by `eq('id', auditId)` inside a `.rpc()` transaction or a Dart-level try/catch that rolls back on failure using Supabase's PostgreSQL functions. Store organisationConfigSnapshot via `jsonEncode()` into a JSONB column — do not store as TEXT to preserve queryability. Define a migration script that adds file_storage_path, mime_type, file_size_bytes, sha256_checksum, and export_metadata (JSONB) columns to the bufdir_export_audits table with NOT NULL constraints only on file_storage_path and sha256_checksum (others nullable for pre-attachment state).

Keep the method signature in an abstract IBufdirAuditRepository so the service stays testable without a real Supabase connection.

Testing Requirements

Unit tests (flutter_test): cover attachFileRef happy path, idempotency on duplicate call, AuditRecordNotFoundException when auditId missing, InvalidAuditStateException on terminal-status record, ChecksumFormatException on malformed checksum, and correct JSONB round-trip for organisationConfigSnapshot. Use a fake in-memory repository implementing the same IBufdirAuditRepository interface to isolate all Supabase calls. Integration test: verify against a Supabase local emulator that the upsert correctly sets all columns and that RLS blocks an unauthenticated caller. Target ≥90% line coverage for the service class.

Component
Export Audit Log Service
service medium
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.