high priority medium complexity testing pending testing specialist Tier 4

Acceptance Criteria

CognitiveLoadRuleEngine unit tests cover: validateStepCount(4) returns no violation, validateStepCount(5) returns no violation (boundary), validateStepCount(6) returns a CognitiveLoadViolation with ruleId 'max-steps-exceeded', all other constraint rules (character limits, option counts, nesting depth) have at least one passing and one failing test case
CognitiveLoadRuleEngine boundary tests use parameterized test cases (using test() within a loop or a data-driven helper) to cover: n-1, n, and n+1 for every numeric constraint boundary defined in the engine
CognitiveLoadRuleEngine returns typed CognitiveLoadViolation objects (not raw strings) with ruleId, severity (warning/error), and a machine-readable constraint name — tests assert on each field independently
PlainLanguageContentService unit tests cover: resolveError(knownCode) returns correct message, resolveError(unknownCode) returns generic fallback, resolveFieldLabel(key) with and without org override, applyOrgTerminology(text, orgId) replaces all registered terminology tokens in a single pass
PlainLanguageContentService fallback chain test: org-specific override → base content → hardcoded fallback — each level is tested independently by simulating missing data at higher priority levels
AccessibilityDesignTokenEnforcer unit tests cover: validateColor(compliantToken) returns no violations, validateColor(nonCompliantToken) returns a contrastViolation, validateTouchTarget(44, 44) passes, validateTouchTarget(40, 44) returns a touchTargetViolation, all violation objects include the token name and the actual vs. required value
WizardStateManager BLoC golden tests (using bloc_test) cover all state transitions: Initial→StepActive(1), StepActive(n)→StepActive(n+1), StepActive(n)→StepActive(n-1), StepActive(5)→Error (step limit), StepActive→Completed, StepActive→Abandoned
WizardStateManager auto-save trigger test: verifies that autoSave() on the mock WizardDraftRepository is called exactly once per nextStep() and previousStep() invocation and zero times during jumpToStep() to an already-saved step (if that is the defined contract)
Overall line coverage ≥90% across all three service files as measured by flutter test --coverage
All tests are hermetic — no network calls, no file system access, no shared mutable state between tests

Technical Requirements

frameworks
Flutter
flutter_test
bloc_test
mocktail
apis
CognitiveLoadRuleEngine public interface
PlainLanguageContentService public interface
AccessibilityDesignTokenEnforcer public interface
WizardStateManager (Cubit/BLoC)
data models
CognitiveLoadViolation
PlainLanguageContent
OrgTerminologyOverride
AccessibilityViolation
WizardState (all subtypes)
WizardProgressSnapshot
performance requirements
Full unit test suite for all three service files must complete in under 20 seconds
BLoC golden tests must complete in under 5 seconds — do not use real async delays; use fake async
security requirements
Tests must not log sensitive draft content (personal data) to console — use opaque identifiers in test fixtures
Do not use real user tokens or org IDs in service layer tests — use hardcoded test fixtures that do not match production patterns

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Organize CognitiveLoadRuleEngine tests around each rule, not each method — this makes it obvious which rule is broken when a test fails. Use a parameterized helper like void testBoundary(String ruleName, int passing, int failing, ...) to avoid duplicating boundary test boilerplate across all constraints. For AccessibilityDesignTokenEnforcer, define test fixtures as constants in a separate test_fixtures.dart file so token values are not scattered across test methods. The WizardStateManager golden tests should use blocTest() with verify: to assert the mock WizardDraftRepository was called the expected number of times — this is the critical correctness check for the auto-save feature.

Avoid testing private methods directly — if a private method needs independent testing, it is a sign it should be extracted into a collaborator class.

Testing Requirements

Structure tests in four files: cognitive_load_rule_engine_test.dart, plain_language_content_service_test.dart, accessibility_design_token_enforcer_test.dart, wizard_state_manager_bloc_test.dart. Use bloc_test's blocTest() helper for all WizardStateManager state transition tests — do not manually call emit() or listen to streams in tests. Use fakeAsync() from flutter_test for any time-sensitive behaviour in WizardStateManager (e.g., debounced auto-save). For PlainLanguageContentService org override chain tests, inject a mock HelpContentRegistry and mock ErrorMessageRegistry so each override level can be toggled independently.

Document the constraint boundary values being tested with inline comments so future maintainers understand the magic numbers.

Component
Cognitive Load Rule Engine
service medium
Dependencies (3)
Build the CognitiveLoadRuleEngine service class that enforces cognitive accessibility constraints at runtime. Implement rules: ≤5 wizard steps per flow, ≤3 primary choices per screen, maximum one primary CTA per screen, and no nested modals. Expose a validateScreen() method returning a list of violations with plain-language descriptions. Integrate as a development-mode assertion layer that logs warnings and blocks navigation on violations. epic-cognitive-accessibility-foundation-task-006 Build the PlainLanguageContentService that serves plain-language text for all user-facing strings. Implement getErrorMessage(errorCode), getHelpText(contextKey), and getActionLabel(actionKey) methods. The service reads from the ErrorMessageRegistry and HelpContentRegistry, applies org-specific terminology overrides from the labels provider, and falls back to safe defaults. All returned strings must be ≤80 words per NHF cognitive accessibility guidelines. epic-cognitive-accessibility-foundation-task-007 Implement the WizardStateManager service using BLoC/Cubit pattern to manage multi-step wizard state across the ≤5 step flow. Integrates with WizardDraftRepository for auto-save on every step transition. Exposes methods for nextStep(), previousStep(), jumpToStep(), and abandonDraft(). Emits typed state events for progress indicator synchronization. Enforces the step limit constraint via CognitiveLoadRuleEngine before allowing step additions. epic-cognitive-accessibility-foundation-task-012
Epic Risks (4)
high impact medium prob technical

The error message registry and help content registry both depend on bundled JSON assets loaded at startup. If asset loading fails silently (e.g. malformed JSON, missing pubspec asset declaration), the entire plain-language layer falls back to empty strings or raw error codes, breaking the accessibility guarantee app-wide.

Mitigation & Contingency

Mitigation: Implement eager validation of both assets during app initialisation with an assertion failure in debug mode and a structured error log in release mode. Add integration tests that verify asset loading in the Flutter test harness on every CI run.

Contingency: Ship a hardcoded minimum-viable fallback message set directly in Dart code so the app always has at least a safe generic message, preventing a blank or code-only error surface.

medium impact medium prob dependency

The AccessibilityDesignTokenEnforcer relies on dart_code_metrics custom lint rules. If the lint toolchain is not already configured in the project's CI pipeline, integrating a new linting plugin may cause unexpected build failures or require significant CI configuration work beyond the estimated scope.

Mitigation & Contingency

Mitigation: Audit the existing dart_code_metrics configuration in the project before starting implementation. Scope the lint rules to a separate Dart package that can be integrated incrementally, starting with the most critical rule (hard-coded colors) and adding others in subsequent iterations.

Contingency: Fall back to Flutter test-level assertions (using the cognitive-accessibility-audit utility) to catch violations in CI if the lint plugin integration is delayed, preserving enforcement coverage without blocking the epic.

medium impact low prob technical

WizardDraftRepository must choose between shared_preferences and Hive for local persistence. Choosing the wrong store for the data volume (e.g. shared_preferences for complex nested wizard state) can lead to serialisation bugs or performance degradation, particularly on lower-end Android devices used by some NHF members.

Mitigation & Contingency

Mitigation: Define a clean repository interface first and implement shared_preferences as the initial backend. Profile serialisation round-trip time with a realistic wizard state payload (≈10 fields) before committing to either store.

Contingency: Swap the persistence backend behind the repository interface without touching wizard UI code, which is possible precisely because the repository abstraction isolates the storage detail.

medium impact high prob scope

The AccessibilityDesignTokenEnforcer scope could expand significantly if a large portion of existing widgets use hard-coded values. Discovering widespread violations during this epic would force either a major refactor or a decision to exclude legacy components, potentially reducing the enforcer's coverage and value.

Mitigation & Contingency

Mitigation: Run a preliminary audit of existing widgets using a simple grep for hard-coded hex colors and raw pixel values before implementation begins. Use the results to set a realistic remediation boundary for this epic and log all out-of-scope violations as tracked tech-debt items.

Contingency: Scope the enforcer to new and modified components only (via file-path filters in dart_code_metrics config), shipping a partial but immediately valuable coverage rather than blocking the epic on full-codebase remediation.