critical priority high complexity backend pending backend specialist Tier 6

Acceptance Criteria

When format=csv or format=pdf, the edge function runs the full aggregation → serialization → generation → upload → record pipeline
CSV generation produces a valid UTF-8 encoded CSV file with BOM for Excel compatibility, with columns matching the org's bufdir_column_schema
PDF generation produces a valid PDF/A file with Bufdir report structure including org name, period, scope, and all activity rows
File is uploaded to the Supabase private export storage bucket at path: {org_id}/{year}/{period_slug}/{uuid}-bufdir-export.{ext}
File path is deterministic but includes a UUID segment to prevent path enumeration
Export record is written to generated_reports table with all required metadata fields before returning the signed URL
Signed download URL is valid for exactly 24 hours from generation time
Response body is: { export_id: string, download_url: string, file_size_bytes: number, expires_at: ISO8601 }
If file upload fails, the generated_reports record is NOT created and HTTP 500 is returned with error details
If generated_reports insert fails after successful upload, the orphaned file is deleted from storage and HTTP 500 is returned
Idempotency: if an export with identical org_id, scope_id, period, and format already exists (completed status), return the existing export_id with a fresh signed URL instead of regenerating
Total finalize processing time for up to 500 rows must complete within 30 seconds
Bufdir export audit log entry is created on every finalize call regardless of success or failure

Technical Requirements

frameworks
Supabase Edge Functions (Deno)
Deno std/csv
pdf-lib or similar Deno-compatible PDF library
apis
Supabase Storage API (upload, createSignedUrl)
Supabase PostgreSQL (generated_reports insert, audit log)
data models
bufdir_export_audit_log
bufdir_column_schema
activity
activity_type
assignment
performance requirements
File generation must complete within 20s for 500 rows
Storage upload must use streaming to avoid buffering entire file in memory
Generated_reports insert must occur within a DB transaction with the audit log entry
Signed URL generation must be a single Supabase SDK call with 86400 second expiry
security requirements
Storage bucket must have RLS: only service role can write; coordinators can only read their org's files via signed URLs
Signed URLs do not expose the internal storage path — they use Supabase's opaque URL scheme
File paths include UUID to prevent enumeration of other orgs' exports
GDPR: exported files contain personal data — bucket must be private, signed URLs expire after 24h
Idempotency check uses DB query, not file path check, to avoid storage timing attacks

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Structure the finalize path as: (1) check idempotency via DB query, (2) run aggregation + serialization (reuse preview pipeline), (3) branch to CSV or PDF generator, (4) upload to storage with streaming, (5) insert generated_reports record + audit log in a single transaction, (6) generate signed URL, (7) return response. Implement rollback logic: wrap steps 4-5 in a try/catch; on step 5 failure, delete the uploaded file. For CSV, use Deno's std/csv for encoding. For PDF, evaluate pdf-lib (pure Deno compatible) — keep the PDF layout simple: header with org/period metadata, then a table of activity rows.

The deterministic path structure (org_id/year/period_slug/uuid.ext) ensures files are logically organised but UUID prevents guessing adjacent exports. Period slug format: YYYY-QN for quarterly, YYYY-HN for half-year, YYYY for annual.

Testing Requirements

Unit test CSV generation: provide known aggregated data, assert output is valid CSV with correct column order, BOM present, all rows encoded correctly, special characters escaped. Unit test PDF generation: assert output bytes start with %PDF, file is non-empty, and title metadata matches org/period. Integration test the full finalize pipeline: call edge function with format=csv, assert HTTP 200, download the signed URL and verify the file contents match the input data. Integration test idempotency: call the same export twice and assert the second call returns the same export_id with a new signed URL.

Test failure rollback: mock storage upload failure and assert no generated_reports record was created. Test audit log is created even on failure. Use Deno test with a real Supabase test project for integration scenarios.

Component
Bufdir Export Edge Function
infrastructure high
Epic Risks (3)
high impact medium prob technical

Supabase Edge Functions have a default execution timeout. For large national-scope exports aggregating tens of thousands of activities across 1,400 chapters, the edge function may time out before completing, leaving coordinators with a failed export and no partial output.

Mitigation & Contingency

Mitigation: Optimise the aggregation SQL using pre-materialised aggregation views or RPC functions that run inside the database rather than iterating records in Deno. Profile query execution time against realistic production data volumes early. Request an elevated timeout limit from Supabase if needed. Implement progress checkpointing so the export can be resumed from the last completed aggregation batch.

Contingency: For organisations exceeding a configurable threshold (e.g. >5,000 activities), switch to an asynchronous export pattern: the edge function writes a 'pending' audit record and enqueues the job; the client polls for completion and is notified via Supabase Realtime when the file is ready.

medium impact medium prob technical

Server-side PDF generation in a Deno Edge Function environment restricts library choices. Many popular PDF libraries require Node.js APIs not available in Deno, or produce large bundle sizes that exceed edge function limits. Choosing the wrong library could block the entire PDF generation path.

Mitigation & Contingency

Mitigation: Spike PDF library selection as the first task of this epic, evaluating at least two Deno-compatible options (e.g. pdf-lib, jsPDF with Deno compatibility shim). Test bundle size and basic rendering before committing to an implementation. Document the chosen library's constraints.

Contingency: If no suitable Deno-native PDF library is found, generate a well-structured HTML report from the edge function and use a headless Chromium service (e.g. Browserless, Gotenberg) for HTML-to-PDF conversion, or temporarily ship CSV-only export while the PDF path is resolved.

high impact high prob technical

Peer mentors affiliated with multiple chapters (a documented NHF scenario) must not be double-counted in participant totals. Incorrect deduplication logic would overreport participation figures to Bufdir, which could be discovered during audit and damage organisational credibility.

Mitigation & Contingency

Mitigation: Define and document the deduplication contract explicitly before coding: deduplication is per-person per-period, not per-activity. Build dedicated unit tests with fixtures containing the exact multi-chapter membership patterns described in NHF's documentation. Have a NHF representative validate test fixture outputs against known-good manual counts.

Contingency: If deduplication logic produces results that cannot be verified against manual counts before launch, surface a deduplication warning in the export preview listing the affected peer mentor IDs, and require explicit coordinator acknowledgement before finalising the export.