critical priority medium complexity backend pending backend specialist Tier 1

Acceptance Criteria

PlainLanguageContentService.getErrorMessage(String errorCode) returns a non-null, non-empty String for any input including unknown codes
PlainLanguageContentService.getHelpText(String contextKey) returns a non-null String; returns a safe generic fallback for unknown keys
PlainLanguageContentService.getActionLabel(String actionKey) returns a non-null String; returns the actionKey humanised as fallback
All strings returned by the service are ≤80 words (enforced by a runtime assertion in debug mode)
Org-specific label overrides from the labels provider take precedence over registry defaults
ErrorMessageRegistry contains entries for all Supabase auth error codes (invalid_credentials, email_not_confirmed, user_not_found, etc.)
HelpContentRegistry contains entries for all wizard context keys defined in the activity registration flow
Service is injectable / mockable — no static singleton call sites in widget code
Fallback chain: org override → registry default → hardcoded safe default
Service initialisation is synchronous after the labels provider is resolved
Unit tests cover the full fallback chain for each of the 3 public methods
No strings returned by the service contain technical jargon, error codes, or stack trace fragments

Technical Requirements

frameworks
Flutter
Dart
Riverpod
data models
ErrorMessageRegistry
HelpContentRegistry
OrgLabelsProvider
PlainLanguageContentService
performance requirements
All 3 public methods are synchronous and return in < 0.5ms
Registry lookup uses Map for O(1) access — no linear search
security requirements
Error messages must never include raw exception messages, stack traces, or internal system identifiers
Auth-related error messages must not reveal whether an email address exists in the system (generic 'invalid credentials' phrasing)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Use Riverpod Provider or simple constructor injection for OrgLabelsProvider — avoid global singletons to keep the service testable. Define ErrorMessageRegistry and HelpContentRegistry as Map constants in separate files (error_message_registry.dart, help_content_registry.dart) — this makes content reviews and updates straightforward. Implement a private _wordCount(String s) helper that splits on whitespace and checks length ≤ 80. The humanised fallback for getActionLabel should convert snake_case/camelCase to Title Case (e.g., 'save_activity' → 'Save Activity') rather than returning the raw key.

For Supabase auth errors, map the Supabase error code strings directly (they are stable across SDK versions). Consider defining error code constants in a separate ErrorCodes class to avoid magic strings at call sites.

Testing Requirements

Unit tests with flutter_test/dart:test. Test each public method with: a known key (expect registry value), an org-override key (expect override value), an unknown key (expect safe default). Test word count enforcement — create a registry entry with 81 words and assert debug assertion fires. Test that org overrides correctly shadow registry values.

Test service initialisation with an empty labels provider (all keys fall through to registry). Mock OrgLabelsProvider via Riverpod override or constructor injection. No widget tests required. Aim for 100% coverage of service class methods.

Component
Plain Language Content Service
service medium
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.