high priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

BufdirColumnMapper.mapRows return type extended to MapRowsResult containing: validRows (List<MappedRow>), quarantinedRows (List<QuarantinedRow>), and summary (RowValidationSummary)
QuarantinedRow contains: originalRowIndex (int), sourceActivityId (String), validationErrors (List<ColumnValidationError>), and mappedPartial (MappedRow?)
ColumnValidationError contains: columnName (String), errorCode (ValidationErrorCode enum), and message (String)
ValidationErrorCode enum covers: requiredColumnMissing, typeMismatch, valueOutOfRange, emptyRequiredString
Required column check: any MappedRow missing a column where ColumnDefinition.isRequired == true is quarantined
Type validation: mapped value type must match ColumnDefinition.allowedTypes — integer columns reject non-integer strings
Value range validation: date columns must parse as valid ISO 8601 dates; integer columns within declared min/max if specified in ColumnDefinition
RowValidationSummary includes: totalRows (int), validCount (int), quarantinedCount (int), and errorBreakdown (Map<ValidationErrorCode, int>)
Quarantined rows are excluded from validRows but included in quarantinedRows — they are never silently dropped
A batch where all rows are quarantined still returns a MapRowsResult (not an exception) — calling code decides whether to abort
Validation logic is a separate private method _validateMappedRow that can be unit tested in isolation

Technical Requirements

frameworks
Dart
data models
MappedRow
QuarantinedRow
ColumnValidationError
ValidationErrorCode
RowValidationSummary
MapRowsResult
ColumnDefinition
performance requirements
Validation pass must not add more than 20% overhead to the total mapping time for 500-row batches
ErrorBreakdown map must be built in O(n) — single pass over quarantined rows
security requirements
ColumnValidationError.message must not embed raw field values — reference column name and error code only to avoid PII leakage in logs

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Extend MapRowsResult as the new return type of mapRows — this is a breaking change to the interface defined in task-001, so update BufdirColumnMapperService accordingly. Validation should run as a second pass after mapping, not interleaved — this keeps mapping logic and validation logic independently testable. Implement _validateMappedRow(MappedRow row, ColumnMappingConfig config) returning List. Type checking strategy: use a _checkType(dynamic value, List allowedTypes) helper that tries parsing (int.tryParse, double.tryParse, DateTime.tryParse) rather than runtimeType checks — this is more robust for string-encoded data from Supabase.

Date validation: require ISO 8601 format (YYYY-MM-DD) and reject partial dates. Quarantined row should include the partial MappedRow (fields that did pass) for diagnostic purposes in the UI.

Testing Requirements

Unit tests for each ValidationErrorCode scenario: missing required column, type mismatch for string/integer/date/boolean, out-of-range date, empty required string. Test batch with zero quarantined rows returns empty quarantinedRows. Test batch where all rows fail returns validRows empty and quarantinedRows full. Test RowValidationSummary counts match actual list lengths.

Test errorBreakdown counts are accurate for mixed-error batches. Test that quarantined rows preserve sourceActivityId for traceability. Integration test: run full NHF and HLF fixture data sets through mapper + validator end-to-end and assert no unexpected quarantines. Target 95%+ branch coverage on validation logic.

Component
Bufdir Column Mapper
service high
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.