@@ -397,12 +393,12 @@ const mapDispatchToProps = (dispatch) => {
updateCurrentSection: (whichSection) => {
dispatch(setCurrentSection(whichSection));
dispatch(setFormComplete("true"));
- },
+ }
};
};
const mapStateToProps = (state) => ({
- count: state.count,
+ count: state.count
});
export default connect(mapStateToProps, mapDispatchToProps)(BuildCheckRow);
diff --git a/components/newappeal/buildrow.js b/components/newappeal/buildrow.js
index d4bf9846..cabca969 100644
--- a/components/newappeal/buildrow.js
+++ b/components/newappeal/buildrow.js
@@ -1,8 +1,25 @@
import _ from "lodash";
import useTranslation from "next-translate/useTranslation";
import { useRouter } from "next/router";
-import xpath from "xpath";
import BuildField from "./buildfield";
+import {
+ parseRowOuterHTML,
+ selectRowDataFieldContent,
+ selectRowDataFieldName,
+ selectRowDateEnd,
+ selectRowDateStart,
+ selectRowDocumentTypeCode,
+ selectRowFieldType,
+ selectRowHint,
+ selectRowLabelDescriptions,
+ selectRowMaxFieldLength,
+ selectRowMaxSubField,
+ selectRowParentField,
+ selectRowParentFieldShowOnValue,
+ selectRowRequiredDocumentLabel,
+ selectRowRequiredDocumentValue,
+ selectRowValidation
+} from "../../lib/newappeal/formDerivation";
export default function BuildRow(props) {
let { t } = useTranslation();
@@ -20,56 +37,39 @@ export default function BuildRow(props) {
uploadCount,
setFileCount,
completedUploadFilesCount,
- setCompletedUploadFilesCount,
+ setCompletedUploadFilesCount
} = props;
- const parser = new DOMParser();
-
return (
{Object.keys(rowXML).map((key, index) => {
- var doc = parser.parseFromString(
- rowXML[key].outerHTML,
- "text/xml"
- );
+ var doc = parseRowOuterHTML(rowXML[key].outerHTML);
if (_.isEmpty(rowXML[key].innerHTML)) {
("");
} else {
- var rows = xpath.select("//label/@description", doc);
- var fieldtype = xpath.select("//@classid", doc);
- var validation = xpath.select("//@validation", doc);
- var maxFieldLength = xpath.select("//@maxFieldLength", doc);
- var maxSubField = xpath.select("//@maxsubfield", doc);
- var datafieldname = xpath.select("//@datafieldname", doc);
- var datafieldcontent = xpath.select(
- "//@datafieldcontent",
- doc
- );
- var parentField = xpath.select("//@parentField", doc);
+ var rows = selectRowLabelDescriptions(doc);
+ var fieldtype = selectRowFieldType(doc);
+ var validation = selectRowValidation(doc);
+ var maxFieldLength = selectRowMaxFieldLength(doc);
+ var maxSubField = selectRowMaxSubField(doc);
+ var datafieldname = selectRowDataFieldName(doc);
+ var datafieldcontent = selectRowDataFieldContent(doc);
+ var parentField = selectRowParentField(doc);
- var parentFieldShowOnValue = xpath.select(
- "//@parentFieldShowOnValue",
- doc
- );
+ var parentFieldShowOnValue =
+ selectRowParentFieldShowOnValue(doc);
- var requiredDocumentValue = xpath.select(
- "//@requiredDocumentValue",
- doc
- );
+ var requiredDocumentValue =
+ selectRowRequiredDocumentValue(doc);
- var requiredDocumentLabel = xpath.select(
- "//@requiredDocumentLabel",
- doc
- );
+ var requiredDocumentLabel =
+ selectRowRequiredDocumentLabel(doc);
- var documentTypeCode = xpath.select(
- "//@ishareDocumentCode",
- doc
- );
+ var documentTypeCode = selectRowDocumentTypeCode(doc);
- var hint = xpath.select("//label/@hint", doc);
- var dateStart = xpath.select("//@dateStart", doc);
- var dateEnd = xpath.select("//@dateEnd", doc);
+ var hint = selectRowHint(doc);
+ var dateStart = selectRowDateStart(doc);
+ var dateEnd = selectRowDateEnd(doc);
hint = !_.isEmpty(hint) && hint[0].value;
diff --git a/components/newappeal/buildsection.js b/components/newappeal/buildsection.js
index fd374474..c8d53cf7 100644
--- a/components/newappeal/buildsection.js
+++ b/components/newappeal/buildsection.js
@@ -4,7 +4,6 @@ import { useRouter } from "next/router";
import { useState } from "react";
import { connect } from "react-redux";
import { formValueSelector, reduxForm } from "redux-form";
-import xpath from "xpath";
import { uploadFiles } from "../../actions/services/documentService";
import { sendEmail } from "../../actions/services/notifyService";
import {
@@ -16,6 +15,14 @@ import {
} from "../../store/appealType/action";
import { getFormCollectionByID, getProgressObj, updateLinks } from "../utils";
import BuildRow from "./buildrow";
+import {
+ parseXml,
+ selectAllTabTitleDescriptions,
+ selectCurrentSectionRows,
+ selectCurrentSectionTitleDescriptions,
+ selectErrorFieldLabelDescription,
+ selectFormTabs
+} from "../../lib/newappeal/formDerivation";
import { FieldsTranslations } from "../elements";
import BuildProgress from "./buildprogress";
@@ -55,26 +62,13 @@ let BuildSection = (props) => {
const documentListObj = props.appealType.documentList;
const currentSection = props.appealType.currentSection;
- const parser = new DOMParser();
- var doc = parser.parseFromString(props.formXML, "text/xml");
- var titles = xpath.select(
- "//form/tabs/tab[" + currentSection + " ]/labels/label/@description",
- doc
- );
- var rowXML = xpath.select(
- "/form/tabs/tab[" +
- currentSection +
- "]/columns//sections/section/rows/row",
- //"/form/tabs/tab/columns//sections/section/rows/row",
- doc
- );
+ var doc = parseXml(props.formXML);
+ var titles = selectCurrentSectionTitleDescriptions(doc, currentSection);
+ var rowXML = selectCurrentSectionRows(doc, currentSection);
- var formObjXML = xpath.select("/form/tabs", doc);
+ var formObjXML = selectFormTabs(doc);
- var titleList = xpath.select(
- "//form/tabs/tab[*]/labels/label/@description",
- doc
- );
+ var titleList = selectAllTabTitleDescriptions(doc);
const progObj = getProgressObj(
formXML,
@@ -370,10 +364,10 @@ let BuildSection = (props) => {
errorsobj = _.keys(errorsobj);
const getErrorLabel = (whichField) => {
- var doc = parser.parseFromString(props.formXML, "text/xml");
- var errorFieldLabel = xpath.select(
- "//*[control/@id='" + whichField + "']//@description",
- doc
+ var doc = parseXml(props.formXML);
+ var errorFieldLabel = selectErrorFieldLabelDescription(
+ doc,
+ whichField
);
return errorFieldLabel[0].value;
diff --git a/context/current-state-scorecard.md b/context/current-state-scorecard.md
deleted file mode 100644
index 68c18ad6..00000000
--- a/context/current-state-scorecard.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Current State Scorecard (2026-03-25)
-
-Purpose: provide a single operational view of architecture/debt progress with evidence references.
-
-## RAG Legend
-
-- Green: materially addressed for current stream
-- Amber: partial progress, follow-on needed
-- Red: unresolved/high risk remains
-
-## Scorecard
-
-| Area | Status | Current position | Evidence |
-| --------------------------------- | ------ | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
-| API contract consistency | Green | Large endpoint clusters normalized on structured contracts with phase21 coverage. | `memory-bank/change-log.md` (endpoint hardening stream), `tests/phase21/endpoint-handler-contract.test.cjs` |
-| Actions/service decomposition | Amber | Shared clients and route builders introduced; monolith risk reduced but full domain split remains. | `actions/clients/*`, `actions/services/*`, `memory-bank/debt-list.md` |
-| Endpoint sprawl/proxy duplication | Amber | Shared relay forwarding and helper reuse reduced duplication; long-tail handlers still exist. | `pages/api/middleware/relayForwarding.js`, `context/architecture.md` |
-| High-risk automation | Green | Focused checks now cover auth redirect safety, signed-delete/upload negatives, and EN/CY rewrite parity. | `tests/phase22/auth-redirect-safety.test.cjs`, `tests/phase22/i18n-route-parity.test.cjs`, `tests/phase22/index.test.cjs` |
-| i18n parity assurance | Amber | Targeted parity checks in place; CI-level parity enforcement still pending. | `tests/phase22/i18n-route-parity.test.cjs`, `context/architecture.md` |
-| Logging redaction consistency | Amber | Relay-side structured redaction improved; broader auth/email/file logging hardening remains open. | `memory-bank/open-questions.md` (Q-001), `context/architecture.md` |
-| Runtime canonicalization | Red | `server.js` and `server/server.js` ambiguity not yet formally closed. | `memory-bank/architect-review.md`, `context/architecture.md` |
-
-## Next execution focus
-
-1. Sequence B: signed-request consolidation + logging hardening.
-2. Sequence C: CI-level i18n parity gates + endpoint long-tail reduction.
-3. Runtime canonicalization decision with explicit operational owner.
-
-## Update cadence
-
-- Update after every non-trivial architecture/debt slice.
-- Keep this file aligned with:
- - `context/architecture.md`
- - `memory-bank/debt-list.md`
- - `memory-bank/change-log.md`
diff --git a/context/newappeal-factor-guardrails.md b/context/newappeal-factor-guardrails.md
new file mode 100644
index 00000000..d31207af
--- /dev/null
+++ b/context/newappeal-factor-guardrails.md
@@ -0,0 +1,117 @@
+# New Appeal Refactor Guardrails
+
+## Purpose
+
+These guardrails apply to all AI-assisted and human-assisted work on the refactor branch for the new appeal flow.
+
+## Critical Rule
+
+Preserve the current live S78 behaviour unless the task explicitly requires a behaviour change.
+
+Do not assume a cleaner implementation is allowed to alter behaviour.
+
+## Protected Areas
+
+Treat the following as protected flow files:
+
+- `pages/newappeal/index.js`
+- `pages/newappeal/[appealtypes].js`
+- `components/newappeal/buildsection.js`
+- `components/newappeal/buildchecksection.js`
+- `components/newappeal/buildrow.js`
+- `components/newappeal/buildfield.js`
+- `components/newappeal/buildcheckrow.js`
+- `components/newappeal/complete.js`
+- `components/newappeal/aboutyou.js`
+- `components/newappeal/createCase.js`
+- any helpers or service modules used directly by save/progress/upload/submit/finalise logic
+
+## Protected Behaviour
+
+The following must not regress:
+
+1. Start a new S78 appeal
+2. Create case / initial journey entry
+3. Section rendering from form definition
+4. Section progression
+5. Save and exit
+6. Resume saved appeal
+7. File/document upload behaviour
+8. Check answers page
+9. Appeal PDF generation/download path
+10. Final submit/finalisation
+11. Confirmation page behaviour
+12. Email/notification side effects
+13. English/Welsh parity for touched areas
+
+## Required Refactor Approach
+
+When refactoring:
+
+1. Understand current behaviour first
+2. Identify the smallest safe boundary
+3. Prefer extraction of pure/helper logic
+4. Keep public interfaces stable where possible
+5. Avoid changing UI, business rules, and structure in one step
+6. Keep each slice easy to review and revert
+
+## Required Testing Mindset
+
+Before changing critical flow behaviour, add or update protection such as:
+
+- characterization tests for current behaviour
+- targeted integration/service tests
+- journey-level regression checks
+- payload-shape assertions for save/submit/finalise paths
+
+If automation is not practical yet, include an explicit manual verification matrix.
+
+## Payload/Integration Safety
+
+Do not change without explicit need:
+
+- CRM payload field names or structure
+- boolean/value normalization behaviour
+- document/file metadata shape
+- save/resume payload expectations
+- PDF generation inputs
+- notification template selection or personalisation structure
+
+## i18n / Accessibility Safety
+
+For touched user-facing behaviour:
+
+- keep EN/CY behaviour aligned
+- keep labels, messages, and route behaviour consistent
+- preserve semantic structure, focus behaviour, and validation messaging
+
+## Delivery Safety
+
+Preferred pattern for each change:
+
+1. protect current behaviour
+2. extract one concern
+3. run focused validation
+4. keep rollback straightforward
+5. merge only when safe
+
+## What To Avoid
+
+Do not:
+
+- perform broad rewrites
+- mix feature delivery with refactor work
+- replace dynamic/config-driven logic with one-off hardcoding
+- move many responsibilities at once
+- introduce new dependencies unless clearly justified
+- silently change business rules while “cleaning up”
+
+## Refactor Success Criteria
+
+A refactor slice is successful when it:
+
+- preserves behaviour
+- reduces complexity or coupling
+- improves readability or testability
+- keeps regression risk controlled
+- remains small enough to merge safely into `SIPS-Development`
diff --git a/context/next-work-plan-sequence-b.md b/context/next-work-plan-sequence-b.md
deleted file mode 100644
index 33a2d9a8..00000000
--- a/context/next-work-plan-sequence-b.md
+++ /dev/null
@@ -1,101 +0,0 @@
-# Sequence B Work Plan (2026-03-25)
-
-Scope: implement the next architecture lane after Sequence A completion.
-
-Sequence B objectives:
-
-1. Consolidate signed-request patterns.
-2. Harden logging policy in sensitive paths.
-
-## Workstream B1: Signed-request consolidation
-
-### Goal
-
-Reduce duplicate hash/header/method composition logic across service modules without changing behavior contracts.
-
-### Target scope
-
-- `actions/services/portalDirectService.js`
-- `actions/services/documentDirectService.js`
-- `actions/services/caseDirectService.js` (if signed routes exist)
-- shared client layer in `actions/clients/`
-
-### Proposed implementation
-
-1. Introduce a focused signed-request helper (or helper set) in `actions/clients/`:
- - signed GET
- - signed POST
- - signed DELETE
-2. Normalize hash/signing + header behavior through helper API.
-3. Migrate in bounded slices (module by module), preserving current catch semantics.
-
-### Acceptance criteria
-
-- No route URL/signature behavior regressions.
-- Existing signed flows preserve:
- - hash generation behavior
- - request method
- - required headers
- - error-return/catch contracts.
-- Phase22 behavioural tests expanded where relevant.
-
-### Validation checklist
-
-- `node tests/phase22/index.test.cjs`
-- `node tests/phase7/service-behaviour.test.cjs`
-- `npm run lint`
-
-### Rollback plan
-
-- Revert helper adoption commit(s) for affected service only.
-- Keep migrations bounded so each module rollback is isolated.
-
-## Workstream B2: Logging hardening in sensitive paths
-
-### Goal
-
-Replace ad-hoc verbose logging in auth/email/file/account-sensitive paths with redacted, structured logs.
-
-### Target scope
-
-- `pages/api/auth/[...nextauth].js`
-- selected `pages/api/email/**`
-- selected `pages/api/file/**`
-- any adjacent shared helper used by these routes
-
-### Proposed implementation
-
-1. Define/confirm minimal redaction policy (link to `memory-bank/open-questions.md` Q-001).
-2. Introduce/standardize structured logger usage pattern for sensitive flows.
-3. Replace high-risk direct logs in bounded route clusters.
-
-### Acceptance criteria
-
-- No secrets/tokens/personal data in new logs.
-- Error correlation remains operationally useful.
-- Existing route behavior/contracts unchanged.
-
-### Validation checklist
-
-- `npm run lint`
-- targeted route-level negative-path checks for changed handlers
-- manual review of log payload fields against redaction policy
-
-### Rollback plan
-
-- Revert logging-hardening commit(s) by cluster.
-- Restore previous logger call sites if operational diagnostics regress.
-
-## Delivery sequencing
-
-1. B1 signed-request helper design + one pilot migration.
-2. B1 full module rollout (portal/document, then any remaining signed paths).
-3. B2 redaction policy confirmation.
-4. B2 auth cluster hardening.
-5. B2 file/email cluster hardening.
-
-## Ownership and governance
-
-- Track each slice in `memory-bank/change-log.md`.
-- Record policy decisions in `memory-bank/decisions.md`.
-- Escalate unresolved policy questions in `memory-bank/open-questions.md`.
diff --git a/context/onboarding-guide.md b/context/onboarding-guide.md
deleted file mode 100644
index f13aecda..00000000
--- a/context/onboarding-guide.md
+++ /dev/null
@@ -1,112 +0,0 @@
-# Engineer Onboarding Guide — PEDW FrontEnd
-
-## 1) Project Overview
-
-PEDW FrontEnd is the Planning and Environment Decisions Wales (PEDW) portal for discovering planning appeals and accessing personalised casework journeys. It supports public search/browse and authenticated dashboard workflows, with strong accessibility and bilingual (English/Welsh) requirements.
-
-## 2) Technology Stack
-
-- **Frontend:** Next.js 14 (Pages Router), React 18
-- **Language:** Primarily JavaScript (some TypeScript tooling present)
-- **State:** Redux, `next-redux-wrapper`, `redux-persist`, `redux-thunk`, `redux-form`
-- **Auth:** `next-auth` + Prisma adapter (email magic-link flow)
-- **Auth data layer:** Prisma + SQL Server (`prisma/schema.prisma`) for next-auth identity/session tables
-- **Portal business data layer:** Dynamics 365 CRM queried via REST + OData through frontend API routes
-- **i18n:** `next-translate` + `i18n.js` + Welsh rewrites in `next.config.js`
-- **Integrations:** Azure Service Bus Relay (CRM transport), Azure Blob/Queue, GOV.UK Notify, Application Insights, mapping (Leaflet/google-map-react), PDF generation
-
-## 3) Architecture Overview
-
-- Active runtime is **Next.js runtime** (legacy custom server files exist but are inactive).
-- UI routes and APIs live in `pages/` and `pages/api/**`.
-- Security controls are applied through `middleware.js` + security headers in `next.config.js`.
-- Auth/session logic is centralized in `pages/api/auth/[...nextauth].js`.
-- Shared integration/service logic sits mostly in `actions/` (notably large `actions/index.js`).
-- Portal data calls are sent from `pages/api/endpoint/**` to Dynamics 365 CRM through Azure Service Bus Relay.
-- For relay-bound calls, a hash is generated from request path (excluding domain) and appended; relay validates using the same shared hash key.
-
-## 4) Repository Structure
-
-- `pages/` — routes + API handlers
-- `components/` — UI/features (case, DNS, account, admin, mapping, PDF templates)
-- `actions/` — API client and side-effect helpers
-- `lib/` — reusable form/domain helpers
-- `store/` — Redux reducers/store setup/hydration/persistence
-- `prisma/` — schema + migrations
-- `locales/` — EN/CY translations
-- `data/` — lookup and form metadata files
-- `tests/` — test area (appears limited)
-- `server/`, `server.js` — legacy/inactive runtime artifacts
-
-## 5) Core System Components
-
-- **Public search/case flows:** basic search (`/search`), advanced search (`/advancedsearch`), and address search (`/addresssearch`) with result pages and case detail/document UIs
-- **Account/auth:** next-auth email verification and Prisma-backed sessions
-- Portal routes (`/myportal/**`): user-specific case/representation/watchlist workflows
-- **File/doc pipeline:** upload/download/blob flows and PDF generation under `pages/api/file/**`
-- **Notifications:** GOV.UK Notify integrations under `pages/api/email/**` + auth email provider
-
-## 5a) User Involvement / Role Model
-
-- Newly registered/authenticated users default to **Interested Party** involvement.
-- Users who raise a new appeal become **Appellants**.
-- CRM contact constraints allow only one role type; once Appellant is assigned, it remains their role.
-- **Agents** can submit appeals on behalf of multiple Appellants.
-- **LPA (Local Planning Authority)** users are a separate persona with a distinct dashboard view.
-- LPA dashboards focus on appeals within that authority.
-- LPA users cannot raise appeals (option is hidden), but they can submit representations.
-
-## 6) Data Model
-
-Prisma schema is focused on next-auth persistence (SQL Server):
-
-- `User`
-- `Account`
-- `Session`
-- `VerificationToken`
-
-Datasource is SQL Server (`DATABASE_URL`). This model underpins authentication/session behavior and should be treated as sensitive core infrastructure. Portal business/case data is sourced separately from Dynamics 365 CRM via relay-backed API calls.
-
-## 7) Key Workflows
-
-- **Sign-in:** user requests magic link -> email via Notify -> callback/session via next-auth.
-- **Search journey:** frontend query -> `pages/api/endpoint/**` proxy endpoint(s) -> path hash appended -> Azure Service Bus Relay (validates hash with shared key) -> Dynamics 365 CRM (REST/OData) -> normalized UI rendering.
-- **Case summary to representation:** user selects case reference from results -> lands on case summary -> “make representation” shown only when criteria/date rules allow -> user can start flow but must be authenticated to submit.
-- **Dashboard journey (`/myportal`):** signed-in users can raise new appeals, search appeals, view watched cases, view partially completed appeals, view submitted appeals, and manage submitted/partially submitted representations.
-- **Role behavior:** default involvement starts as Interested Party; new appeal creation sets Appellant role (persistent due to CRM single-role contact model); Agent users may act for multiple Appellants.
-- **LPA dashboard behavior:** LPA users see an authority-scoped dashboard, do not see raise-appeal options, and can raise representations on existing appeals.
-- **Document workflow:** user upload/submit -> blob storage + metadata updates -> PDF/document retrieval.
-- **Bilingual routing:** Welsh aliases rewired in `next.config.js`, locale resources in `locales/en|cy`, page namespace mapping in `i18n.js`.
-
-## 8) Development Workflow
-
-From scripts and runbook:
-
-- `npm run dev` — local dev
-- `npm run build` + `npm start` — production build/start
-- `npm run lint` — baseline validation
-
-Expected change protocol emphasizes:
-
-- scoped changes,
-- EN/CY parity checks,
-- accessibility smoke checks,
-- negative-path checks on sensitive flows,
-- memory-bank/log updates for non-trivial changes.
-
-## 9) Observed Conventions
-
-- 4-space indentation, no trailing commas (per local conventions)
-- Keep page-level logic thin where possible; place reusable logic in `lib/`/`components/`/`actions/`
-- API naming pattern commonly uses `*_api.js`
-- Heavy use of proxy-style API handlers
-- Existing AI governance artifacts define guardrails (`.clinerules`, `GUARDRAILS.md`, `context/`, `memory-bank/`)
-
-## 10) Potential Risks / Weak Areas
-
-1. **Large `actions/index.js` coupling** (many responsibilities in one module)
-2. **API contract inconsistency** (error/response handling varies across endpoints)
-3. **Extensive `console.log` footprint** (signal/noise and potential sensitive logging concerns)
-4. **Duplication across API proxy handlers** (token/header/hash/relay logic repeated)
-5. **i18n complexity drift risk** (rewrite + locale namespace parity maintenance)
-6. **Limited automated tests for high-risk flows** (manual validation burden)
diff --git a/context/project-overview.md b/context/project-overview.md
deleted file mode 100644
index dab2a4f9..00000000
--- a/context/project-overview.md
+++ /dev/null
@@ -1,88 +0,0 @@
-# PEDW FrontEnd Project Overview
-
-## Purpose
-
-PEDW FrontEnd is a public-service web platform for planning casework and DNS (Developments of National Significance) journeys, with public search and citizen self-service plus authenticated portal/admin capabilities.
-
-## Current Stack
-
-- **Frontend:** Next.js 14 (`pages` router), React 18
-- **Server runtime:** Next.js runtime (legacy server files remain in-repo but are not used)
-- **State:** Redux + `next-redux-wrapper` + `redux-persist`
-- **Auth:** `next-auth` with Prisma adapter and email sign-in flow
-- **Auth persistence:** Prisma + SQL Server schema (`prisma/schema.prisma`) for next-auth tables
-- **Portal case data source:** Microsoft Dynamics 365 CRM (queried via REST + OData)
-- **Portal API transport path:** Frontend API routes -> Azure Service Bus Relay -> Dynamics 365 CRM
-- **Relay request integrity:** Forwarded API calls include a hash derived from request path (excluding domain). Relay recomputes hash with shared key and rejects mismatches.
-- **i18n:** `next-translate`, locales `en` and `cy`, Welsh rewrites in `next.config.js`
-- **Integrations:** Azure storage/queues, GOV.UK Notify, Application Insights, mapping, PDF generation
-
-## Repository Shape (Intent)
-
-- `pages/`: routes and API handlers (`pages/api/**`)
-- `components/`: UI and feature components (case, account, admin, mapping, PDF templates)
-- `actions/`: API client + shared side-effect logic
-- `lib/`: reusable domain helpers and form-related logic
-- `store/`: Redux reducers/store hydration/persistence
-- `prisma/`: schema and migrations
-- `locales/`: translation resources (EN/CY)
-- `server/` + root `server.js`: legacy runtime files retained in repository
-
-## Core User/Business Areas
-
-1. Public appeal discovery via:
- - basic search (`/search`)
- - advanced search (`/advancedsearch`)
- - address search (`/addresssearch`)
-2. Search results and case summary journey (case reference selection -> summary page)
-3. Representation flow from case summary (criteria/date dependent “make representation” action)
-4. Passwordless authentication via next-auth magic links for authenticated submissions
-5. Authenticated dashboard (`/myportal/**`) for personalised casework
-6. New appeal and representation lifecycle management (partial + submitted states)
-7. Admin/document workflows
-
-## Planning Casework Portal Dashboard (My Portal)
-
-The Planning Casework dashboard is the authenticated Appellant/Interested party user’s operational home page for:
-
-- starting new appeal submissions
-- Viewing the users submitted appeals
-- finding existing cases quickly
-- resuming in-progress submissions
-- monitoring submitted cases/representations
-- tracking watched cases
-- Information to view infrastructure project activity
-
-The Planning Casework dashboard is the authenticated LPA user’s operational home page for:
-
-- finding existing cases quickly
-- resuming in-progress submissions
-- monitoring submitted cases/representations
-- tracking watched cases
-- Information to view infrastructure project activity
-
-The dashboard is organized into task-focused panels that separate **action entry points** (for example, “Make a new appeal”, “Search for a case”) from **state-based worklists** (for example, “Appeals awaiting submission”, “My cases”, and representation lists).
-
-## Public Basic Search Results (Search Journey)
-
-In the public case-discovery journey, the basic search results page provides a structured list of matched cases and key metadata. The page supports sorting and pagination, links each case reference through to case detail, and presents a clear no-results or loading/error state when appropriate.
-
-Results content is locale-aware (EN/CY labels and formatted values) and includes highlighted query matches in key visible fields where supported by returned data.
-
-## User Involvement Model (Portal)
-
-- Newly registered/authenticated portal users default to **Interested Party** involvement.
-- Users who raise a new appeal become **Appellants**.
-- CRM contact constraints allow only one role type, so once set to Appellant this remains their role.
-- **Agents** can submit appeals on behalf of multiple Appellants.
-- **LPA (Local Planning Authority)** users have a distinct persona/dashboard view scoped to appeals raised within their authority.
-- LPA users do **not** see the “raise appeal” option.
-- LPA users can still make/submit representations on appeals.
-
-## Operating Constraints
-
-- Public-sector reliability expectations: avoid regressions on live service paths.
-- Bilingual parity is mandatory for user-facing route/content changes.
-- Accessibility expectations are high (keyboard and semantic behavior).
-- Security-sensitive areas include auth/session, uploads/documents, notifications, and account data.
-- Distinguish data paths: next-auth identity/session data is persisted in SQL Server; portal business data is sourced from Dynamics 365 CRM via relay-backed API calls with request-path hash validation.
diff --git a/context/refactor-branch-charter.md b/context/refactor-branch-charter.md
new file mode 100644
index 00000000..25b24b8b
--- /dev/null
+++ b/context/refactor-branch-charter.md
@@ -0,0 +1,90 @@
+# Refactor Branch Charter — New Appeal Flow
+
+## Purpose
+
+This branch exists to safely refactor the live new appeal flow so it is easier to maintain, safer to change, and better prepared to support additional appeal types beyond the current S78 planning appeal flow.
+
+This is a **behaviour-preserving refactor branch**, not a feature branch.
+
+## Primary Goal
+
+Create a safer internal structure for the new appeal flow while preserving current live behaviour for the S78 appeal journey.
+
+## Why This Branch Exists
+
+The current new appeal implementation has grown over time to meet business need and now contains a mix of:
+
+- page composition
+- flow orchestration
+- XML-driven form rendering
+- validation
+- file/document handling
+- progress/save logic
+- submission/finalisation logic
+- integration shaping for CRM, PDF generation, and notifications
+
+This branch exists to improve those boundaries incrementally without disrupting the live service.
+
+## Scope
+
+In scope:
+
+- behaviour-preserving refactor of `pages/newappeal/**` and `components/newappeal/**`
+- extraction of reusable workflow logic from UI-heavy components
+- improved boundaries between rendering, workflow, and integration logic
+- regression test coverage for critical S78 journeys
+- preparing the codebase for future appeal-type extensibility
+
+Out of scope unless explicitly requested:
+
+- business rule changes
+- visual redesign
+- broad framework/library migration
+- replacing working dynamic form behaviour with hardcoded appeal-specific logic
+- changes to live BAU behaviour beyond strictly necessary bug fixes
+
+## Branch Relationship to BAU
+
+- BAU continues on `SIPS-Development`
+- this branch is the protected refactor lane
+- safe, proven slices may be merged back into `SIPS-Development` when ready
+- urgent live fixes should go to `SIPS-Development` first, then be synced into this branch
+
+## Non-Negotiable Principles
+
+1. Preserve live S78 behaviour unless explicitly told otherwise.
+2. Prefer extraction over rewrite.
+3. Prefer small, mergeable slices over long-lived hidden change.
+4. Add or update regression protection before changing critical flow logic.
+5. Keep English/Welsh behaviour aligned.
+6. Treat save/resume/upload/check/submit/complete as protected journey stages.
+
+## Target Direction
+
+The long-term direction is:
+
+- shared new appeal workflow engine
+- appeal-type definitions/configuration separated from UI rendering
+- smaller, clearer components
+- isolated validation and payload-shaping logic
+- safer addition of future appeal types through definition + bounded type-specific rules
+
+## Definition of Success
+
+This branch is succeeding when:
+
+- core S78 journey behaviour remains stable
+- regression confidence increases
+- high-risk logic moves out of large render-heavy components
+- new appeal code becomes easier to understand and test
+- future appeal types can be added with less change to core flow code
+
+## Working Branch Model
+
+This refactor stream uses the `refactor` branch as its working base.
+
+- BAU continues on `SIPS-Development`
+- refactor work is performed from `refactor`
+- safe refactor slices may later be merged into `SIPS-Development`
+
+This branch should not be treated as BAU, and BAU should not be treated as the refactor workspace.
diff --git a/context/refactor-tracker.md b/context/refactor-tracker.md
new file mode 100644
index 00000000..b12e85c0
--- /dev/null
+++ b/context/refactor-tracker.md
@@ -0,0 +1,110 @@
+# New Appeal Refactor Tracker
+
+Base branch for this tracker: `refactor`
+
+## Status
+
+Current slice: Slice 1 — XML/Form Derivation Extraction
+Status: NOT STARTED
+
+---
+
+## Slice List
+
+### Slice 1 — XML/Form Derivation Extraction
+
+Status: NOT STARTED
+
+- Extract XML parsing helpers into lib
+- Keep selectors identical
+- No behaviour change
+
+---
+
+### Slice 2a — Payload Cleanup Helpers
+
+Status: NOT STARTED
+
+- Extract boolean normalization
+- Extract null/internal key stripping
+
+---
+
+### Slice 2b — File Merge/Dedupe Helpers
+
+Status: NOT STARTED
+
+- Extract file list merge logic
+- Extract dedupe logic
+
+---
+
+### Slice 3 — Side Effect Facade
+
+Status: NOT STARTED
+
+- Wrap existing service calls
+- No logic changes
+
+---
+
+### Slice 4 — BuildSection UI Extraction
+
+Status: NOT STARTED
+
+---
+
+### Slice 5 — BuildCheckSection UI Extraction
+
+Status: NOT STARTED
+
+---
+
+### Slice 6 — BuildCheckRow Formatter Map
+
+Status: NOT STARTED
+
+---
+
+### Slice 7 — Props Boundary Cleanup
+
+Status: NOT STARTED
+
+---
+
+### Slice 8 — Start Flow Cleanup (CreateCase / AboutYou)
+
+Status: NOT STARTED
+
+---
+
+## Rules
+
+- Only work on ONE slice at a time
+- Do not move to next slice until current is COMPLETE
+- Do not combine slices
+- Preserve behaviour at all times
+
+## Notes
+
+- Save/resume payload shape is sensitive
+- File upload logic duplicated in multiple places
+- BuildSection is highest risk area
+
+## Regression Checklist (Run After Each Slice)
+
+- Start new appeal
+- Save and exit
+- Resume saved appeal
+- Navigate sections
+- Upload file
+- View check answers
+- Submit appeal (if safe to test)
+- View confirmation page
+- Verify EN/CY parity
+
+Validation:
+
+- npm run lint passed
+- Playwright end-to-end new appeal journey passed
+- submission completed successfully through CRM insertion
diff --git a/lib/newappeal/formDerivation.js b/lib/newappeal/formDerivation.js
new file mode 100644
index 00000000..393a2cc7
--- /dev/null
+++ b/lib/newappeal/formDerivation.js
@@ -0,0 +1,102 @@
+import xpath from "xpath";
+
+export const parseXml = (xmlStr) => {
+ const parser = new DOMParser();
+ return parser.parseFromString(xmlStr, "text/xml");
+};
+
+export const selectCurrentSectionTitleDescriptions = (doc, currentSection) => {
+ return xpath.select(
+ "//form/tabs/tab[" + currentSection + " ]/labels/label/@description",
+ doc
+ );
+};
+
+export const selectCurrentSectionRows = (doc, currentSection) => {
+ return xpath.select(
+ "/form/tabs/tab[" +
+ currentSection +
+ "]/columns//sections/section/rows/row",
+ doc
+ );
+};
+
+export const selectFormTabs = (doc) => {
+ return xpath.select("/form/tabs", doc);
+};
+
+export const selectAllTabTitleDescriptions = (doc) => {
+ return xpath.select("//form/tabs/tab[*]/labels/label/@description", doc);
+};
+
+export const parseRowOuterHTML = (outerHTML) => {
+ const parser = new DOMParser();
+ return parser.parseFromString(outerHTML, "text/xml");
+};
+
+export const selectRowLabelDescriptions = (doc) => {
+ return xpath.select("//label/@description", doc);
+};
+
+export const selectRowFieldType = (doc) => {
+ return xpath.select("//@classid", doc);
+};
+
+export const selectRowValidation = (doc) => {
+ return xpath.select("//@validation", doc);
+};
+
+export const selectRowMaxFieldLength = (doc) => {
+ return xpath.select("//@maxFieldLength", doc);
+};
+
+export const selectRowMaxSubField = (doc) => {
+ return xpath.select("//@maxsubfield", doc);
+};
+
+export const selectRowDataFieldName = (doc) => {
+ return xpath.select("//@datafieldname", doc);
+};
+
+export const selectRowDataFieldContent = (doc) => {
+ return xpath.select("//@datafieldcontent", doc);
+};
+
+export const selectRowParentField = (doc) => {
+ return xpath.select("//@parentField", doc);
+};
+
+export const selectRowParentFieldShowOnValue = (doc) => {
+ return xpath.select("//@parentFieldShowOnValue", doc);
+};
+
+export const selectRowRequiredDocumentValue = (doc) => {
+ return xpath.select("//@requiredDocumentValue", doc);
+};
+
+export const selectRowRequiredDocumentLabel = (doc) => {
+ return xpath.select("//@requiredDocumentLabel", doc);
+};
+
+export const selectRowDocumentTypeCode = (doc) => {
+ return xpath.select("//@ishareDocumentCode", doc);
+};
+
+export const selectRowHint = (doc) => {
+ return xpath.select("//label/@hint", doc);
+};
+
+export const selectRowDateStart = (doc) => {
+ return xpath.select("//@dateStart", doc);
+};
+
+export const selectRowDateEnd = (doc) => {
+ return xpath.select("//@dateEnd", doc);
+};
+
+export const selectErrorFieldLabelDescription = (doc, whichField) => {
+ return xpath.select(
+ "//*[control/@id='" + whichField + "']//@description",
+ doc
+ );
+};
diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md
index a2de7b0a..7cca28ff 100644
--- a/memory-bank/change-log.md
+++ b/memory-bank/change-log.md
@@ -912,6 +912,63 @@ Follow-ups:
- Breadcrumb route-state extraction closure slices (1/2/3) complete.
+### CL-22541-Z4: breadcrumbs post-closure hotfix — callable label resolver parity (static + view-all)
+
+date: 2026-04-09
+author: Cline
+scope: `components/breadcrumbs.js`, `tests/phase22/breadcrumbs-route-map-structure.test.cjs`
+type: change
+rationale: Fix post-refactor regression where some breadcrumbs rendered blank labels because mapped label entries were returned as function references instead of resolved values.
+impact: Restores user-visible breadcrumb labels for static and view-all keyed routes; no auth/session/API/security behavior change.
+status: completed
+
+Summary:
+
+- Fixed static text breadcrumb label resolver to invoke mapped label functions:
+ - `getStaticTextCrumbLabel(path)` now resolves callable entries (`resolver ? resolver() : null`).
+- Fixed view-all label resolver parity to support both function-backed and string-backed map entries:
+ - `getViewAllLabel(viewKey)` now invokes function entries and returns string entries directly.
+- Added structure guard assertions to prevent regression of callable label resolution behavior.
+
+Validation:
+
+- `node tests/phase22/breadcrumbs-route-map-structure.test.cjs` -> pass (4/4).
+- `node tests/phase22/index.test.cjs` -> pass (combined suite).
+
+Follow-ups:
+
+- Breadcrumb label resolver parity now aligned across mapped/static/view-all label paths.
+
+### CL-22541-Z5: architecture comparison overview refresh (senior architecture review)
+
+date: 2026-04-09
+author: Cline
+scope: `context/architecture-overview-2026-04-09.md`
+type: change
+rationale: User requested a fresh senior architecture review and comparison baseline covering scalability, maintainability, coupling, boundary quality, operational/deployment risk, and prioritized technical debt actions.
+impact: Documentation-only architecture guidance update; no runtime/auth/session/security/API behavior change.
+status: completed
+
+Summary:
+
+- Added new architecture comparison baseline document:
+ - `context/architecture-overview-2026-04-09.md`
+- Included requested structure:
+ 1. current architecture summary
+ 2. strengths
+ 3. risks (scalability, maintainability, coupling, weak boundaries, operational/deployment)
+ 4. prioritized recommendations
+ 5. low-risk next improvements
+- Added explicit comparison notes vs existing `context/architecture.md` and identified strategic focus areas for the next modernization wave.
+
+Validation:
+
+- Documentation coherence review against current repo architecture docs (`context/architecture.md`, `context/integration-map.md`, `context/project-overview.md`).
+
+Follow-ups:
+
+- Optionally elevate this architecture overview into a periodic architecture scorecard cadence and add measurable KPIs in memory-bank.
+
### CL-00X: 22500 `components/elements/index.js` Phase 1 helper extraction
date: 2026-04-07