critical priority medium complexity backend pending backend specialist Tier 0

Acceptance Criteria

mapActivityToBuffirCategory(activityTypeId) returns the correct official Bufdir category code string for a known activity type
Mappings are read exclusively from the activity_type_configuration table — zero hard-coded category values in Dart source
mapActivityToBuffirCategory for an unmapped activityTypeId returns null and appends a structured warning to a MappingWarnings list rather than throwing
batchMap(List<Activity>) processes all activities and returns a List<MappedActivity> where each item holds the original activity plus its resolved category code (or null with a warning)
batchMap returns a BatchMappingResult containing both the mapped list and an aggregated warnings list of all unmapped types encountered
All unmapped activity type IDs encountered during a batch are written to the audit trail (Supabase insert into export_audit_log)
Mapping table is loaded once per export session and cached — not re-fetched per activity
When activity_type_configuration table is empty or unreachable, batchMap throws BufdirMappingUnavailableException with a meaningful message
Unit tests cover: successful mapping, unmapped type produces warning not exception, empty batch, all-unmapped batch, null activityTypeId input

Technical Requirements

frameworks
Flutter
Supabase Dart client (supabase_flutter)
Riverpod (service DI)
apis
Supabase PostgREST (activity_type_configuration table)
Supabase PostgREST (export_audit_log insert)
data models
activity_type_configuration (activity_type_id, bufdir_category_code, label)
Activity
MappedActivity
BatchMappingResult
MappingWarning
performance requirements
Mapping table (expected <500 rows) loaded once per export session into a Map<String, String> cache
batchMap of 10,000 activities must complete in-memory map lookups in under 100ms after cache is warm
Single Supabase fetch to load the entire mapping table at session start
security requirements
activity_type_configuration is a read-only lookup table — service must use a read-only Supabase role with no INSERT/UPDATE permissions
Audit log writes (unmapped warnings) use the authenticated user's session token so the export_audit_log row captures the actor

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Load the full activity_type_configuration table at export session start with a single `.from('activity_type_configuration').select('activity_type_id, bufdir_category_code')` and build a `Map` in the service constructor or a Riverpod AsyncNotifier. All subsequent lookups are O(1) map gets. For the MappingWarning model, include: activityTypeId, activityId, exportSessionId, timestamp — this gives the audit trail enough context for a coordinator to locate and fix the misconfigured type. The batchMap return type BatchMappingResult should be a freezed class: `{List mapped, List warnings}`.

Surface the warnings list to the ExportDataQueryBuilder metadata.warnings channel (established in task-005) so all data-quality issues are consolidated in one place before the export file is generated.

Testing Requirements

Unit tests (flutter_test) with a stub/fake ActivityTypeConfigurationRepository: (1) known activityTypeId maps to correct Bufdir code; (2) unknown activityTypeId returns null and adds to MappingWarnings; (3) null activityTypeId handled gracefully; (4) batchMap with mixed known/unknown returns correct mapped count and warning count; (5) empty batch returns empty result with no warnings; (6) empty mapping table throws BufdirMappingUnavailableException. Integration test against Supabase local instance: seed activity_type_configuration with 10 rows, run batchMap with 2 unmapped types, assert audit_log receives 2 warning rows with correct activityTypeId values.

Component
Bufdir Category Mapper
service medium
Epic Risks (3)
high impact medium prob technical

NHF's three-level hierarchy (national / region / chapter) with 1,400 chapters may have edge cases such as chapters belonging to multiple regions, orphaned nodes, or missing parent links in the database. Incorrect scope expansion would silently under- or over-report activities, which could invalidate a Bufdir submission.

Mitigation & Contingency

Mitigation: Obtain a full hierarchy fixture export from NHF before implementation begins. Write exhaustive unit tests covering boundary cases: single chapter, full national roll-up, chapters with no activities, and chapters assigned to multiple regions. Validate resolver output against a known-good manual count.

Contingency: If hierarchy data quality is too poor for automated resolution at launch, implement a manual scope override in the coordinator UI that allows the coordinator to explicitly select org units from a tree picker, bypassing the resolver.

medium impact high prob dependency

The activity_type_configuration table may not cover all activity types currently in use, leaving a subset unmapped at launch. Bufdir submissions with unmapped categories will be incomplete and may be rejected by Bufdir.

Mitigation & Contingency

Mitigation: Run a query against production activity data before implementation to enumerate all distinct activity type IDs. Cross-reference with Bufdir's published category schema (request from Norse Digital Products). Flag every gap as a known issue and build the warning surface into the preview panel.

Contingency: Implement a fallback 'Other' category bucket for unmapped types and surface a prominent warning in the export preview requiring coordinator acknowledgement before proceeding. Log unmapped types for post-launch cleanup.

high impact low prob security

Supabase RLS policies on generated_reports and the storage bucket must enforce strict org isolation. A misconfigured policy could allow a coordinator from one organisation to read another organisation's export files, creating a serious data breach with GDPR implications.

Mitigation & Contingency

Mitigation: Write RLS integration tests that attempt cross-org reads with explicitly different JWT tokens and assert that all attempts return empty sets or 403 errors. Include RLS policy review in the pull request checklist. Use Supabase's built-in policy tester during development.

Contingency: If a policy gap is discovered post-deployment, immediately revoke all signed URLs for affected exports, audit the access log for unauthorised reads, and issue a coordinated disclosure to affected organisations per GDPR breach notification requirements.