high priority low complexity infrastructure pending backend specialist Tier 1

Acceptance Criteria

validateEmail(String? email) returns a ValidationFailure with key 'email' for inputs that do not match a standard email regex (RFC 5322 simplified: local@domain.tld); empty/null email passes because email is optional
validatePhone(String? phone) accepts Norwegian phone numbers: exactly 8 digits with optional '+47 ' or '0047 ' prefix; spaces and hyphens between digit groups are stripped before validation; invalid format returns ValidationFailure with key 'phone'
validateChapterCount(int count, {required bool nhfRuleEnabled}) returns ValidationFailure with key 'chapterCount' and localisation key 'validation.chapterCount.maxExceeded' when nhfRuleEnabled is true and count > 5
When nhfRuleEnabled is false, validateChapterCount always returns ValidationSuccess regardless of count
nhfRuleEnabled flag is sourced from a ContactValidatorConfig object injected into the validator at construction time — not a global singleton or hardcoded boolean
ContactValidatorConfig is populated from the organisation's feature flags stored in Supabase (read by the caller; the validator itself has no Supabase dependency)
All three new rules are also exposed as Flutter FormFieldValidator<String> factory methods for direct use in TextFormField widgets
The existing task-005 required field rules are not modified or broken by this extension
Norwegian phone stripping logic is extracted into a private _normalisePhone(String) helper and is independently tested

Technical Requirements

frameworks
Flutter
Dart
Riverpod
apis
Supabase (for feature flag read — performed by caller, not the validator)
data models
ContactValidatorConfig
ContactFormData (extended with chapterCount)
ValidationResult
performance requirements
Email regex must be pre-compiled as a static final RegExp to avoid recompilation on every keystroke
Phone normalisation and validation must complete synchronously in under 0.5ms
security requirements
Email regex must not be catastrophically backtracking (ReDoS) — use a simple linear regex, not a complex nested quantifier pattern
Feature flag state must be read from a trusted server-side source (Supabase); the client must not allow users to override the nhfRuleEnabled flag through URL parameters or local storage
ui components
TextFormField (email field with email keyboard type)
TextFormField (phone field with phone keyboard type)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Define email regex as `static final RegExp _emailRegex = RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$');` — simple and ReDoS-safe. For phone: strip all spaces and hyphens, strip '+47' or '0047' prefix, then assert exactly 8 remaining digits using `RegExp(r'^\d{8}$')`. Inject ContactValidatorConfig via the constructor: `const ContactFormValidator({required this.config})` — this keeps the validator testable without Supabase. Populate ContactValidatorConfig from a Riverpod provider that reads the org feature flags (already fetched as part of app initialisation).

Document that nhfRuleEnabled maps to an organisation-level flag named e.g. `max_chapter_affiliations_rule_enabled` in the feature flags table.

Testing Requirements

Pure Dart unit tests: (1) valid email passes; (2) email without domain fails; (3) email without '@' fails; (4) null/empty email passes (optional field); (5) '91234567' passes phone validation; (6) '+47 91234567' passes; (7) '0047 91 23 45 67' passes (after stripping spaces); (8) '1234567' (7 digits) fails; (9) '123456789' (9 digits) fails; (10) chapter count 5 passes when nhfRuleEnabled=true; (11) chapter count 6 fails when nhfRuleEnabled=true; (12) chapter count 100 passes when nhfRuleEnabled=false. Test the regex static constant is pre-compiled (assert it is a static field). Aim for 100% branch coverage.

Component
Contact Form Validator
infrastructure low
Epic Risks (3)
high impact medium prob security

Blindeforbundet's encryption key retrieval mechanism may not be finalised at implementation time, or session key availability via Supabase RLS may be inconsistent, causing decryption failures that expose masked placeholders to users and degrade the experience.

Mitigation & Contingency

Mitigation: Agree with Blindeforbundet on key storage and retrieval contract before implementation starts. Prototype key retrieval in a spike against the staging Supabase instance and validate the full decrypt/verify cycle with real test data before committing to the implementation.

Contingency: Implement a fallback that shows a 'field temporarily unavailable' state with a retry affordance. Log decryption failures server-side for audit. Escalate to Blindeforbundet stakeholders to unblock key management before the service tier epic begins.

medium impact medium prob technical

NHF contacts may belong to up to 5 chapters, each governed by separate RLS policies. A coordinator's chapter scope may not cover all affiliations, causing partial profile reads or silent data omissions that are difficult to detect in tests.

Mitigation & Contingency

Mitigation: Map all RLS policy combinations for multi-chapter contacts early. Write integration tests that create contacts with 5 affiliations and query them from coordinators with varying chapter scopes. Use Supabase's RLS test utilities to verify row visibility per role.

Contingency: Add an explicit 'affiliation partially visible' state in the repository response model so the UI can communicate scope limitations to the coordinator rather than silently showing incomplete data.

low impact medium prob scope

Organisation-specific validation rules (e.g., NHF chapter limit, Blindeforbundet encrypted field edit flow) may expand in scope during implementation as edge cases are discovered, causing the validator to grow beyond the planned complexity.

Mitigation & Contingency

Mitigation: Define the complete validation rule set with product and org stakeholders before coding begins. Document each rule with its source organisation and acceptance test. Use a rule registry pattern so new rules can be added without modifying core validator logic.

Contingency: Timebox validator enhancements to 2 hours per additional rule. Defer non-blocking rules to a follow-on maintenance task rather than blocking the epic delivery.