high priority medium complexity backend pending backend specialist Tier 4

Acceptance Criteria

The validator accepts a BufdirPayload and returns a ValidationResult: { isValid: bool, errors: List<ValidationError>, warnings: List<ValidationWarning> }
Each ValidationError contains: { field: string (dot-notation path), code: string, message: string (human-readable, in Norwegian for coordinator display) }
Required field check: organisation_number, reporting_period_start, reporting_period_end, schema_version are all present and non-empty
Org number format check: exactly 9 digits, matches Norwegian org number regex `^[0-9]{9}$`
Date range validity: reporting_period_start is before reporting_period_end, neither date is in the future, period does not exceed 12 months
Non-negative totals: grand total activity_count >= 0, grand total participant_count >= 0, all per-category totals >= 0
Plausibility check: participant_count per peer mentor does not exceed a configurable threshold (default 5000) — produces a WARNING not an error
Zero-activity payload (all totals = 0) produces a specific warning code 'EMPTY_REPORT' but is not blocked
The validator is called after serialization in the edge function pipeline; if errors exist, the edge function returns HTTP 422 with the full ValidationResult JSON
When isValid is false, no export file (CSV or PDF) is generated

Technical Requirements

frameworks
Dart (Supabase Edge Function / Deno runtime)
apis
Bufdir submission rules (internal reference document)
data models
BufdirPayload
ValidationResult
ValidationError
ValidationWarning
performance requirements
Validation must complete in under 50 ms for any payload size
All validation rules run in a single pass over the payload — no repeated traversals
security requirements
Error messages must not leak internal system identifiers or stack traces to the client response
Plausibility threshold must be server-side configurable (env variable) — not hardcoded — so it can be updated without redeployment

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Structure the validator as a list of rule functions, each with signature `List Function(BufdirPayload)`. The main `validate()` function runs all rules and collects results, never short-circuiting. This pattern makes adding new Bufdir rules trivial. Use Norwegian error messages because coordinators will see them in the app preview response — agree exact Norwegian wording with the product team.

Define error codes as Dart constants (e.g. `const String kMissingOrgNumber = 'MISSING_ORG_NUMBER'`) so the Flutter client can match on codes programmatically for localised display. The plausibility threshold should be read from an environment variable (`BUFDIR_MAX_PARTICIPANTS_PER_MENTOR`) with a fallback default of 5000. Keep validation logic free of any I/O — pure functions only.

Testing Requirements

Unit tests (dart:test) for each validation rule in isolation: (1) valid payload passes all checks, (2) missing organisation_number returns error with correct field path and code, (3) malformed org number (8 digits, letters) returns error, (4) start date after end date returns error, (5) future dates return error, (6) period > 12 months returns error, (7) negative activity_count returns error, (8) participant_count > threshold returns warning not error, (9) all-zero totals returns EMPTY_REPORT warning, (10) multiple errors on one payload are all reported (not fail-fast). Integration test: pass the serializer output from task-006 tests through the validator and assert no errors for well-formed data. Target 100% branch coverage for each individual rule function.

Component
Bufdir Format Serializer
data medium
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.