critical priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

validate() accepts a non-null BufdirReportModel and an optional nullable prior-period BufdirReportModel
All three evaluators (completeness, threshold, anomaly) are executed against every section in the report
When prior-period model is null, the anomaly evaluator is invoked with null prior sections — it must not throw
The returned ValidationResult contains all issues from all evaluators across all sections
Issues in ValidationResult are sorted: errors first, then warnings, then info (stable sort — preserves field order within same severity)
An empty report with no issues returns a ValidationResult with an empty issues list and isValid == true
A report with at least one error returns isValid == false
A report with only warnings returns isValid == true (warnings do not block submission)
The service is constructed with all three evaluators and their configs injected — no hard-coded dependencies
The service class contains no Flutter SDK imports and no async operations
Section iteration preserves the order defined by BufdirReportModel.sections

Technical Requirements

frameworks
Dart
data models
BufdirReportModel
BufdirReportSectionModel
ValidationResult
ValidationIssue
performance requirements
Full validation of a report with 10 sections and 20 fields each completes in under 10ms
No unnecessary list copies — aggregate into a single growing list before sorting
security requirements
The service must not retain references to report data after validate() returns
Evaluator exceptions must be caught per-section and added as a system-level ValidationIssue rather than crashing the call

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Inject evaluators via constructor to enable testing with fakes: BufdirFieldValidationService({ required CompletenessRuleEvaluator completenessEvaluator, required ThresholdComplianceRuleEvaluator thresholdEvaluator, required AnomalyDetectionRuleEvaluator anomalyEvaluator }). The validate() method should iterate report.sections, call each evaluator per section (passing prior section looked up by sectionId from the prior-period model), collect results into a flat list, then sort by severity ordinal. Define severity ordinals in the ValidationIssueSeverity enum: error = 0, warning = 1, info = 2. Use List.sort() with a comparator on severity ordinal.

Wrap each evaluator call in a try/catch and emit a synthetic ValidationIssue(severity: error, ruleClass: 'system', message: 'Validation error in section $sectionId: $e') on failure — this ensures one bad section never silently swallows other sections.

Testing Requirements

Unit tests using flutter_test. Test class: BufdirFieldValidationServiceTest. Required scenarios: (1) all fields valid, no prior → ValidationResult with empty issues and isValid true, (2) one completeness error → isValid false, error appears first in sorted result, (3) one threshold warning only → isValid true, (4) one anomaly warning only → isValid true, (5) mixed errors and warnings → errors sorted before warnings, (6) null prior-period model → anomaly evaluator runs without crash, (7) multiple sections with multiple issues → all issues aggregated, (8) empty BufdirReportModel (no sections) → empty ValidationResult, isValid true. Use test doubles (fake implementations) for each evaluator rather than real evaluators — this isolates the orchestration logic.

Minimum 90% line coverage.

Component
Bufdir Field Validation Service
service medium
Dependencies (3)
Implement the anomaly detection rule class. For each numeric field, compare the current period value to the prior-period equivalent (passed in as context). If the deviation exceeds a configurable percentage threshold (default 50%), emit a ValidationIssue with severity 'warning', ruleClass 'anomaly', and a message such as 'Activity count dropped from 42 to 0, a 100% decrease from the previous period.' Handle null prior-period values gracefully (skip anomaly check, do not emit false positives). epic-bufdir-report-preview-core-logic-task-004 Implement the completeness rule class within BufdirFieldValidationService. For each required field in a BufdirReportSectionModel, check that the value is non-null and non-empty. Return a ValidationIssue with severity 'error', ruleClass 'completeness', the field key, and an English message such as 'Field X is required and must not be empty.' Ensure field keys map to human-readable Bufdir field names via a configurable lookup map. epic-bufdir-report-preview-core-logic-task-002 Implement the threshold compliance rule class. For numeric fields that have a Bufdir-defined minimum value (configured via an injected BufdirThresholdConfig map), compare the field value against the minimum. Return a ValidationIssue with severity 'warning', ruleClass 'threshold', field key, and a message such as 'Activity count (3) is below the required minimum of 5 for category X.' Config must be injectable to support per-organization overrides. epic-bufdir-report-preview-core-logic-task-003
Epic Risks (2)
medium impact medium prob scope

The exact minimum threshold values required by Bufdir guidelines (e.g., minimum participant counts per section) may not be formally documented in machine-readable form. If thresholds must be researched or negotiated during implementation, the validation service will be delayed and may launch with incomplete rules, reducing its effectiveness.

Mitigation & Contingency

Mitigation: Compile threshold rules from the Bufdir reporting guidelines PDF before sprint start. Store rules in a separate configuration file (not hardcoded in the service class) so they can be updated without a service rewrite. Treat unknown thresholds as 'no minimum' to avoid false errors.

Contingency: Launch with completeness and anomaly validation only, shipping threshold compliance rules as a follow-on config update once rules are confirmed with Bufdir. Flag this as a known limitation in the coordinator help text.

high impact low prob technical

BufdirPreviewService coordinates three async operations (fetch aggregated data, map structure, run validation). Race conditions or partial failures in this chain could produce an inconsistent preview model — e.g., a model with field values but no validation results — which would silently mislead coordinators into thinking the report is clean.

Mitigation & Contingency

Mitigation: Model the orchestration as a single BLoC/Cubit state machine with explicit states (Loading, Loaded, Error) and ensure validation is always run atomically after mapping, never in parallel. Write integration tests that simulate network failure at each step of the chain.

Contingency: If a partial failure state reaches production, detect it via the missing validation summary field in the preview model and show a full-screen error state rather than an incomplete preview, prompting the coordinator to retry.