high priority low complexity backend pending backend specialist Tier 2

Acceptance Criteria

ReportFieldValidator exposes a static or top-level pure function `validate(FieldConfig config, dynamic value) → ValidationResult` with no instance state
ValidationResult contains: `bool isValid`, `String? errorMessage`, and `String fieldId` so callers can correlate results to fields without extra bookkeeping
Required rule: returns invalid with localised error when value is null, empty string, or empty list; passes for all non-empty values
minLength / maxLength rules: applied only to String values; boundary values (value.length == minLength and == maxLength) return valid
Regex pattern rule: compiles pattern once per call; an invalid regex string itself returns a descriptive error rather than throwing
Numeric range (min / max): works for both int and double; boundary values are inclusive; non-numeric strings return a type-mismatch error
Select options rule: checks value exists in the allowed list using case-insensitive equality; returns the full allowed list in the error message for discoverability
Date range constraint: accepts DateTime values or ISO-8601 strings; validates start ≤ value ≤ end; null start or end means open-ended
Multiple rules on a single FieldConfig are evaluated in order; the first failing rule short-circuits and its message is returned
validateAll(List<FieldConfig> configs, Map<String, dynamic> values) → Map<String, ValidationResult> runs all field validations and returns the complete map; callers can use Future.wait on parallel isolate tasks without race conditions
All error messages are non-null, human-readable English strings suitable for display in the UI without further formatting
No Flutter or Supabase imports anywhere in this file — pure Dart only

Technical Requirements

frameworks
Dart (latest stable)
flutter_test (for unit tests in task-011)
data models
FieldConfig (from task-005: field_id, field_type, label, validation_rules map)
ValidationResult (new: isValid, errorMessage, fieldId)
performance requirements
validate() must complete in < 1 ms for any single field on a mid-range device — no async I/O, no DB calls
validateAll() over 50 fields must complete in < 10 ms synchronously
Regex patterns must NOT be compiled on every call if FieldConfig instances are reused; use a module-level cache keyed by pattern string
security requirements
Regex patterns sourced from org config (remote JSONB) must be validated for catastrophic backtracking (ReDoS) before compilation; reject patterns with nested quantifiers
No sensitive field values (e.g. national ID, health data) should appear in error messages — use generic placeholders like 'Value does not match required format'

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Model ValidationResult as a sealed class or a simple immutable data class using Dart's `final` fields — avoid using exceptions for validation failures (return types are cleaner and force callers to handle errors). Store the rule evaluation order in FieldConfig as a `List` rather than a Map so deterministic ordering is guaranteed. For the regex cache use a top-level `final Map _patternCache = {}` — this is safe because RegExp is immutable. For date parsing, prefer `DateTime.tryParse()` over `DateTime.parse()` to avoid exceptions on malformed input.

Export only the public surface (`validate`, `validateAll`, `FieldConfig`, `ValidationResult`) from a single `report_field_validator.dart` barrel file; keep rule implementations in private helpers. This makes the dependency boundary explicit and easy to mock in integration tests.

Testing Requirements

Covered by task-011. Internally: implement as a single Dart library file with no framework dependencies so it can be tested with `dart test` without spinning up Flutter. Ensure all 7 rule types have at least one passing and one failing test case, plus boundary and null-value edge cases. Parallel validation path (validateAll) must be tested with a 20-field fixture to confirm no shared mutable state.

Component
Report Field Validator
service low
Epic Risks (3)
high impact medium prob security

Supabase RLS policies for multi-org report access may be more complex than anticipated — coordinators need cross-peer-mentor access within their org but not across orgs, and draft reports should be invisible to coordinators until submitted. Misconfigured RLS could expose sensitive health data or block legitimate access.

Mitigation & Contingency

Mitigation: Define and test RLS policies in isolation before writing repository code. Create a dedicated SQL migration file with policy definitions and an automated integration test suite that verifies each role's access boundaries using real Supabase auth tokens.

Contingency: If RLS proves too complex to express declaratively, implement application-level access control in the repository layer with explicit org and role checks, and add a security audit task before the feature goes to production.

high impact medium prob integration

The org field config JSON stored in Supabase may lack a stable, versioned schema contract. If different organisations have drifted to different field-definition formats, org-field-config-loader will fail silently or crash, breaking form rendering for those orgs.

Mitigation & Contingency

Mitigation: Define a canonical JSON Schema for field config and validate all existing org configs against it before implementation begins. Store a schema version field in every config record and handle version migrations explicitly in the loader.

Contingency: If existing configs are too heterogeneous, implement a config normalisation pass in org-field-config-loader that coerces known variants to the canonical format, logging warnings for fields that cannot be normalised so operations can fix them in the admin console.

medium impact low prob technical

TTL-based schema cache invalidation may cause peer mentors to use stale field definitions for up to the TTL window after an admin updates the org config, potentially collecting data against outdated field structures.

Mitigation & Contingency

Mitigation: Set a conservative TTL (e.g. 15 minutes) and expose a manual cache-bust mechanism triggered on app foreground-resume. Document the maximum staleness window in the admin console so org admins know to plan config changes outside active reporting windows.

Contingency: If stale schema causes a data quality incident, add a Supabase Realtime subscription to the org config table that invalidates the cache immediately on any config update.