high priority medium complexity frontend pending frontend specialist Tier 4

Acceptance Criteria

ConfirmBeforeSubmitScreen extends or composes SingleActionScreenLayout — it does not implement its own scaffold or app bar
The screen reads all wizard answers from WizardStateManager without modifying state — it is a read-only view of the current draft
WizardProgressIndicator is visible at the top of the screen showing the final step position (e.g., step 5 of 5) in the same style as all other wizard steps
Each wizard field answer is rendered as a labelled row: left-aligned plain-language field label (from PlainLanguageContentService) and right-aligned or below answer value
Empty or null field values are displayed as '—' (em dash) with a screen-reader-accessible label of '[field name] not provided' — never shown as blank space
The primary CTA button has label 'Submit' and is rendered using the design-token primary button style; it is the only primary action on the screen
The secondary action 'Go back' is rendered as a text button or outlined button below or above the primary CTA — it calls WizardStateManager.previousStep()
Tapping 'Submit' triggers form submission and shows an inline loading state on the button (CircularProgressIndicator inside the button) while awaiting the API response; the button is disabled during loading
On submission success the screen is replaced (not pushed) by the post-submission confirmation screen so the user cannot navigate back to the form
On submission failure PlainLanguageErrorDisplay is shown inline above the Submit button with the relevant errorCode; the button re-enables
The answer summary list is fully accessible: each label-value pair is wrapped in a Semantics widget so screen readers read them as a group (e.g., 'Date of activity: 14 March 2026')
Screen renders correctly with 1 to 5 wizard answers without layout overflow at any text scale factor from 1.0 to 2.0

Technical Requirements

frameworks
Flutter
apis
WizardStateManager.currentDraftState
PlainLanguageContentService.resolveFieldLabel(fieldKey)
WizardStateManager.previousStep()
SubmissionService.submitDraft(draftId)
WizardProgressIndicator (widget from task-008)
data models
WizardDraft
WizardStep
WizardAnswer (fieldKey, value)
WizardProgressSnapshot
performance requirements
Screen must render within 2 frames of navigation — no heavy computation on first build
Summary list must use ListView.builder if the answer count could exceed 10, preventing unnecessary widget instantiation
security requirements
Sensitive field values (e.g., personal identification numbers) must be masked by default with a reveal toggle; masking rule is driven by a per-field isSensitive flag in the WizardStep definition
The Submit action must include CSRF protection if the submission goes through a REST API (attach the Supabase auth JWT in the Authorization header)
ui components
ConfirmBeforeSubmitScreen (StatefulWidget)
SingleActionScreenLayout (base layout from task-010)
WizardProgressIndicator (from task-008)
WizardAnswerSummaryRow (label + value with Semantics)
PlainLanguageErrorDisplay (from task-013)
SubmitButton with loading state

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Keep ConfirmBeforeSubmitScreen stateful only for the submission loading boolean — all wizard data comes from WizardStateManager via a BlocBuilder or Riverpod watch. Do not duplicate draft data in local widget state. For the sensitive field masking, implement a SensitiveFieldToggle widget that wraps any WizardAnswerSummaryRow where isSensitive is true — it shows asterisks by default and reveals on tap with an eye icon. Ensure the navigation after successful submission uses Navigator.pushReplacement or GoRouter.replace so the confirmation screen is not on the back stack — users must not be able to back-navigate into a submitted form.

The SingleActionScreenLayout base should handle the keyboard avoidance padding so you do not need custom Scaffold configuration.

Testing Requirements

Write flutter_test widget tests covering: (1) all wizard answer fields are rendered with correct labels from PlainLanguageContentService, (2) null/empty answers display em dash with correct semantic label, (3) Submit button triggers submission and shows loading indicator, (4) submission failure shows PlainLanguageErrorDisplay with correct errorCode, (5) 'Go back' calls WizardStateManager.previousStep() exactly once, (6) WizardProgressIndicator shows final step number, (7) Submit button is disabled during loading, (8) successful submission navigates away and removes the screen from the stack. Use a mock WizardStateManager and mock SubmissionService. Include a golden test of the screen with 3 sample answers. Run accessibility checks via SemanticsController to confirm label-value pairs are grouped correctly.

Component
Wizard Progress Indicator
ui low
Dependencies (3)
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.