critical priority medium complexity backend pending backend specialist Tier 1

Acceptance Criteria

IntegrationConfigValidator.validate(config) returns a ValidationResult containing a list of ValidationError objects (never throws on validation failure)
ValidationResult.isValid is true only when errors list is empty
Each ValidationError contains: field (dot-notation path), code (enum), message (human-readable English string)
Validation checks all requiredCredentialFields from IntegrationTypeRegistry — missing or blank fields each produce a distinct ValidationError with code REQUIRED_FIELD_MISSING
Validation rejects unknown integration type ids with code UNKNOWN_INTEGRATION_TYPE
Sync schedule cron expression (if provided) is validated for correct cron syntax — invalid expressions produce code INVALID_CRON_EXPRESSION
Cross-field constraint: if sync_enabled is true, sync_schedule must also be provided; violation produces code DEPENDENCY_CONSTRAINT_VIOLATED
Field mapping completeness: if field_mapping is present, each mapping entry must have non-empty source and target — empty mappings produce code INCOMPLETE_FIELD_MAPPING
Validator does not perform network calls or database queries — purely in-memory logic
Unit tests cover: valid config passes, each individual required field missing, invalid cron, sync enabled without schedule, empty field mapping entry

Technical Requirements

frameworks
Flutter
Dart
Riverpod (for DI/provider exposure)
data models
IntegrationConfig
ValidationResult
ValidationError
ValidationErrorCode (enum)
IntegrationTypeEntry (from Registry)
performance requirements
Validation of a single config must complete in under 5ms — no async operations
Validator must be stateless and safely callable from multiple isolates
security requirements
Validator must not log credential field values — only field names in error messages
Error messages must not expose internal system implementation details

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Integration Task

Handles integration between different epics or system components. Requires coordination across multiple development streams.

Implementation Notes

Keep the validator purely functional — validate(IntegrationConfig config) → ValidationResult. Do not inject Supabase or any async dependency. Use a list of private validator functions that each accept the config and return List, then flatten and return as ValidationResult. This makes adding new rules trivial.

For cron validation, implement a minimal cron parser that checks for 5 or 6 fields and that each field matches its allowed range/pattern — do not pull in a heavy dependency for this. Expose the validator as a Riverpod Provider so BLoC/Cubit classes can inject it without manual instantiation. The validation errors should be designed for direct display in the UI — use clear, user-facing English messages like 'API key is required for Xledger integration' rather than technical codes alone.

Testing Requirements

Unit tests using flutter_test. Organize tests by validation rule: one describe block per rule. Required tests: (1) fully valid Xledger config passes, (2) fully valid Dynamics config passes, (3) each required credential field missing individually for each integration type, (4) unknown integration type, (5) invalid cron expression (multiple malformed strings), (6) sync_enabled=true with missing schedule, (7) field mapping with empty source, (8) field mapping with empty target, (9) field mapping is null/absent and not flagged as error. All tests must be synchronous (no async).

Aim for 100% branch coverage on the validate() method.

Component
Integration Config Validator
infrastructure medium
Epic Risks (3)
medium impact high prob technical

Each of the five external systems (Xledger, Dynamics, Cornerstone, Consio, Bufdir) has a different authentication flow, field schema, and error format. Forcing them into a uniform adapter interface may require compromises that result in leaky abstractions or make the adapter contract too complex to maintain.

Mitigation & Contingency

Mitigation: Design the IntegrationAdapter interface with a loose invoke() payload rather than a typed one, allowing each adapter to declare its own input/output schema. Use integration type metadata in the registry to document per-adapter quirks. Build Xledger first as the most documented API, then adapt the interface based on learnings.

Contingency: If the uniform interface cannot accommodate all five systems, split into two interface tiers: a simple polling/export adapter and a richer bidirectional adapter, with the registry declaring which tier each system implements.

medium impact high prob dependency

Development and testing of the Cornerstone and Consio adapters depends on NHF providing sandbox API access. If credentials or documentation are delayed, these adapters cannot be validated, blocking the epic's acceptance criteria.

Mitigation & Contingency

Mitigation: Implement Xledger and Dynamics adapters first (better-documented, sandbox available). Create a mock adapter for Cornerstone/Consio using recorded API responses for CI testing. Proactively request sandbox access from NHF at project kickoff.

Contingency: Ship the epic with Cornerstone/Consio adapters in a 'stub' state (connectivity test returns a simulated success, invoke() is not production-wired) and gate the NHF integration behind a feature flag until real API access is obtained.

medium impact medium prob scope

Real-world field mappings may include nested transformations, conditional logic, and data type coercions (e.g., Norwegian date formats, currency rounding rules) that the Field Mapping Resolver's initial design does not accommodate, requiring scope expansion mid-epic.

Mitigation & Contingency

Mitigation: Gather actual field mapping examples from Blindeforbundet (Xledger) and HLF (Dynamics) before designing the resolver. Identify the most complex transformation required and ensure the resolver design handles it. Limit Phase 1 to direct field renaming and format conversion only.

Contingency: If complex transformations are required, implement a simple expression evaluator (e.g., JSONata or a custom mini-DSL) as an extension point in the resolver, delivering basic mappings first and complex ones in a follow-up task.