Files
pedwfrontend/memory-bank/change-log.md
T

202 KiB
Raw Blame History

Change Log (AI/Human Curated)

Entry Template

date: YYYY-MM-DD
author: <agent|name>
scope: <files/routes/features>
type: change
rationale: <why change was made>
impact: <user/system/security/i18n/a11y>
status: completed|rolled-back|partial

Summary:
Validation:
Follow-ups:

date: 2026-04-09
author: Cline
scope: lib/routing/routeState.js, components/breadcrumbs.js, components/case/summary.js, tests/phase22/{route-state-helper.test.cjs,index.test.cjs}
type: change
rationale: Action Priority 3 refactor backlog item by extracting duplicated route-state decision logic (va/adv/ads/key) into a shared pure helper and reusing it from breadcrumb and case-summary back-link flows to reduce drift/regression risk.
impact: Refactor-only decision-layer consolidation for search/case/myportal navigation; no auth/session/CSP/API contract changes; EN/CY behavior intended unchanged as helper only resolves route targets and preserves existing query forwarding behavior.
status: completed

Summary:

  • Created branch from origin/SIPS-Development: 22541-breadcrumb-route-state-helper.
  • Added lib/routing/routeState.js with:
    • normalizeRouteStateQuery(query)
    • resolveSearchResultsHref(options)
  • Updated components/breadcrumbs.js to consume the shared helper for:
    • breadcrumb href resolution (/myportal/viewall, advanced/address/default results)
    • case-results breadcrumb target on /case/[ticketnumber]
    • preserving existing label selection behavior via normalized flags.
  • Updated components/case/summary.js to replace local buildSearchHref with shared helper output (searchHref), including DNS-route override and no-flag fallback behavior.
  • Added table-driven unit coverage in tests/phase22/route-state-helper.test.cjs and registered it in tests/phase22/index.test.cjs.

Validation:

  • npx eslint lib/routing/routeState.js components/breadcrumbs.js components/case/summary.js tests/phase22/route-state-helper.test.cjs tests/phase22/index.test.cjs -> pass with 1 pre-existing warning in components/case/summary.js (react-hooks/exhaustive-deps).
  • node tests/phase22/route-state-helper.test.cjs -> pass (5/5).
  • node tests/phase22/index.test.cjs -> fails in existing auth suite (tests/phase22/auth-redirect-safety.test.cjs) with ReferenceError: resolveLocale is not defined in pages/api/auth/[...nextauth].js; unrelated to route-state changes.

Follow-ups:

  • If needed, triage/fix the existing phase22 auth redirect test harness failure separately to restore full combined suite execution.
  • Expand helper adoption in future slices to other navigation decision call sites if additional duplication emerges.

CL-22541-B: breadcrumb route-state helper expansion (label + DNS path helper)

date: 2026-04-09
author: Cline
scope: lib/routing/routeState.js, components/breadcrumbs.js, components/case/summary.js, tests/phase22/route-state-helper.test.cjs
type: change
rationale: Continue Priority 3 breadcrumb refactor in a second bounded commit by extracting remaining repeated breadcrumb label and DNS-route-path decision logic into shared pure helpers. impact: Refactor-only extraction for navigation decision logic; no auth/session/CSP/API changes; no intended EN/CY behavior change beyond internal logic centralization. status: completed

Summary:

  • Extended lib/routing/routeState.js with:
    • resolveSearchBreadcrumbLabel(...)
    • isDnsRoutePath(pathname)
  • Updated components/breadcrumbs.js to use resolveSearchBreadcrumbLabel(...) and removed inline label decision IIFE.
  • Updated components/case/summary.js to use isDnsRoutePath(router.pathname) and removed inline DNS path checks.
  • Expanded tests/phase22/route-state-helper.test.cjs with coverage for:
    • breadcrumb label resolution matrix
    • DNS path detection helper

Validation:

  • npx eslint lib/routing/routeState.js components/breadcrumbs.js components/case/summary.js tests/phase22/route-state-helper.test.cjs -> pass with 1 pre-existing warning in components/case/summary.js (react-hooks/exhaustive-deps).
  • node tests/phase22/route-state-helper.test.cjs -> pass (7/7).
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Next breadcrumbs slice can target incremental decomposition of large pathname branch rendering blocks in components/breadcrumbs.js into grouped render helpers with behavior parity.

CL-22541-C: breadcrumb case-state bundling helper (single-call resolver)

date: 2026-04-09
author: Cline
scope: lib/routing/routeState.js, components/breadcrumbs.js, tests/phase22/route-state-helper.test.cjs
type: change
rationale: Continue Priority 3 breadcrumbs refactor by bundling repeatedly paired breadcrumb href/label resolution into one pure helper so component call sites stay thinner and less drift-prone. impact: Refactor-only centralization of existing route-state composition logic; no intended auth/session/API/i18n behavior change. status: completed

Summary:

  • Added resolveCaseBreadcrumbState(...) in lib/routing/routeState.js to return:
    • breadcrumbHref
    • caseResultsHref
    • breadcrumbLabel
  • Updated components/breadcrumbs.js to replace three separate helper invocations with one resolver call.
  • Extended tests/phase22/route-state-helper.test.cjs with bundled case-state resolver coverage.

Validation:

  • npx eslint lib/routing/routeState.js components/breadcrumbs.js tests/phase22/route-state-helper.test.cjs -> pass.
  • node tests/phase22/route-state-helper.test.cjs -> pass (8/8).
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Next bounded slice can extract grouped pathname render blocks from components/breadcrumbs.js (data-driven map or small render helpers) while preserving route parity.

CL-22541-D: breadcrumbs shared my-portal crumb helper extraction

date: 2026-04-09
author: Cline
scope: components/breadcrumbs.js
type: change
rationale: Continue bounded breadcrumbs decomposition by extracting repeated my-portal breadcrumb <li> markup into one local render helper to reduce duplication and keep follow-on slices safer. impact: Refactor-only JSX deduplication in breadcrumb rendering; no intended route, auth/session, API, EN/CY, or accessibility behavior change. status: completed

Summary:

  • Added local renderMyPortalCrumb() helper in components/breadcrumbs.js.
  • Replaced repeated identical my-portal crumb JSX in multiple /myportal/* route branches with helper calls.
  • Kept special-case crumbs untouched where label/href differs (e.g. newappeal parent-title crumb, cyMyPortalHref account branches).

Validation:

  • npx eslint components/breadcrumbs.js -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Next bounded slice can target additional repeated breadcrumb list-item patterns (e.g., repeated case-reference tail item blocks) with same behavior parity approach.

CL-22541-E: breadcrumbs case-reference crumb helper extraction

date: 2026-04-09
author: Cline
scope: components/breadcrumbs.js
type: change
rationale: Continue bounded breadcrumbs decomposition by extracting repeated case-reference tail breadcrumb markup into one local helper and replacing repeated inline blocks. impact: Refactor-only JSX deduplication in breadcrumb rendering; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added local renderCaseReferenceCrumb(referenceValue) helper in components/breadcrumbs.js.
  • Replaced repeated {t("common:breadcrumb-case-reference")}: ... list-item blocks across case/dns route branches with helper calls.
  • Corrected initial over-wrap by using helper calls directly (so generated <li> is not nested in another <li>).

Validation:

  • npx eslint components/breadcrumbs.js -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Next bounded slice can target remaining repeated linked breadcrumb list-item patterns (e.g., recurring address-search result link pairs) via small local render helpers.

date: 2026-04-09
author: Cline
scope: components/breadcrumbs.js
type: change
rationale: Deliver a slightly larger bounded slice by extracting and reusing multiple breadcrumb rendering patterns (plain text crumb and linked crumb) across several route branches. impact: Refactor-only JSX deduplication and consistency improvement in breadcrumb rendering; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added reusable helpers in components/breadcrumbs.js:
    • renderTextCrumb(label)
    • renderLinkCrumb(href, label, onClick)
  • Replaced multiple repeated inline breadcrumb blocks across search/address/myportal/dns branches with helper calls.
  • Included onClick-capable link helper usage in the /myportal/case/id/[incident] address-results breadcrumb branch to preserve router.back() behavior.
  • Combined with existing renderMyPortalCrumb and renderCaseReferenceCrumb helpers for more consistent branch composition.

Validation:

  • npx eslint components/breadcrumbs.js -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Next slice recommendation: extract remaining repeated inline link crumbs for case/DNS branches that still hardcode <li><Link ...> (e.g., /myportal/case, /myportal/case/[ticketnumber], /dnsdetails, /dns/[developmentName]) to complete link-pattern consolidation.

date: 2026-04-09
author: Cline
scope: components/breadcrumbs.js
type: change
rationale: Continue the larger bounded breadcrumb refactor cadence by replacing the next batch of repeated inline <li><Link ...> crumbs with the shared link helper. impact: Refactor-only JSX deduplication and consistency improvement in breadcrumb rendering; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Reused renderLinkCrumb(href, label, onClick) for another broad set of previously inline Link crumbs, including:
    • service-name crumb in the shared shell
    • new appeal select-appeal parent/title crumbs
    • case and ticketnumber branches (/myportal/case, /case/[ticketnumber], /case/id/[incident], /myportal/case/[ticketnumber])
    • additional myportal/dns/account branches (/myportal/dns/[developmentName], /dnsdetails, /dns/[developmentName], /dns/application-view, account crumbs)
  • Kept special behavior intact where needed (e.g., existing callback-based crumbs that already use helper onClick).
  • Increased consistency of crumb rendering style by routing most link crumbs through one helper.

Validation:

  • npx eslint components/breadcrumbs.js -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Next slice recommendation: target remaining anchor/back-link duplication (<a href="#"> and occasional <a href={...}>) by introducing a bounded helper for action/back crumbs while preserving existing non-Link semantics.

date: 2026-04-09
author: Cline
scope: components/breadcrumbs.js
type: change
rationale: Continue the larger bounded breadcrumbs refactor cadence by extracting repeated anchor/back-link crumb markup into one local helper while preserving non-Link semantics. impact: Refactor-only JSX deduplication and consistency improvement in breadcrumb rendering; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added renderAnchorCrumb(href, label, onClick, listItemClass) in components/breadcrumbs.js for repeated <a ...> breadcrumb actions.
  • Replaced repeated anchor/back-link crumb blocks in:
    • /myportal/searchresults my-portal anchor crumb
    • /newappeal/[appealtypes] and /myportal/[appealtypes] step-back crumbs
    • /myportal/representation back-chevron action crumbs (all three state branches)
  • Preserved original click handlers, href values, and class semantics (govuk-breadcrumbs__link-item, backChevron) to keep behavior parity.

Validation:

  • npx eslint components/breadcrumbs.js -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Next slice recommendation: consolidate remaining one-off inline breadcrumb <li><Link ...> blocks still present in edge branches (e.g., /newappeal/[appealtypes], /myportal/[appealtypes], /case) and consider introducing an optional renderBackCrumb(...) semantic wrapper for readability.

CL-22541-I: breadcrumbs edge branch consolidation + semantic back-crumb wrapper

date: 2026-04-09
author: Cline
scope: components/breadcrumbs.js
type: change
rationale: Continue the same bounded breadcrumbs refactor cadence by consolidating remaining edge inline Link/back crumbs and introducing a semantic back-crumb wrapper for readability. impact: Refactor-only JSX deduplication/readability improvement; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added semantic helper renderBackCrumb(onClick, listItemClass?) that delegates to renderAnchorCrumb(...) with existing back-link text.
  • Replaced remaining one-off inline edge crumbs in:
    • /newappeal/[appealtypes] and /myportal/[appealtypes] (currentSection === 9999 service-name link and back-link branch)
    • /case search-results link crumb (including existing router.back() callback)
    • /myportal/representation back-link branches now use renderBackCrumb(...).
  • Preserved existing href targets, callback behavior, and class semantics (including special govuk-breadcrumbs__link-item usage).

Validation:

  • npx eslint components/breadcrumbs.js -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Next slice recommendation: consider extracting a small, route-keyed breadcrumb branch map/helper grouping in components/breadcrumbs.js (purely structural) to reduce the long inline conditional chain while keeping one-slice behavior parity.

CL-22541-J: breadcrumbs static text-only route crumb consolidation

date: 2026-04-09
author: Cline
scope: components/breadcrumbs.js
type: change
rationale: Continue the same bounded refactor cadence by consolidating repeated static text-only breadcrumb route branches into one route-label resolver. impact: Refactor-only structural deduplication/readability improvement; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added getStaticTextCrumbLabel(path) in components/breadcrumbs.js to centralize static text-only crumb labels by pathname.
  • Replaced repeated single-line static crumb branches with one generic render path:
    • renderTextCrumb(getStaticTextCrumbLabel(pathname)) when mapping returns a label.
  • Consolidated static branch handling for routes including:
    • account register
    • DNS index/help/contact/applications process pages
    • cookies/privacy/accessibility/static auth routes.
  • Preserved route labels (including translation keys and legacy hardcoded labels where already present).

Validation:

  • npx eslint components/breadcrumbs.js -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Next slice recommendation: extract a small grouped helper for recurring two-crumb route pairs (e.g., my-portal + section label, dns list + case reference) to reduce the remaining conditional chain while preserving explicit route behavior.

CL-22541-K: breadcrumbs grouped two-crumb helper extraction

date: 2026-04-09
author: Cline
scope: components/breadcrumbs.js
type: change
rationale: Continue the same bounded refactor cadence by extracting recurring two-crumb patterns into small grouped helpers while preserving explicit route branches. impact: Refactor-only readability/deduplication improvement; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added grouped helper renderMyPortalSectionCrumbs(sectionLabel) for recurring my-portal + section text pairs.
  • Added grouped helper renderDnsCaseReferenceCrumbs(referenceValue) for recurring DNS applications link + case-reference pairs.
  • Replaced duplicated route branches with grouped helper usage for:
    • /myportal/addresssearch, /myportal/advancedsearch, /myportal/contactus
    • /dnsdetails, /dns/[developmentName]
  • Preserved existing href behavior (including EN/CY DNS applications link), labels, and case-reference rendering.

Validation:

  • npx eslint components/breadcrumbs.js -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Next slice recommendation: extract a small route-to-renderer map for static single-branch crumbs (where branch has no bespoke side-effects) so the main JSX conditional chain shrinks further without altering explicit behavior in dynamic/sensitive branches.

CL-22541-L: breadcrumbs simple-route renderer map introduction

date: 2026-04-09
author: Cline
scope: components/breadcrumbs.js
type: change
rationale: Continue bounded readability refactor by introducing a small route-to-renderer map for simple static branches, reducing conditional-chain length while preserving explicit dynamic branches. impact: Refactor-only structural deduplication/readability improvement; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added getSimpleRouteRenderer(path) in components/breadcrumbs.js for simple routes with no bespoke side effects.
  • Introduced a single invocation block that resolves and renders mapped simple routes when present.
  • Migrated simple route branches into the map, including:
    • /myportal, /searchresults, /advancedsearch, /addresssearch, /contactus
    • /viewall, /myportal/dnsapplications
  • Preserved existing label text, helper usage, and rendered crumb sequence semantics.

Validation:

  • npx eslint components/breadcrumbs.js -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Next slice recommendation: extract a second tiny map for simple "link + text" pair routes (no callbacks, no side effects) to further shrink the conditional chain while keeping callback/dynamic branches explicit.

CL-22541-M: breadcrumbs simple link+text pair map + focused structure coverage

date: 2026-04-09
author: Cline
scope: components/breadcrumbs.js, tests/phase22/{breadcrumbs-route-map-structure,index}.test.cjs
type: change
rationale: Execute the next bounded slice by extracting simple link+text pair route branches into a tiny renderer map and adding focused regression checks that mapped and dynamic callback branches remain in the intended structure. impact: Refactor-only structural deduplication/readability improvement plus focused test coverage; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added getSimpleLinkTextPairRenderer(path) in components/breadcrumbs.js for simple no-callback "link + text" pair routes.
  • Migrated these routes into the new pair map:
    • /addresssearchresults
    • /advancedsearchresults
  • Kept dynamic/callback-driven branches explicit and untouched (e.g., /myportal/case/id/[incident] with router.back()).
  • Added focused phase22 suite tests/phase22/breadcrumbs-route-map-structure.test.cjs asserting:
    • mapped simple pair routes exist in the new map
    • explicit dynamic callback branch remains present.
  • Registered the new suite in tests/phase22/index.test.cjs.

Validation:

  • npx eslint components/breadcrumbs.js tests/phase22/index.test.cjs tests/phase22/breadcrumbs-route-map-structure.test.cjs -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite, including new breadcrumbs-route-map tests 2/2).

Follow-ups:

  • Next slice recommendation: introduce a tiny local renderMappedRoute(path, resolver) helper to remove repeated inline IIFE map invocations, then optionally merge simple maps under one top-level resolver while preserving explicit ordering and side-effect-free constraints.

CL-22541-N: breadcrumbs unified mapped-route resolver + widened structural guardrails

date: 2026-04-09
author: Cline
scope: components/breadcrumbs.js, tests/phase22/breadcrumbs-route-map-structure.test.cjs
type: change
rationale: Execute the requested wider next slice by removing duplicated mapped-route invocation patterns, introducing a single top-level mapped resolver with explicit precedence, and migrating an additional safe my-portal route group into map-driven rendering while preserving explicit dynamic/callback branches. impact: Refactor-only structural consolidation/readability improvement with widened mapped-route coverage and stronger structure guardrail tests; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added getSimpleMyPortalRouteRenderer(path) in components/breadcrumbs.js and migrated additional safe map-friendly routes:
    • /myportal/searchresults
    • /myportal/addresssearch
    • /myportal/advancedsearch
    • /myportal/contactus
    • /myportal/viewall
  • Added resolveMappedRouteRenderer(path) to unify map precedence under one resolver.
  • Added renderMappedRoute(path, resolver?) helper and replaced duplicated inline map IIFE invocations with a single call:
    • {renderMappedRoute(pathname)}
  • Kept dynamic/stateful callback branches explicit and unchanged, especially /myportal/case/id/[incident] (router.back()) and other callback/state driven branches.
  • Expanded tests/phase22/breadcrumbs-route-map-structure.test.cjs to assert:
    • new my-portal route-map entries are present
    • unified resolver/helper signatures are present
    • single mapped-route invocation is used in JSX
    • dynamic callback branch remains explicit.

Validation:

  • npx eslint components/breadcrumbs.js tests/phase22/breadcrumbs-route-map-structure.test.cjs tests/phase22/index.test.cjs -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite; breadcrumbs-route-map tests 3/3).

Follow-ups:

  • Next wider slice candidate: move a second safe myportal results pair group (/myportal/advancedsearchresults, /myportal/addresssearchresults) into the unified mapped resolver where callback-free, then consider splitting resolver maps into a dedicated local constant section for improved scanability.

CL-22541-O: breadcrumbs myportal results-pair migration into unified mapped resolver

date: 2026-04-09
author: Cline
scope: components/breadcrumbs.js, tests/phase22/breadcrumbs-route-map-structure.test.cjs
type: change
rationale: Execute the agreed next slice by migrating the remaining callback-free myportal results-pair branches into the unified mapped resolver so more safe branches are rendered through one consistent mapping flow. impact: Refactor-only structural consolidation/readability improvement; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Extended getSimpleMyPortalRouteRenderer(path) to include:
    • /myportal/advancedsearchresults
    • /myportal/addresssearchresults
  • Removed now-redundant explicit conditional branches for those two routes from the main JSX chain.
  • Kept callback/stateful branches explicit and unchanged (notably /myportal/case/id/[incident] with router.back()).
  • Expanded tests/phase22/breadcrumbs-route-map-structure.test.cjs guardrails to assert:
    • both myportal results-pair routes are mapped
    • legacy explicit isPath(...) branches for those routes are no longer present

Validation:

  • npx eslint components/breadcrumbs.js tests/phase22/breadcrumbs-route-map-structure.test.cjs tests/phase22/index.test.cjs -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite; breadcrumbs-route-map tests 3/3).

Follow-ups:

  • Next wider slice candidate: split map resolver sections into clearly named grouped constants (simple/public/myportal) near one composition point, then optionally move map-only route renderers to a local pure helper module if continued growth impacts scanability.

CL-22541-P: breadcrumbs grouped route-map constants + unified map composition point

date: 2026-04-09
author: Cline
scope: components/breadcrumbs.js, tests/phase22/breadcrumbs-route-map-structure.test.cjs
type: change
rationale: Execute the next wider slice by replacing per-group resolver functions with grouped route-map constants and a single composition array to further improve scanability while preserving route precedence semantics. impact: Refactor-only structural readability improvement; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Replaced function-based map accessors with grouped route-map constants in components/breadcrumbs.js:
    • simpleRouteRenderersByPath
    • simpleLinkTextPairRenderersByPath
    • simpleMyPortalRouteRenderersByPath
  • Added unified map composition point:
    • mappedRouteRendererMaps (ordered array preserving precedence)
    • getMappedRendererByPath(path, routeRendererMap) helper
  • Updated resolveMappedRouteRenderer(path) to iterate the composed map list while retaining previous resolution order and behavior.
  • Kept dynamic/callback branches explicit and unchanged outside map-driven paths.
  • Expanded structure tests to assert grouped map composition contracts (mappedRouteRendererMaps, getMappedRendererByPath) in addition to existing resolver and invocation guardrails.

Validation:

  • npx eslint components/breadcrumbs.js tests/phase22/breadcrumbs-route-map-structure.test.cjs tests/phase22/index.test.cjs -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite; breadcrumbs-route-map tests 3/3).

Follow-ups:

  • Next slice option A (larger): extract map constants + resolver helpers into a local lib/routing/breadcrumbRouteMaps.js pure module with focused tests, keeping callback/stateful branch rendering in component.
  • Next slice option B (smaller): expand structure tests to assert explicit map ordering invariants so future reordering regressions are caught early.

CL-22541-Q: breadcrumbs route-map helper module extraction + map-order invariants

date: 2026-04-09
author: Cline
scope: lib/routing/breadcrumbRouteMaps.js, components/breadcrumbs.js, tests/phase22/{breadcrumb-route-maps-helper,breadcrumbs-route-map-structure,index}.test.cjs
type: change
rationale: Execute both queued follow-up slices together by extracting map composition/resolution logic into a pure routing helper module and adding stronger structure + pure-helper guardrails for map-order precedence. impact: Refactor-only structural modularization and test hardening; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added new pure helper module lib/routing/breadcrumbRouteMaps.js:
    • buildBreadcrumbRendererMaps(...)
    • getMappedRendererByPath(...)
    • resolveMappedRouteRenderer(...)
  • Updated components/breadcrumbs.js to import/use shared helper logic:
    • map composition now via buildBreadcrumbRendererMaps(...)
    • resolver now delegates to resolveMappedRouteRendererByMaps(...)
    • dynamic/callback-sensitive explicit branches remain unchanged.
  • Added focused pure-helper tests in tests/phase22/breadcrumb-route-maps-helper.test.cjs for:
    • map build order
    • map lookup null/renderer behavior
    • resolver precedence behavior
  • Expanded tests/phase22/breadcrumbs-route-map-structure.test.cjs to assert:
    • component imports shared breadcrumb route-map helper module
    • resolver delegation uses helper module
    • map builder call preserves explicit ordering (simple -> link-text -> myportal)
  • Updated tests/phase22/index.test.cjs to include the new helper suite.

Validation:

  • npx eslint lib/routing/breadcrumbRouteMaps.js components/breadcrumbs.js tests/phase22/breadcrumb-route-maps-helper.test.cjs tests/phase22/breadcrumbs-route-map-structure.test.cjs tests/phase22/index.test.cjs -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite; breadcrumb-route-maps-helper 3/3; breadcrumbs-route-map 3/3).

Follow-ups:

  • Next slice option A (larger): move per-path map object declarations out of components/breadcrumbs.js into module-level pure factories (still injected with t/router/currentView) to further shrink component body.
  • Next slice option B (smaller): add one focused parity test asserting mapped resolver fallback returns null in component rendering path for non-mapped routes (ensures no accidental render noise).

CL-22541-R: breadcrumbs mapped-group factory composition + unmapped-route null parity guard

date: 2026-04-09
author: Cline
scope: lib/routing/breadcrumbRendererFactories.js, components/breadcrumbs.js, tests/phase22/breadcrumbs-route-map-structure.test.cjs
type: change
rationale: Execute both approved next slices in one commit by centralizing deterministic mapped route-group composition into a factory-level helper and adding explicit structure guard coverage for null fallback behavior on unmapped routes. impact: Refactor-only structural consolidation and test hardening; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added createMappedRouteRendererGroups(...) in lib/routing/breadcrumbRendererFactories.js to compose and return all deterministic mapped renderer groups in one place:
    • simpleRouteRenderersByPath
    • simpleLinkTextPairRenderersByPath
    • simpleMyPortalRouteRenderersByPath
    • caseDetailRouteRenderersByPath
    • detailAndAccountRouteRenderersByPath
  • Updated components/breadcrumbs.js to consume createMappedRouteRendererGroups(...) and remove local per-group factory composition boilerplate while preserving existing map precedence and resolver flow.
  • Expanded tests/phase22/breadcrumbs-route-map-structure.test.cjs to assert:
    • grouped renderer maps are composed via createMappedRouteRendererGroups(...)
    • mapped rendering path explicitly preserves null fallback (routeRenderer ? routeRenderer() : null)

Validation:

  • npx eslint lib/routing/breadcrumbRendererFactories.js components/breadcrumbs.js tests/phase22/breadcrumbs-route-map-structure.test.cjs tests/phase22/breadcrumb-route-maps-helper.test.cjs tests/phase22/index.test.cjs -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite; breadcrumbs-route-map 4/4).

Follow-ups:

  • Next slice option A (larger): extract remaining deterministic inline branches (/myportal/case, /case/id/[incident] if kept deterministic after constraints review) only if they remain callback/state free.
  • Next slice option B (smaller): add one focused assertion in helper-level tests that empty grouped map entries are tolerated without affecting precedence resolution.

CL-22541-S: breadcrumbs deterministic case-branch extraction (/myportal/case, /case/id/[incident])

date: 2026-04-09
author: Cline
scope: lib/routing/breadcrumbRendererFactories.js, components/breadcrumbs.js, tests/phase22/breadcrumbs-route-map-structure.test.cjs
type: change
rationale: Execute the next deterministic extraction slice by migrating remaining callback-free case breadcrumb branches into the case-detail factory group while preserving map precedence and leaving stateful/callback routes inline. impact: Refactor-only structural extraction and test guardrail expansion; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Extended createCaseDetailRouteRenderers(...) in lib/routing/breadcrumbRendererFactories.js to map:
    • /myportal/case
    • /case/id/[incident]
  • Injected additional deterministic inputs required by these routes:
    • currentReference
    • nestedSearchString
  • Updated createMappedRouteRendererGroups(...) wiring to pass the new inputs to case-detail factory composition.
  • Updated components/breadcrumbs.js to pass nestedSearchString into grouped factory composition.
  • Removed now-redundant inline isPath(...) branches for:
    • /myportal/case
    • /case/id/[incident]
  • Expanded tests/phase22/breadcrumbs-route-map-structure.test.cjs to assert:
    • both new routes are mapped in createCaseDetailRouteRenderers
    • both inline branches are absent from components/breadcrumbs.js

Validation:

  • npx eslint lib/routing/breadcrumbRendererFactories.js components/breadcrumbs.js tests/phase22/breadcrumbs-route-map-structure.test.cjs tests/phase22/index.test.cjs -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite; breadcrumbs-route-map 4/4).

Follow-ups:

  • Next slice option A (larger): evaluate whether any remaining deterministic inline branches can be extracted without violating callback/state constraints.
  • Next slice option B (smaller): add a focused helper-level precedence test where earlier map contains falsy/non-function values and later maps contain valid renderers.

CL-22541-T: breadcrumbs larger slice — new-appeal mapped group extraction (/newappeal/selectappeal)

date: 2026-04-09
author: Cline
scope: lib/routing/{breadcrumbRendererFactories,breadcrumbRouteMaps}.js, components/breadcrumbs.js, tests/phase22/{breadcrumb-route-maps-helper,breadcrumbs-route-map-structure}.test.cjs
type: change
rationale: Execute the next larger deterministic refactor slice by extracting callback-free new-appeal breadcrumb branching into a dedicated mapped renderer group and extending map-order guardrails. impact: Refactor-only structural extraction plus precedence-test hardening; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added createNewAppealRouteRenderers(...) in lib/routing/breadcrumbRendererFactories.js.
  • Mapped /newappeal/selectappeal into the new grouped renderer factory.
  • Extended grouped composition return from createMappedRouteRendererGroups(...) with newAppealRouteRenderersByPath.
  • Updated lib/routing/breadcrumbRouteMaps.js to include newAppealRouteRenderersByPath in explicit map precedence ordering.
  • Updated components/breadcrumbs.js to consume/pass newAppealRouteRenderersByPath into map builder.
  • Removed now-redundant inline isPath("/newappeal/selectappeal") branch from component JSX.
  • Expanded tests:
    • tests/phase22/breadcrumbs-route-map-structure.test.cjs
      • assert new factory export and mapped route presence
      • assert inline branch removal
      • assert updated map-order invariant (simple -> link-text -> myportal -> new-appeal -> case detail -> detail/account)
    • tests/phase22/breadcrumb-route-maps-helper.test.cjs
      • assert six-map ordering contract and updated grouped precedence wiring.

Validation:

  • npx eslint lib/routing/breadcrumbRendererFactories.js lib/routing/breadcrumbRouteMaps.js components/breadcrumbs.js tests/phase22/breadcrumb-route-maps-helper.test.cjs tests/phase22/breadcrumbs-route-map-structure.test.cjs tests/phase22/index.test.cjs -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Next larger slice candidate: evaluate extraction viability for /case branch only if callback/router.back() behavior can remain explicit and parity-safe.
  • Next smaller slice candidate: add helper-level test coverage for unexpected non-function map values and explicit skip behavior (defensive precedence guard).

CL-22541-U: breadcrumbs smaller slice — defensive mapped-route resolver hardening

date: 2026-04-09
author: Cline
scope: lib/routing/breadcrumbRouteMaps.js, tests/phase22/breadcrumb-route-maps-helper.test.cjs
type: change
rationale: Execute the queued smaller slice by hardening mapped-route lookup against malformed map entries and non-function values while keeping precedence behavior unchanged. impact: Refactor-hardening only; no intended breadcrumb route behavior change for valid mapped renderers. status: completed

Summary:

  • Updated getMappedRendererByPath(...) to safely handle invalid route-map entries:
    • returns null when map is missing/non-object
    • returns null when mapped value is not a function
  • Preserved existing resolver precedence semantics for valid function renderers.
  • Added focused helper test asserting resolver skips:
    • invalid map entries (null/undefined)
    • non-function mapped values and still resolves the first later valid renderer.

Validation:

  • npx eslint lib/routing/breadcrumbRouteMaps.js tests/phase22/breadcrumb-route-maps-helper.test.cjs tests/phase22/index.test.cjs -> pass.
  • node tests/phase22/index.test.cjs -> pass (combined suite; breadcrumb-route-maps-helper 5/5).

Follow-ups:

  • Next larger slice: extract remaining callback-bearing deterministic branches into mapped route groups with callback injection, then update structure guards accordingly.

CL-00X: 22500 components/elements/index.js Phase 1 helper extraction

date: 2026-04-07
author: Cline
scope: components/elements/index.js, components/elements/helpers/fileUploadHelpers.js, components/elements/helpers/translationHelpers.js, memory-bank/refactor-backlog.md
type: change
rationale: Execute Phase 1 of the approved components/elements/index.js decomposition plan by extracting pure helper logic only, reducing monolith coupling while preserving UI/component behavior.
impact: No route/API contract changes; refactor-only extraction of translation/file-upload helper functions with expected behavior parity for EN/CY field labels, file naming, thumbnail icon mapping, and filename validation.
status: completed

Summary:

  • Created work branch from origin/SIPS-Development: 22500-elements-index-phase1.
  • Added helper modules:
    • components/elements/helpers/fileUploadHelpers.js
      • getThumbnailIconByMimeType
      • getDocumentTypePrefix
      • validateUploadFilename
    • components/elements/helpers/translationHelpers.js
      • getFieldTranslation
      • getPickListTranslation
  • Updated components/elements/index.js to consume these helpers and removed duplicated inline helper implementations.
  • Kept field renderer/component placement and external prop contracts unchanged (Phase 1 non-goals respected).
  • Updated memory-bank/refactor-backlog.md with a phased Priority 6 track and Phase 1 guardrail-aligned acceptance criteria.

Validation:

  • npx eslint components/elements/index.js components/elements/helpers/fileUploadHelpers.js components/elements/helpers/translationHelpers.js -> pass

Follow-ups:

  • Phase 2: extract low-risk leaf field renderer components from components/elements/index.js in bounded slices.
  • Perform manual EN/CY + a11y smoke matrix on new appeal/myportal form journeys before merge.

CL-00Y: 22500 components/elements/index.js Phase 2 leaf renderer extraction (Rich multiline)

date: 2026-04-07
author: Cline
scope: components/elements/index.js, components/elements/fields/renderRichMultiline.js
type: change
rationale: Continue the approved phased decomposition by extracting one low-risk leaf renderer (RenderRichMultiline) from the elements monolith while keeping existing field wiring and behavior intact.
impact: Refactor-only move of rich multiline renderer implementation; no route/API/auth/security changes and no intended EN/CY behavior change.
status: completed

Summary:

  • Added components/elements/fields/renderRichMultiline.js containing the extracted RenderRichMultiline renderer.
  • Updated components/elements/index.js to import the extracted renderer and removed the inline duplicate implementation.
  • Kept RichMultiLinefield usage and props unchanged (same Redux Field component wiring and validation flow).

Validation:

  • npx eslint components/elements/index.js components/elements/fields/renderRichMultiline.js -> pass

Follow-ups:

  • Continue Phase 2 in bounded slices by extracting additional low-risk leaf renderers (e.g., RenderMultiline / RenderTextfield) with no behavior change.

CL-00Z: 22500 components/elements/index.js Phase 2 leaf renderer extraction (Text + Multiline)

date: 2026-04-07
author: Cline
scope: components/elements/index.js, components/elements/fields/renderTextfield.js, components/elements/fields/renderMultiline.js
type: change
rationale: Complete the requested next bounded phase by extracting the additional low-risk leaf renderers (RenderTextfield, RenderMultiline) from the elements monolith into dedicated field modules while preserving existing wiring and behavior.
impact: Refactor-only move of two renderer components; no intended changes to auth/API/security and no intended EN/CY behavior change.
status: completed

Summary:

  • Added components/elements/fields/renderTextfield.js and components/elements/fields/renderMultiline.js.
  • Updated components/elements/index.js to import the extracted renderers.
  • Removed inline RenderTextfield and RenderMultiline implementations from index.js.

Validation:

  • npx eslint components/elements/index.js components/elements/fields/renderTextfield.js components/elements/fields/renderMultiline.js -> pass

Follow-ups:

  • Continue Phase 2 by selecting the next lowest-risk leaf renderer extraction in a separate commit.

CL-001: TASK22211 endpoint search-document contract consistency slice

date: 2026-03-23
author: Cline
scope: pages/api/endpoint/{getsearchdocumenthistory_api,getsearchdocumenthistorypaged_api,getsearchdocumentdetails_api,getsearchdocumentdetailspaged_api,getsearchdocumentTypes_api}.js, tests/phase21/endpoint-handler-contract.test.cjs
type: change
rationale: Continue the endpoint contract-consistency stream by normalizing a coherent search-document handler cluster that still used raw error passthrough and noisy legacy logging patterns.
impact: Improved negative-path consistency and safer error contract handling in search-document endpoints while preserving success payload behavior.
status: completed

Summary:

  • Branch created from SIPS-Development: TASK22211-endpoint-search-document-contract-consistency.
  • Standardized five search-document handlers to respondError/respondSuccess usage.
  • Added explicit required-input guards:
    • DOCUMENT_ID_REQUIRED for history/historypaged
    • INCIDENT_ID_REQUIRED for details/detailspaged/types
    • ORDER_BY_REQUIRED, FIELD_SORT_REQUIRED, SHOW_NUMBER_OF_RECORDS_REQUIRED for details-paged query requirements
  • Removed noisy direct logging in paged/details code paths.
  • Preserved success contract patterns (pass-through or transformed payloads where already established).
  • Expanded phase21 endpoint tests with missing-input, catch-path, and success parity assertions for this cluster.

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 11/11
    • email-handler: 12/12
    • endpoint-handler: 53/53
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps warnings; no new lint errors)

Follow-ups:

  • Continue the next endpoint cluster using the same pattern (bounded slice + phase21 test expansion).
  • Keep response success payloads contract-stable and avoid broad relay/auth refactors in this stream.

CL-002: TASK22211 endpoint token handler contract consistency slice

date: 2026-03-23
author: Cline
scope: pages/api/endpoint/getToken.js, tests/phase21/endpoint-handler-contract.test.cjs
type: change
rationale: Close out remaining non-standard endpoint contract handling by normalizing the legacy token endpoint to shared API response helpers and explicit error coding.
impact: Improved endpoint error consistency and test coverage for token acquisition failures while preserving successful token payload passthrough.
status: completed

Summary:

  • Refactored getToken.js to use respondSuccess and respondError from pages/api/middleware/apiResponse.
  • Removed legacy raw res.status(...).json(...)/bare status assignment pattern and dead logging artifacts.
  • Added explicit catch-path contract: TOKEN_FETCH_FAILED with 400 status.
  • Added endpoint phase21 tests for:
    • success token payload passthrough
    • catch-path error contract assertion

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 11/11
    • email-handler: 12/12
    • endpoint-handler: 147/147
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps warnings; no new lint errors)

Follow-ups:

  • Remaining outlier API handler for this consistency stream is pages/api/file/generateappealpdfcopy.js (not yet on shared response helpers).

CL-003: TASK22211 endpoint contract-hardening stream backfill (all known slices)

date: 2026-03-23
author: Cline
scope: pages/api/endpoint/*_api.js, pages/api/endpoint/getToken.js, tests/phase21/endpoint-handler-contract.test.cjs
type: change
rationale: Backfill memory-bank traceability so the complete known TASK22211 contract-consistency stream is documented in one place now that memory-bank is being versioned.
impact: Improves governance/auditability of API contract hardening, makes rollout and rollback analysis easier, and records exactly which endpoint clusters were normalized.
status: completed

Summary:

  • Backfilled all known TASK22211 slices currently on branch (in commit order):
    • b57f3de search-document endpoint contracts + phase21 coverage
    • 9af541a my-portal retrieval endpoint contracts
    • b880364 basic search endpoint contracts
    • a106dea DNS basic search endpoint contracts
    • b5a3a62 portal module + LPA case endpoint contracts
    • 4601d7c case detail endpoint contracts
    • 2959c7d delete/watched-case endpoint contracts
    • b59f13a metadata + linked-case endpoint contracts
    • bcf03a6 form + publication endpoint contracts
    • e0e91c8 DNS + representation endpoint contracts
    • 98e159d case creation + media endpoint contracts
    • 88e4586 advanced-search-paged endpoint contract
    • cb69bbe case update + CRM task endpoint contracts
    • 722ef98 hash + metadata endpoint contracts
    • 134f99c address-search endpoint contract
    • 8b6ed73 new-appeal appeal-types endpoint contract
    • eec59e8 token endpoint contract handling
  • Across the stream, handlers were standardized toward respondSuccess/respondError, required-input guards, and explicit negative-path error codes while preserving success payload compatibility.
  • Phase21 endpoint contract suite was expanded incrementally alongside each slice.

Validation:

  • Stream validation baseline (latest known run):
    • node tests/phase21/api-contract-slice1.test.cjs -> pass (endpoint-handler 147/147)
    • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Continue with remaining non-standard API outlier(s), notably pages/api/file/generateappealpdfcopy.js.
  • Keep future slices logged in this file at commit-time now that memory-bank is versioned.

CL-004: TASK22224 file + static endpoint contract hardening bundle (phase21)

date: 2026-03-23
author: Cline
scope: pages/api/file/{downloadblob,generateappealpdfcopy}.js, pages/api/endpoint/{getsipsmedia_api,getappealtypesfornewappeal_api}.js, tests/phase21/{file-handler-contract,endpoint-handler-contract}.test.cjs
type: change
rationale: Deliver the agreed larger bounded slice for remaining non-standard file/static handlers, improving negative-path consistency while preserving current success payload behavior.
impact: Standardized error envelopes/codes for download and generated PDF copy flows, method guard parity for static endpoints, and expanded phase21 contract coverage for both file and endpoint handlers.
status: completed

Summary:

  • downloadblob.js:
    • added explicit catch-path response via respondError with DOWNLOAD_BLOB_FAILED
    • kept success behavior intact (attachment header + raw file body)
    • removed dead internal helper (streamToBuffer) and tightened local declarations
  • generateappealpdfcopy.js:
    • removed unused imports/noisy console warnings
    • standardized required-input and negative-path contracts:
      • INCIDENT_ID_REQUIRED (400)
      • CASE_NOT_FOUND (404)
      • FORM_COLLECTION_NOT_FOUND (400)
      • APPEAL_PDF_COPY_GENERATION_FAILED (400)
    • preserved success output contract (PDF content headers + buffer body)
  • getsipsmedia_api.js and getappealtypesfornewappeal_api.js:
    • added method guard for non-GET requests using METHOD_NOT_ALLOWED (405)
    • preserved existing GET success payloads
  • Expanded phase21 tests:
    • file-handler-contract.test.cjs: added coverage for download failure + full generated PDF copy contract/negative paths
    • endpoint-handler-contract.test.cjs: added method guard tests for both static endpoints

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 17/17
    • email-handler: 12/12
    • endpoint-handler: 149/149
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • If desired, next slice can target remaining file-route parity candidates outside this bundle, but this closes the planned TASK22224 scope.

CL-005: TASK22224 downloadblob hotfix closure (path normalization + hash compatibility)

date: 2026-03-23
author: Cline
scope: pages/api/file/downloadblob.js
type: change
rationale: Close post-merge runtime regressions reported on live links where download URLs alternated between filename-only/full-path blob names and mixed encoded/raw hash input variants.
impact: Restored reliable blob downloads without relaxing hash security guarantees (still HMAC validated), and preserved existing caller compatibility across legacy/new URL encodings.
status: completed

Summary:

  • Hotfix 1 (f09f3b7): normalized blob path resolution
    • accepts both forms of blobname input:
      • filename only (legacy)
      • full prefixed path (already includes casefolderID/...)
    • prevents double-prefix lookup failures
    • sets attachment filename from final path segment only
  • Hotfix 2 (bd3bf68): hash compatibility validation
    • validates against a bounded set of canonical query-path variants (raw/encoded combinations for casefolderID and blobname)
    • fixes INVALID_HASH false negatives for legitimate caller-generated links
    • keeps strict HMAC requirement in place (no unauthenticated bypass)

Validation:

  • node tests/phase21/file-handler-contract.test.cjs -> pass (17/17)
  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 17/17
    • email-handler: 12/12
    • endpoint-handler: 149/149
  • User confirmation: "downloadblob now works"

Follow-ups:

  • Next recommended slice on this branch: complete file-route guard parity for deleteblob.js, deleteblobcase.js, and deleteblobrep.js by aligning hash validation canonicalization and explicit respondError contracts (MISSING_REQUIRED_QUERY, INVALID_HASH, operation-specific *_FAILED).
  • Extend tests/phase21/file-handler-contract.test.cjs for the above routes with mixed encoded/raw hash cases to lock compatibility.

CL-006: TASK22224 file delete-route guard parity slice

date: 2026-03-23
author: Cline
scope: pages/api/file/{deleteblob,deleteblobcase,deleteblobrep}.js, tests/phase21/file-handler-contract.test.cjs
type: change
rationale: Execute the next planned slice to align hash/canonicalization behavior and negative-path contracts across high-risk file delete routes, matching the compatibility posture established for downloadblob.
impact: Reduces false INVALID_HASH failures for legitimate encoded/raw caller variants while preserving strict hash enforcement and improving resilience via explicit catch-path contracts.
status: completed

Summary:

  • deleteblob.js
    • added bounded hash candidate validation for encoded/raw combinations of casefolderID and blobname
    • normalized delete path handling for both filename-only and already-prefixed blob paths
    • added explicit catch-path contract: DELETE_BLOB_FAILED
  • deleteblobcase.js
    • added hash candidate validation for raw/encoded casefolderID
    • added explicit catch-path contract: DELETE_BLOB_CASE_FAILED
  • deleteblobrep.js
    • added hash candidate validation for encoded/raw casefolderID + repfile
    • added explicit catch-path contract: DELETE_BLOB_REP_FAILED
  • Phase21 tests expanded (file-handler-contract.test.cjs):
    • encoded hash-variant acceptance cases for all three delete routes
    • explicit dependency-failure contract assertions for all three delete routes

Validation:

  • node tests/phase21/file-handler-contract.test.cjs -> pass (23/23)
  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 23/23
    • email-handler: 12/12
    • endpoint-handler: 149/149

Follow-ups:

  • Optional next slice: apply same bounded hash-canonicalization parity to remaining high-sensitivity file routes where mixed encoded/raw callers may exist (getbloblist, getprogressobjblob) and add regression cases to phase21.

CL-007: TASK22224 getrepsblob stability hotfix after delete representation flow

date: 2026-03-23
author: Cline
scope: actions/azurestorage.js (getRepsBlobs), tests/phase21/file-handler-contract.test.cjs
type: change
rationale: Resolve reported runtime 400 (GET_REPS_BLOB_FAILED) after delete representation actions, caused by stale soft-deleted blob tag hits during representation blob enumeration.
impact: Prevents transient/stale Azure tag index entries from breaking representation retrieval, improving reliability of post-delete refresh without relaxing route security contracts.
status: completed

Summary:

  • Hardened getRepsBlobs(containerName) in actions/azurestorage.js:
    • fixed async misuse (blobClient.getProperties().contentLength without await)
    • added existence/property guard with explicit await blobClient.getProperties()
    • skips 404s (soft-deleted/stale tag index results) instead of throwing
    • preserves behavior for non-404 failures (rethrow for proper error visibility)
    • kept existing _rep.json/undefined name filtering intact
  • Added phase21 contract coverage for getrepsblob route:
    • success payload contract test
    • dependency failure contract test (GET_REPS_BLOB_FAILED)

Validation:

  • node tests/phase21/file-handler-contract.test.cjs -> pass (25/25)
  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 25/25
    • email-handler: 12/12
    • endpoint-handler: 149/149

Follow-ups:

  • Optional: add the same stale-tag existence guard pattern to any remaining Azure tag-list readers that still consume findBlobsByTags results without property existence verification.

CL-008: TASK22224 awaiting-submission route resilience parity hardening

date: 2026-03-23
author: Cline
scope: pages/api/file/getawaitingsubmissionfromblob.js, tests/phase21/file-handler-contract.test.cjs
type: change
rationale: Add explicit catch-path contract parity for awaiting-submission blob retrieval route so unexpected dependency failures return consistent, actionable error envelopes.
impact: Improves reliability/diagnostics for post-delete case refresh and aligns file-route error handling style without changing success payload contract or hash verification behavior.
status: completed

Summary:

  • Refactored getawaitingsubmissionfromblob handler to structured try/catch flow.
  • Preserved existing guard behavior:
    • MISSING_REQUIRED_QUERY for missing container/hash
    • INVALID_HASH for signature mismatch
  • Added explicit dependency failure contract:
    • GET_AWAITING_SUBMISSION_BLOB_FAILED (400)
    • message: Failed to retrieve awaiting submission blobs
  • Added phase21 coverage for this route:
    • success payload pass-through contract
    • dependency failure contract assertion

Validation:

  • node tests/phase21/file-handler-contract.test.cjs -> pass (27/27)
  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 27/27
    • email-handler: 12/12
    • endpoint-handler: 149/149

Follow-ups:

  • Optional parity sweep: apply the same explicit catch-path contract pattern to remaining file routes that still rely on implicit promise-chain errors.

CL-009: TASK22224 proxy-route resilience and encoding parity bundle

date: 2026-03-23 author: Cline scope: pages/api/file/{getbloblistproxy,getrepsblobproxy,getawaitingsubmissionfromblobproxy,createappealcompletemessageproxy_api}.js, tests/phase21/file-handler-contract.test.cjs type: change rationale: Continue larger-slice hardening by aligning proxy handlers with explicit async error handling and safer encoded upstream query forwarding for hash-based downstream calls. impact: Improves proxy reliability and compatibility for encoded query values while preserving existing proxy error contracts and response behavior. status: completed

Summary:

  • getbloblistproxy.js
    • converted .then/.catch chain to explicit try/catch
    • encoded forwarded container and casefolderID query values
    • preserved error contract: GET_BLOB_LIST_PROXY_FAILED
  • getrepsblobproxy.js
    • converted .then/.catch chain to explicit try/catch
    • encoded forwarded container
    • preserved error contract: GET_REPS_BLOB_PROXY_FAILED
  • getawaitingsubmissionfromblobproxy.js
    • converted .then/.catch chain to explicit try/catch
    • preserved error contract: GET_AWAITING_SUBMISSION_PROXY_FAILED
  • createappealcompletemessageproxy_api.js
    • converted .then/.catch chain to explicit try/catch
    • encoded forwarded container and tempcaseref
    • preserved error contract: CREATE_APPEAL_COMPLETE_MESSAGE_PROXY_FAILED
  • Phase21 tests expanded for proxy paths:
    • getbloblistproxy success + dependency failure
    • getrepsblobproxy success
    • getawaitingsubmissionfromblobproxy dependency failure
    • createappealcompletemessageproxy dependency failure

Validation:

  • node tests/phase21/file-handler-contract.test.cjs -> pass (46/46)
  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 46/46
    • email-handler: 12/12
    • endpoint-handler: 149/149

Follow-ups:

  • Optional next big slice: bring remaining proxy/message routes using raw axios promise chains (createcaseinvolvement_api.js, createrepinvolvement_api.js, updatecase_api.js) onto the same async/await + explicit contract pattern.

CL-010: TASK22224 involvement/update route async contract hardening bundle

date: 2026-03-23 author: Cline scope: pages/api/file/{createcaseinvolvement_api,createrepinvolvement_api,updatecase_api}.js, tests/phase21/file-handler-contract.test.cjs type: change rationale: Execute the next requested slice by modernizing remaining relay-backed involvement/update handlers that still used axios promise chains and legacy dead imports. impact: Improves reliability/readability and preserves existing response contracts, including 412 "record exists" semantics for involvement creation flows. status: completed

Summary:

  • createcaseinvolvement_api.js
    • removed unused CryptoJS import
    • refactored axios .then/.catch to explicit try/catch
    • preserved conflict behavior: status 412 -> success { record: "exists" }
    • preserved failure contract: CREATE_CASE_INVOLVEMENT_FAILED
  • createrepinvolvement_api.js
    • removed unused CryptoJS import
    • refactored axios .then/.catch to explicit try/catch
    • preserved conflict behavior: status 412 -> success { record: "exists" }
    • preserved failure contract: CREATE_REP_INVOLVEMENT_FAILED
  • updatecase_api.js
    • removed unused CryptoJS import
    • refactored axios .then/.catch to explicit try/catch
    • preserved failure contract: UPDATE_CASE_FAILED
  • Phase21 tests expanded:
    • createcaseinvolvement 412 conflict success contract
    • createrepinvolvement dependency failure contract
    • updatecase dependency failure contract

Validation:

  • node tests/phase21/file-handler-contract.test.cjs -> pass (49/49)
  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 49/49
    • email-handler: 12/12
    • endpoint-handler: 149/149

Follow-ups:

  • Optional: apply equivalent modernization to any remaining relay-backed handlers outside pages/api/file/ that still use raw axios promise chains and have no explicit phase21 contract assertions.

CL-011: TASK22224 aggressive non-file bundle (email/admin/endpoint parity)

date: 2026-03-23 author: Cline scope: pages/api/email/{getmailinglist,getcaseref,notify}.js, pages/api/admin/{getnewappeals_api,getlatestdocuments_api}.js, pages/api/endpoint/getportallogin_api.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Execute requested aggressive bundling for remaining non-file modernization/parity candidates: remove legacy promise chains and improve hash compatibility on login endpoint while preserving existing contracts. impact: Improves consistency and resilience across email/admin/endpoint routes with no contract regressions; adds encoded hash-variant compatibility for portal login hash checks. status: completed

Summary:

  • pages/api/email/getmailinglist.js
    • converted axios .then/.catch to try/catch
    • preserved flattening behavior and error contract MAILING_LIST_FETCH_FAILED
  • pages/api/email/getcaseref.js
    • converted axios .then/.catch to try/catch
    • preserved flattening behavior and error contract CASE_REF_FETCH_FAILED
  • pages/api/email/notify.js
    • converted notify client .then/.catch to try/catch
    • preserved success payload and error contract EMAIL_NOTIFY_FAILED
  • pages/api/admin/getnewappeals_api.js
    • removed unused CryptoJS import
    • converted axios .then/.catch to try/catch
    • preserved @odata.nextLink normalization and error contract ADMIN_NEW_APPEALS_FETCH_FAILED
  • pages/api/admin/getlatestdocuments_api.js
    • converted axios .then/.catch to try/catch
    • preserved flatten/enrich behavior and error contract ADMIN_LATEST_DOCS_FETCH_FAILED
  • pages/api/endpoint/getportallogin_api.js
    • retained required query/hash guards
    • expanded hash validation to accept raw + encoded emailAddress query-path candidates
    • preserved error contract PORTAL_LOGIN_FETCH_FAILED
  • phase21 endpoint tests expanded:
    • getportallogin encoded hash variant success path
    • getnewappeals_api catch contract
    • getlatestdocuments_api catch contract

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 49/49
    • email-handler: 12/12
    • endpoint-handler: 152/152

Follow-ups:

  • Remaining major modernization candidate is pages/api/file/generateappealpdf.js (+ optional pages/api/file/generatepdf.js) if we continue final closure slices.

CL-012: TASK22224 generatepdf/generateappealpdf async hardening slice

date: 2026-03-23 author: Cline scope: pages/api/file/{generateappealpdf,generatepdf}.js, tests/phase21/file-handler-contract.test.cjs type: change rationale: Execute next requested slice to modernize remaining file PDF-generation handlers still using promise chains, while preserving existing hash/response behavior. impact: Improves maintainability and error-path consistency for PDF generation routes; phase21 coverage now includes explicit failure contracts for both handlers. status: completed

Summary:

  • pages/api/file/generateappealpdf.js
    • converted mixed promise-chain flow to async/await + try/catch
    • preserved existing guard contracts: MISSING_REQUIRED_QUERY, INVALID_HASH
    • preserved generation failure contract: GENERATE_APPEAL_PDF_FAILED
    • replaced JSX render call with React.createElement(...) compatibility form used by test loader
  • pages/api/file/generatepdf.js
    • converted create/upload promise-chain to async/await + try/catch
    • preserved existing guard contracts: HASH_REQUIRED, INVALID_HASH
    • preserved generation failure contract: GENERATE_PDF_FAILED
    • replaced JSX render call with React.createElement(...) compatibility form used by test loader
  • phase21 file tests expanded:
    • generatepdf catch-path contract (GENERATE_PDF_FAILED)
    • generateappealpdf catch-path contract (GENERATE_APPEAL_PDF_FAILED)

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 51/51
    • email-handler: 12/12
    • endpoint-handler: 152/152

Follow-ups:

  • Remaining optional cleanup in these handlers is dead import/unused local pruning (non-behavioral) if we want a final low-risk tidy pass.

CL-013: TASK22224 completion-message route parity closure slice

date: 2026-03-24 author: Cline scope: pages/api/file/createappealcompletemessage_api.js, tests/phase21/file-handler-contract.test.cjs type: change rationale: Continue next requested slice by closing the final promise-chain parity outlier in file completion-message flow and strengthening phase21 contract coverage. impact: Improves maintainability and async error hygiene while preserving route behavior and existing error contracts. status: completed

Summary:

  • pages/api/file/createappealcompletemessage_api.js
    • replaced inline .catch(...) on fire-and-forget updateAccount(...) with explicit async IIFE + try/catch and void invocation
    • preserved non-blocking behavior and logging semantics for account-update failure path
    • preserved primary route contracts and success payload ({ status: "success" })
  • phase21 file contract tests expanded:
    • success path for encoded hash candidate on createappealcompletemessage_api
    • dependency-failure contract assertion for CREATE_APPEAL_COMPLETE_MESSAGE_FAILED

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 53/53
    • email-handler: 12/12
    • endpoint-handler: 152/152

Follow-ups:

  • Optional final low-risk tidy sweep: remove dead imports/unused locals in legacy file handlers now that contract hardening stream is functionally complete.

CL-014: TASK22224 pdf render compatibility tidy slice

date: 2026-03-24 author: Cline scope: pages/api/file/{generatepdf,generateappealpdf}.js, tests/phase21/file-handler-contract.test.cjs type: change rationale: Apply a low-risk compatibility tidy so PDF render invocation remains stable across runtime and contract-test VM contexts. impact: Keeps functional behavior unchanged while reducing test/runtime mismatch risk in render path setup. status: completed

Summary:

  • pages/api/file/generatepdf.js
    • switched render call input from React.createElement(MyDocument, ...) to direct MyDocument(...) invocation in ReactPDF.renderToStream(...)
  • pages/api/file/generateappealpdf.js
    • switched render call input from React.createElement(MyDocument, ...) to direct MyDocument(...) invocation in ReactPDF.renderToStream(...)
  • tests/phase21/file-handler-contract.test.cjs
    • added Buffer injection for generatepdf catch-path test harness to align VM context expectations

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 53/53
    • email-handler: 12/12
    • endpoint-handler: 152/152

Follow-ups:

  • Optional: dead import cleanup (Document/Page/Text/View/StyleSheet/PDFViewer, middleware, nextConnect, fs, etc.) can be done in a dedicated non-behavioral hygiene PR.

CL-015: TASK22224 pdf handler dead-code hygiene slice

date: 2026-03-24 author: Cline scope: pages/api/file/{generatepdf,generateappealpdf}.js type: change rationale: Execute the requested next low-risk slice by removing dead imports and unused locals in recently hardened PDF handlers. impact: Non-behavioral maintainability cleanup; reduces lint noise and future edit risk while preserving existing contracts. status: completed

Summary:

  • pages/api/file/generatepdf.js
    • pruned unused Azure storage imports, leaving only createRepPDFBlob
    • pruned unused @react-pdf/renderer named imports
    • removed unused imports (middleware, nextConnect, fs)
    • removed unused locals (casefolderID, representationType, repRaiser, localeSelect, repCapacity, repType)
  • pages/api/file/generateappealpdf.js
    • pruned unused Azure storage imports to only required functions
    • pruned unused @react-pdf/renderer named imports
    • removed unused imports (middleware, nextConnect, fs, path, unused pdf templates)
    • removed unused local (caseRef)

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 53/53
    • email-handler: 12/12
    • endpoint-handler: 152/152

Follow-ups:

  • Optional: run full repo lint in a separate pass for broader non-slice hygiene now that targeted contract suite is stable.

CL-016: TASK22224 documents download contract slice

date: 2026-03-24 author: Cline scope: pages/api/documents/download/[id].js, tests/phase21/{documents-handler-contract,api-contract-slice1}.test.cjs type: change rationale: Execute next aggressive slice by standardizing document download guard behavior and bringing the route under phase21 contract coverage. impact: Improves reliability on invalid input and relay-failure paths while preserving existing user-visible fallback behavior (/filenotavailable) for download failures. status: completed

Summary:

  • pages/api/documents/download/[id].js
    • added explicit required-query guard for id and hash
    • unified fallback redirect path via constant (/filenotavailable)
    • preserved streaming download behavior and retry flow
  • added tests/phase21/documents-handler-contract.test.cjs covering:
    • missing query -> redirect contract
    • success -> attachment/content-type headers + stream pipe contract
    • relay failure -> redirect contract
  • updated combined runner (tests/phase21/api-contract-slice1.test.cjs) to include documents handler contract suite

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 53/53
    • email-handler: 12/12
    • endpoint-handler: 152/152
    • documents-handler: 3/3

Follow-ups:

  • Optional future hardening: migrate documents route onto shared respondError/respondSuccess envelope if product requirements allow replacing redirect-style fallback.

CL-017: TASK22224 endpoint legacy-comment hygiene slice

date: 2026-03-24 author: Cline scope: pages/api/endpoint/{createwatchedcases_api,getadvancedsearchpaged_api}.js type: change rationale: Complete second requested slice with low-risk maintainability cleanup by removing large obsolete commented legacy handler blocks. impact: Non-behavioral cleanup only; improves readability and reduces maintenance noise with no runtime contract changes. status: completed

Summary:

  • createwatchedcases_api.js
    • removed obsolete commented promise-chain implementation block
  • getadvancedsearchpaged_api.js
    • removed obsolete commented legacy implementation block retained below active handler

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 53/53
    • email-handler: 12/12
    • endpoint-handler: 152/152
    • documents-handler: 3/3

Follow-ups:

  • Optional further hygiene pass can target remaining oversized commented historical sections in non-sensitive handlers.

CL-018: TASK22224 nextauth notify micro-refactor

date: 2026-03-24 author: Cline scope: pages/api/auth/[...nextauth].js type: change rationale: Execute the explicitly approved auth micro-slice by replacing inline promise .catch(...) with explicit try/catch while preserving existing auth behavior. impact: Auth-sensitive non-functional refactor only; keeps current sign-in flow, template/locale routing, and error-handling semantics unchanged. status: completed

Summary:

  • pages/api/auth/[...nextauth].js
    • replaced:
      • await notifyClient.sendEmail(...).catch((error) => consoleLogger(error))
    • with explicit:
      • try { await notifyClient.sendEmail(...) } catch (error) { consoleLogger(error) }
  • preserved behavior contracts:
    • Notify failures are still logged and do not throw through auth handler
    • no changes to callback URL construction, locale/template selection, NextAuth options, session/cookies/pages config

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 53/53
    • email-handler: 12/12
    • endpoint-handler: 152/152
    • documents-handler: 3/3

Follow-ups:

  • Optional future auth hygiene (separate guarded slice): replace verbose auth console.log diagnostics with structured logger usage once production logging requirements are confirmed.

CL-019: TASK22229 P2-S1 relay forwarding pilot (proxy endpoint cluster)

date: 2026-03-24 author: Cline scope: pages/api/middleware/relayForwarding.js, pages/api/endpoint/{getwatchedcasesproxy_api,getmyrepresentationsproxy_api,getrepresentationsproxy_api,getawaitingsubmissionproxy_api}.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Start Priority-2 by piloting a shared relay forwarding helper on a bounded proxy endpoint cluster to reduce duplicated token/hash/header/axios boilerplate while preserving endpoint contracts. impact: Maintainability and consistency improvement with no intended behavioral contract changes; error code/status/message and success payload contracts remain unchanged for migrated endpoints. status: completed

Summary:

  • Added shared relay helper:
    • pages/api/middleware/relayForwarding.js
    • exports relayGet({ queryUrl, res, errorResponse, transformData })
    • centralizes token fetch, relay URL + hash composition, azureHeaders, GET execution, success/error response handling, and logging
  • Migrated pilot endpoint cluster to relayGet:
    • getwatchedcasesproxy_api.js (with existing payload transform preserved)
    • getmyrepresentationsproxy_api.js
    • getrepresentationsproxy_api.js
    • getawaitingsubmissionproxy_api.js (with existing payload transform preserved)
  • Updated endpoint contract tests to mock relayGet for migrated routes while preserving existing assertions.

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 53/53
    • email-handler: 12/12
    • endpoint-handler: 152/152
    • documents-handler: 3/3

Follow-ups:

  • P2-S2 rollout: migrate the next relay-heavy endpoint batch onto relayGet/shared forwarding utility pattern.
  • P2-S3 hardening: add shared timeout/retry config and redacted structured relay error logging policy.

CL-020: TASK22229 P2-S2 Batch 1 (account/login relay GET cluster)

date: 2026-03-24 author: Cline scope: pages/api/middleware/relayForwarding.js, pages/api/endpoint/{getaccounts_api,getemailaccountcheck_api,getpreferredlanguage_api,getlogin_api,getpersonalaccount_api,getportalloginproxy_api}.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Deliver first P2-S2 batch as a dedicated commit by migrating a bounded account/login endpoint cluster onto shared relayGet while preserving response contracts. impact: Reduced relay boilerplate and improved consistency with no intended endpoint contract changes. status: completed

Summary:

  • Extended relayGet to support optional requestOptionsBuilder for handlers requiring paged header variants.
  • Migrated Batch 1 endpoints to relayGet:
    • getaccounts_api.js
    • getemailaccountcheck_api.js
    • getpreferredlanguage_api.js
    • getlogin_api.js (uses azureHeadersPaged via requestOptionsBuilder)
    • getpersonalaccount_api.js
    • getportalloginproxy_api.js (uses azureHeadersPaged via requestOptionsBuilder)
  • Updated phase21 endpoint contract tests to mock relayGet for migrated handlers, preserving existing guard/catch/success assertions.

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 53/53
    • email-handler: 12/12
    • endpoint-handler: 152/152
    • documents-handler: 3/3

Follow-ups:

  • Continue P2-S2 with Batch 2 as next dedicated commit on this same branch.

CL-021: TASK22229 P2-S2 Batch 2 (my-portal + representation relay GET cluster)

date: 2026-03-24 author: Cline scope: pages/api/endpoint/{getmycases_api,getmyrepresentations_api,getwatchedcases_api,getawaitingsubmission_api,getrepresentations_api}.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Deliver second P2-S2 commit by migrating the next bounded portal/representation GET endpoint cluster onto shared relayGet while preserving route contracts. impact: Reduced duplicated relay boilerplate and aligned forwarding behavior with no intended response contract changes. status: completed

Summary:

  • Migrated Batch 2 endpoints to relayGet:
    • getmycases_api.js (preserved title mapping transform)
    • getmyrepresentations_api.js
    • getwatchedcases_api.js (preserved watched-case projection transform)
    • getawaitingsubmission_api.js (preserved title mapping transform)
    • getrepresentations_api.js
  • Updated phase21 endpoint contract tests for migrated handlers by mocking relayGet in guard/catch test paths.

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 53/53
    • email-handler: 12/12
    • endpoint-handler: 152/152
    • documents-handler: 3/3

Follow-ups:

  • Continue P2-S2 with Batch 3 as the next dedicated commit on this branch.

CL-022: TASK22229 P2-S2 Batch 3 (case/event relay GET cluster)

date: 2026-03-24 author: Cline scope: pages/api/endpoint/{getcase_api,getcasebyid_api,getincidentbyid_api,getsipsevents_api}.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Deliver third P2-S2 commit by migrating an additional bounded case/event endpoint cluster onto shared relayGet while preserving route contracts. impact: Further relay boilerplate reduction and consistent forwarding behavior with no intended contract changes. status: completed

Summary:

  • Migrated Batch 3 endpoints to relayGet:
    • getcase_api.js (preserved @odata.nextLink normalization)
    • getcasebyid_api.js (preserved @odata.nextLink normalization and array-wrapped success payload)
    • getincidentbyid_api.js
    • getsipsevents_api.js
  • Updated phase21 endpoint contract tests to mock relayGet for migrated handlers in guard/catch paths.

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 53/53
    • email-handler: 12/12
    • endpoint-handler: 152/152
    • documents-handler: 3/3

Follow-ups:

  • Continue P2-S2 with next bounded batch (e.g. search/listing cluster) as a separate commit if required.

CL-023: TASK22229 P2-S2 Batch 6 (profile/form/document-history/portal-module relay GET cluster)

date: 2026-03-24 author: Cline scope: pages/api/endpoint/{getpersonalaccount_api,getformdata_api,getsearchdocumenthistory_api,getsearchdocumenthistorypaged_api,getportalmoduledetails_api}.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Execute requested Batch 6 by migrating the next bounded set of read-only endpoint handlers to shared relayGet while preserving existing request validation and response/error contracts. impact: Reduced endpoint relay boilerplate and improved consistency; no intended behavior contract changes. status: completed

Summary:

  • Migrated Batch 6 endpoints to shared relayGet:
    • getpersonalaccount_api.js
    • getformdata_api.js
    • getsearchdocumenthistory_api.js (uses requestOptionsBuilder with azureHeadersPaged)
    • getsearchdocumenthistorypaged_api.js (uses requestOptionsBuilder with azureHeadersPaged)
    • getportalmoduledetails_api.js
  • Preserved all existing required-query validation guards and existing error codes/messages.
  • Updated Phase 21 endpoint contract tests to inject relayGet mocks for migrated handlers (validation path, catch path, and success path where applicable).

Validation:

  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (152/152)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded batch: migrate remaining legacy relay GET handlers in advanced/basic search-document/detail clusters that still use direct axios relay patterns.

CL-024: TASK22229 P2-S2 Batch 7 (search-document + portal-proxy relay GET cluster)

date: 2026-03-24 author: Cline scope: pages/api/endpoint/{getportalmoduledetailsproxy_api,getsearchdocumentTypes_api,getsearchdocumentdetails_api,getsearchdocumentdetailspaged_api,getappealpdfdocuments_api}.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Continue P2-S2 in bounded commits by migrating a coherent search-document/portal-proxy GET cluster onto shared relay forwarding while preserving existing validation, transforms, and error contracts. impact: Reduced duplicate relay plumbing and improved consistency in search-document handlers; no intended API contract changes. status: completed

Summary:

  • Migrated Batch 7 endpoints to shared relayGet:
    • getportalmoduledetailsproxy_api.js
    • getsearchdocumentTypes_api.js
    • getsearchdocumentdetails_api.js
    • getsearchdocumentdetailspaged_api.js
    • getappealpdfdocuments_api.js
  • Preserved existing guard behavior and error response contracts.
  • Preserved endpoint-specific transform behavior via transformData, including:
    • grouped search-document type payloads
    • document hashlink enrichment and published-date normalization
    • @odata.nextLink normalization where present
    • appeal PDF document name projection
  • Updated phase21 endpoint tests to mock relayGet for migrated handlers in validation/catch/success paths as applicable.

Validation:

  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (152/152)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Continue with next bounded P2-S2 batch from remaining legacy GET endpoints (e.g., advanced/basic paged search-detail clusters and related non-migrated proxies).

CL-025: TASK22229 P2-S2 Batch 8 (basic search details + DNS details relay GET cluster)

date: 2026-03-24 author: Cline scope: pages/api/endpoint/{getbasicsearchpaged_api,getbasicsearchdetails_api,getbasicsearchdetailspaged_api,getbasicdnssearchdetails_api,getbasicdnssearchdetailspaged_api}.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Continue the P2-S2 relay migration using bounded commits by moving the remaining basic-search-details and DNS-details GET handlers to shared relayGet, preserving existing guards and response contracts. impact: Reduced duplicated relay plumbing and more consistent forwarding behavior across search-detail handlers; no intended API contract changes. status: completed

Summary:

  • Migrated Batch 8 endpoints to shared relayGet:
    • getbasicsearchpaged_api.js
    • getbasicsearchdetails_api.js
    • getbasicsearchdetailspaged_api.js
    • getbasicdnssearchdetails_api.js
    • getbasicdnssearchdetailspaged_api.js
  • Preserved existing validation guards and error contracts.
  • Preserved existing endpoint-specific transforms:
    • @odata.nextLink normalization in paged responses
    • flattened ticketnumber enrichment for search detail handlers
  • Updated phase21 endpoint contract tests to inject relayGet mocks for migrated handlers in validation and catch-path tests.

Validation:

  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (152/152)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Continue P2-S2 with next bounded batch from remaining legacy GET endpoints (currently: createwatchedcases_api, deletewatchedcasesproxy_api, getadvancedsearch_api, getadvancedsearchpaged_api, getappealid_api, getbasicdnssearchpaged_api, getbasicpartsaveddetails_api, getbasicsearch_by_address_api, getbasicsearch_by_lparref_api, getdnscoords_api, getdnslist_api, getmylpacases_api, getportallogin_api).

CL-026: TASK22229 P2-S2 Batch 9 (appeal-id + DNS list/paged + part-saved relay GET cluster)

date: 2026-03-24 author: Cline scope: pages/api/endpoint/{getappealid_api,getbasicdnssearchpaged_api,getbasicpartsaveddetails_api,getdnslist_api}.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Continue bounded P2-S2 relay migration by moving another coherent set of read-only handlers to shared relayGet, preserving existing guards, transforms, and error contracts. impact: Reduced duplicated relay plumbing and improved consistency for DNS list/paged and appeal detail lookup endpoints; no intended API contract changes. status: completed

Summary:

  • Migrated Batch 9 endpoints to shared relayGet:
    • getappealid_api.js
    • getbasicdnssearchpaged_api.js
    • getbasicpartsaveddetails_api.js
    • getdnslist_api.js
  • Preserved existing input validation guards and endpoint-specific error contracts.
  • Preserved endpoint-specific transform behavior (@odata.nextLink normalization in DNS list/paged handlers).
  • Updated phase21 endpoint contract tests to inject relayGet mocks for these migrated handlers in guard and catch-path assertions.

Validation:

  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (152/152)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Continue P2-S2 with next bounded batch from remaining legacy relay GET endpoints (createwatchedcases_api, deletewatchedcasesproxy_api, getadvancedsearch_api, getadvancedsearchpaged_api, getbasicsearch_by_address_api, getbasicsearch_by_lparref_api, getdnscoords_api, getmylpacases_api, getportallogin_api).

CL-027: TASK22229 P2-S2 Batch 10 (portal login + my LPA cases relay GET pair)

date: 2026-03-24 author: Cline scope: pages/api/endpoint/{getportallogin_api,getmylpacases_api}.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Continue bounded relay migration by moving two remaining high-use portal retrieval endpoints to shared relayGet while preserving existing hash/lookup guards and response contracts. impact: Further relay boilerplate reduction and consistent forwarding behavior in portal login/LPA case retrieval flows; no intended API contract changes. status: completed

Summary:

  • Migrated Batch 10 endpoints to shared relayGet:
    • getportallogin_api.js (preserved hash validation guard and error contract)
    • getmylpacases_api.js (preserved LPA lookup/404 guard and title transform)
  • Preserved endpoint-specific behavior:
    • getportallogin_api: raw + encoded hash candidate validation before relay call
    • getmylpacases_api: JSONPath LPA lookup with LPA_NOT_FOUND handling and pinswg_title enrichment transform
  • Updated phase21 endpoint contract tests to inject relayGet mocks for migrated handlers in validation/catch/success paths.

Validation:

  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (152/152)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Continue P2-S2 with the remaining legacy relay GET candidates (createwatchedcases_api, deletewatchedcasesproxy_api, getadvancedsearch_api, getadvancedsearchpaged_api, getbasicsearch_by_address_api, getbasicsearch_by_lparref_api, getdnscoords_api).

CL-028: TASK22229 P2-S2 Batch 11 (advanced-search-paged relay GET)

date: 2026-03-24 author: Cline scope: pages/api/endpoint/getadvancedsearchpaged_api.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Continue bounded relay migration by moving the remaining advanced-search paged GET handler to shared relayGet while preserving query validation and response contracts. impact: Reduced duplicated relay plumbing and aligned advanced paged search route with shared forwarding; no intended API contract changes. status: completed

Summary:

  • Migrated getadvancedsearchpaged_api.js to shared relayGet.
  • Preserved existing guard behavior and error contracts:
    • SEARCH_STRING_REQUIRED
    • ORDER_BY_REQUIRED
    • FIELD_SORT_REQUIRED
    • SHOW_NUMBER_OF_RECORDS_REQUIRED
    • INVALID_SEARCH_STRING
    • ADVANCED_SEARCH_PAGED_FETCH_FAILED
  • Preserved endpoint-specific transform behavior (@odata.nextLink normalization via transformData).
  • Updated phase21 endpoint contract tests to inject relayGet mocks for getadvancedsearchpaged_api guard/catch assertions.

Validation:

  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (152/152)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Continue P2-S2 with remaining legacy relay GET candidates (createwatchedcases_api, deletewatchedcasesproxy_api, getadvancedsearch_api, getbasicsearch_by_address_api, getbasicsearch_by_lparref_api, getdnscoords_api).

CL-029: TASK22229 P2-S2 Batch 12 (remaining relay GET candidates)

date: 2026-03-24 author: Cline scope: pages/api/middleware/relayForwarding.js, pages/api/endpoint/{createwatchedcases_api,deletewatchedcasesproxy_api,getadvancedsearch_api,getbasicsearch_by_address_api,getbasicsearch_by_lparref_api,getdnscoords_api}.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Complete Batch 12 by migrating the remaining listed legacy relay GET candidates to shared forwarding helpers while preserving endpoint-specific validation, transforms, and error contracts. impact: Consolidated relay GET behavior and reduced endpoint-level relay boilerplate; no intended API contract changes. status: completed

Summary:

  • Extended relay middleware with reusable helpers:
    • relayGetData now supports token reuse via transformData path
    • Added forwardGetData to support custom base URLs (used by proxy-to-local endpoint) while preserving hash behavior
  • Migrated remaining Batch 12 candidates:
    • getadvancedsearch_api.js -> relayGet + relayGetData for project-type enrichment loop
    • getdnscoords_api.js -> relayGetData for both DNS + SIPS source pulls
    • getbasicsearch_by_lparref_api.js -> relayGetData
    • getbasicsearch_by_address_api.js -> relayGetData for appeal-type and incident fetches
    • deletewatchedcasesproxy_api.js -> forwardGetData with BASE_URL
    • createwatchedcases_api.js -> relayGetData for record-exists pre-check (POST/PATCH upsert flow preserved)
  • Preserved endpoint-specific guard and error contracts (including status codes such as 500 for BASIC_SEARCH_BY_ADDRESS_FETCH_FAILED).
  • Updated phase21 endpoint contract tests for migrated handlers to mock relayGetData / forwardGetData where appropriate.

Validation:

  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (152/152)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Batch 12 completes the previously listed remaining P2-S2 relay GET candidates.

CL-030: TASK22236 P2-S3 relay forwarding hardening (timeouts, retries, structured redacted logs)

date: 2026-03-24 author: Cline scope: pages/api/middleware/relayForwarding.js, tests/phase21/relay-forwarding-hardening.test.cjs type: change rationale: Begin P2-S3 by hardening shared relay forwarding behavior with bounded timeout/retry controls and structured redacted operational logging, reducing transient failure impact while preserving endpoint contracts. impact: Improves resilience/observability for relay GET traffic; endpoint success/error contracts remain unchanged because caller handlers still manage response envelopes. status: completed

Summary:

  • Hardened forwardGetData in relayForwarding.js with:
    • configurable timeout (RELAY_TIMEOUT_MS, default 8000ms)
    • bounded retries (RELAY_RETRY_MAX, default 2)
    • exponential backoff with cap (RELAY_RETRY_BASE_DELAY_MS, RELAY_RETRY_MAX_DELAY_MS)
    • retry eligibility for transient statuses/codes (408/429/5xx, selected network timeout/reset codes)
  • Added structured, redacted operational relay logs:
    • relay_request_retrying
    • relay_request_failed
  • Preserved compatibility behaviors:
    • existing token/header/hash handling
    • optional appendHash and custom requestOptionsBuilder
    • endpoint-level relayGet error response semantics
  • Added focused Phase 21 hardening tests:
    • retries on retryable status and succeeds
    • does not retry non-retryable status
    • applies timeout and respects appendHash=false

Validation:

  • node tests/phase21/relay-forwarding-hardening.test.cjs -> pass (3/3)
  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (152/152)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next hardening increment: introduce endpoint-specific retry overrides for write paths (if future non-GET use is introduced) to keep retry policy conservative by operation type.

CL-031: TASK22236 P2-S3 Batch 2A policy tightening (retry classification, config clamping, log de-dup)

date: 2026-03-24 author: Cline scope: pages/api/middleware/relayForwarding.js, tests/phase21/relay-forwarding-hardening.test.cjs type: change rationale: Execute P2-S3 Batch 2A by tightening relay retry policy and operational safety bounds while preventing duplicate error noise between relay-layer and endpoint-layer logging. impact: Stronger resilience and cleaner observability with no endpoint contract changes; retry behavior is now explicitly conservative for deterministic client/auth failures. status: completed

Summary:

  • Updated relay retry policy:
    • explicitly non-retryable statuses: 400, 401, 403, 404, 422
    • retries still allowed for transient classes (408, 429, 5xx) and selected transport error codes
    • any other explicit numeric HTTP status now treated as non-retryable by default
  • Added runtime-safe config clamping for relay knobs:
    • timeout clamped to 100..30000ms
    • retries clamped to 0..4
    • retry delays clamped to 0..5000ms
    • both env-derived and per-call numeric overrides are sanitized
  • Reduced duplicate logging noise:
    • non-retry terminal relay failures are marked as already logged in middleware
    • relayGet catch now skips consoleLogger when relay layer has already emitted a structured log
  • Expanded relay hardening tests:
    • no retry on 401
    • duplicate logging suppression path through relayGet
    • env-value clamping behavior for timeout bounds

Validation:

  • node tests/phase21/relay-forwarding-hardening.test.cjs -> pass (6/6)
  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (152/152)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Batch 2B: update memory/context docs with canonical relay hardening policy, env knobs, and rollback/tuning guidance.

CL-032: TASK22239 governance + operational rollout playbook bundle (relay hardening)

date: 2026-03-24 author: Cline scope: context/runbook.md, context/integration-map.md, memory-bank/{change-log,decisions,patterns}.md type: change rationale: Deliver a combined governance and operational rollout slice after P2-S3 code hardening so relay policy changes have explicit merge gates, smoke matrix coverage, and rollback/monitoring discipline. impact: Improves release safety and auditability for relay policy updates without changing runtime endpoint behavior. status: completed

Summary:

  • Added a dedicated Relay Hardening Rollout Playbook section to context/runbook.md covering:
    • pre-merge governance gate checklist
    • non-prod smoke matrix for deterministic and transient failure classes
    • progressive rollout guidance
    • Day 1/Day 3 monitoring checks
    • fast mitigation/rollback sequence
  • Added relay policy governance references in architecture/integration context docs to make rollout checks discoverable for future contributors.
  • Recorded governance baseline updates in memory-bank:
    • decision entry for relay policy-change governance gate
    • reusable pattern entry for required rollout/smoke/rollback workflow

Validation:

  • Documentation consistency review across context/runbook.md, context/integration-map.md, and memory-bank entries.

Follow-ups:

  • Optional next iteration: add a PR template block in Azure DevOps mirroring the runbook governance gate checklist.

CL-033: TASK22242 relay telemetry enrichment (lifecycle events + correlation fields)

date: 2026-03-24 author: Cline scope: pages/api/middleware/relayForwarding.js, tests/phase21/relay-forwarding-hardening.test.cjs type: change rationale: Add richer relay observability so operations can correlate retries and outcomes per request and track latency/status patterns without changing endpoint contracts. impact: Improves operational diagnostics and trend analysis for relay traffic while preserving existing API behavior. status: completed

Summary:

  • Enriched relay middleware telemetry with request lifecycle events:
    • relay_request_started
    • relay_request_retrying
    • relay_request_succeeded
    • relay_request_failed
  • Added shared telemetry fields for correlation and analysis:
    • relayRequestId (per request correlation id)
    • attemptsMade, retryCountUsed, remainingRetries
    • elapsedMs
    • statusClass (2xx/4xx/5xx style buckets)
    • resolved runtime knobs included at start event
  • Kept existing retry policy and endpoint response contracts unchanged.
  • Expanded phase21 relay hardening tests to assert telemetry behavior:
    • started/retrying/succeeded event presence
    • failed event telemetry fields
    • stable relayRequestId across lifecycle events for one request

Validation:

  • node tests/phase21/relay-forwarding-hardening.test.cjs -> pass (7/7)
  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (152/152)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next step: map these lifecycle fields into central dashboards/alerts (retry rate, status-class distribution, p95 elapsedMs).

CL-034: TASK22242 per-endpoint relay overrides + idempotency-aware retry gating

date: 2026-03-24 author: Cline scope: pages/api/middleware/relayForwarding.js, tests/phase21/relay-forwarding-hardening.test.cjs type: change rationale: Deliver the next functional relay enhancement by enabling route-level retry tuning while adding safe-by-default retry gating for non-idempotent methods. impact: Improves control and safety of relay retries without breaking existing endpoint contracts. status: completed

Summary:

  • Added relay policy override support (relayPolicy) to shared relay helpers (relayGet, relayGetData, forwardGetData):
    • per-call override of timeoutMs, maxRetries, retryBaseDelayMs, retryMaxDelayMs
    • optional method override via relayPolicy.method
  • Added idempotency-aware retry gating scaffolding:
    • retries allowed by default only for idempotent methods (GET, HEAD, OPTIONS)
    • non-idempotent retry behavior controlled by:
      • env flag RELAY_ALLOW_NON_IDEMPOTENT_RETRIES (default false)
      • per-call override relayPolicy.allowNonIdempotentRetries
  • Extended relay telemetry fields to include method and non-idempotent policy posture in start/failure/retry events.
  • Preserved existing route behavior:
    • existing GET endpoint flows continue to use retries per configured bounds
    • no endpoint response contract changes

Validation:

  • node tests/phase21/relay-forwarding-hardening.test.cjs -> pass (10/10)
  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (152/152)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Future non-GET relay adoption should explicitly opt in/out per route using relayPolicy and include targeted negative-path tests.

CL-035: TASK22242 apply relayPolicy overrides across broader endpoint cluster

date: 2026-03-24 author: Cline scope: pages/api/endpoint/{getaccounts_api,getemailaccountcheck_api,getpreferredlanguage_api,getpersonalaccount_api,getlogin_api,getbasicsearch_api,getbasicsearchpaged_api,getadvancedsearch_api,getadvancedsearchpaged_api}.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Expand practical adoption of per-endpoint relay policy tuning so high-traffic account/login/search handlers explicitly declare timeout/retry posture rather than relying only on global defaults. impact: Better operational control and predictable retry behavior per endpoint cluster, with no API contract changes. status: completed

Summary:

  • Added explicit relayPolicy usage to a broader endpoint set:
    • account/login: getaccounts, getemailaccountcheck, getpreferredlanguage, getpersonalaccount, getlogin
    • search: getbasicsearch, getbasicsearchpaged, getadvancedsearch, getadvancedsearchpaged
  • Applied conservative policy profiles by flow:
    • login endpoint (getlogin): no retries (maxRetries: 0) and tighter timeout
    • account lookup endpoints: low retry posture (maxRetries: 1)
    • search endpoints: bounded retry posture (maxRetries: 2) for transient resilience
  • Kept method explicit as GET in policy for clarity and future-proofing.
  • Extended phase21 endpoint tests with relayPolicy propagation assertions:
    • getaccounts relayPolicy pass-through
    • getlogin strict relayPolicy pass-through
    • getbasicsearchpaged relayPolicy pass-through

Validation:

  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (155/155)
  • node tests/phase21/relay-forwarding-hardening.test.cjs -> pass (10/10)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next slice: apply relayPolicy declarations to remaining relayGet endpoints in coherent batches (portal module/documents/DNS groups) and standardize policy presets in one shared constants module.

CL-036: TASK22242 portal-facing relayPolicy parity (login + module endpoints)

date: 2026-03-24 author: Cline scope: pages/api/endpoint/{getportallogin_api,getportalloginproxy_api,getportalmoduledetails_api,getportalmoduledetailsproxy_api}.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Complete the next practical relay policy rollout slice by bringing portal-facing login/module endpoints onto explicit per-endpoint timeout/retry posture. impact: Improves predictability and operational tuning for portal-facing relay GET flows without changing API contracts. status: completed

Summary:

  • Added explicit relayPolicy for four portal-facing endpoints:
    • getportallogin_api -> strict/no-retry profile (maxRetries: 0, tighter timeout)
    • getportalloginproxy_api -> low-retry account lookup profile (maxRetries: 1)
    • getportalmoduledetails_api -> bounded read profile (maxRetries: 2)
    • getportalmoduledetailsproxy_api -> bounded read profile (maxRetries: 2)
  • Kept method explicit (GET) in each endpoint policy object.
  • Extended phase21 endpoint tests with relayPolicy propagation assertions for all four endpoints.

Validation:

  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (159/159)
  • node tests/phase21/relay-forwarding-hardening.test.cjs -> pass (10/10)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next slice: extract shared relay policy presets into constants to reduce duplication and enforce profile consistency across remaining relayGet endpoints.

CL-037: TASK22242 portal/my-cases relayPolicy parity (my portal + representations)

date: 2026-03-24 author: Cline scope: pages/api/endpoint/{getmycases_api,getmyrepresentations_api,getwatchedcases_api,getawaitingsubmission_api,getrepresentations_api}.js, tests/phase21/endpoint-handler-contract.test.cjs type: change rationale: Continue the branch-by-branch relay policy rollout by applying explicit policy posture to core my-portal retrieval endpoints. impact: Improves consistency and operational predictability of relay behavior for portal case/representation listing flows without changing endpoint contracts. status: completed

Summary:

  • Added explicit relayPolicy declarations to:
    • getmycases_api
    • getmyrepresentations_api
    • getwatchedcases_api
    • getawaitingsubmission_api
    • getrepresentations_api
  • Applied bounded read profile across the batch:
    • method: "GET"
    • timeoutMs: 8000
    • maxRetries: 2
    • retryBaseDelayMs: 150
    • retryMaxDelayMs: 800
  • Extended phase21 endpoint contract tests with relayPolicy pass-through assertions for each endpoint.

Validation:

  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (164/164)
  • node tests/phase21/relay-forwarding-hardening.test.cjs -> pass (10/10)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next slice: extract shared relay policy presets into a constants module and reference them from all relayGet endpoints to reduce duplication.

CL-038: TASK22242 P2-S3 closure slice — shared relay policy presets extraction

date: 2026-03-24 author: Cline scope: pages/api/middleware/relayPolicyPresets.js, pages/api/endpoint/{getaccounts_api,getemailaccountcheck_api,getpreferredlanguage_api,getpersonalaccount_api,getlogin_api,getportallogin_api,getportalloginproxy_api,getbasicsearch_api,getbasicsearchpaged_api,getadvancedsearch_api,getadvancedsearchpaged_api,getportalmoduledetails_api,getportalmoduledetailsproxy_api,getmycases_api,getmyrepresentations_api,getawaitingsubmission_api,getrepresentations_api,getwatchedcases_api}.js, tests/phase21/_shared.cjs type: change rationale: Complete the planned P2-S3 final maintainability slice by centralizing repeated relay timeout/retry policy objects into shared presets used consistently across all targeted relayGet endpoints. impact: Eliminates duplicated policy literals, reduces drift risk, and preserves endpoint contracts/behavior by reusing equivalent policy values. status: completed

Summary:

  • Added new middleware constants module:
    • pages/api/middleware/relayPolicyPresets.js
    • RELAY_POLICY_STRICT_LOGIN
    • RELAY_POLICY_LOOKUP
    • RELAY_POLICY_BOUNDED_READ
    • RELAY_POLICY_SEARCH_PAGED
  • Refactored 18 relayGet endpoints to import and use shared presets instead of inline relayPolicy object literals:
    • lookup profile: getaccounts, getemailaccountcheck, getpreferredlanguage, getpersonalaccount, getportalloginproxy
    • strict login profile: getlogin, getportallogin
    • bounded read profile: getbasicsearch, getportalmoduledetails, getportalmoduledetailsproxy, getmycases, getmyrepresentations, getawaitingsubmission, getrepresentations, getwatchedcases
    • search paged profile: getbasicsearchpaged, getadvancedsearch, getadvancedsearchpaged
  • Updated phase21 VM test harness (tests/phase21/_shared.cjs) to inject preset constants so endpoint contract tests continue to execute with import-stripped modules.

Validation:

  • node tests/phase21/endpoint-handler-contract.test.cjs -> pass (164/164)
  • node tests/phase21/relay-forwarding-hardening.test.cjs -> pass (10/10)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • P2-S3 planned slices are now complete; no further mandatory relay policy rollout slices remain for this stream.

CL-039: TASK22260 actions façade increment — shared relay client extraction

date: 2026-03-25 author: Cline scope: actions/clients/{relayClient,index}.js, actions/services/{accountDirectService,portalDirectService,documentDirectService}.js, actions/index.js, actions/clients/README.md type: change rationale: Continue Priority 1 façade decomposition by extracting duplicated hash-signing relay helper logic into a dedicated client module while preserving existing service/public export contracts. impact: Reduces duplication and drift risk in security-sensitive relay signing helper logic without changing call-site behavior. status: completed

Summary:

  • Added a new shared client wrapper:
    • actions/clients/relayClient.js exporting buildHashedQueryUrl
  • Added actions/clients/index.js barrel and exposed client exports via actions/index.js.
  • Updated direct services to consume shared relay client helper instead of duplicating local helper implementations:
    • actions/services/accountDirectService.js
    • actions/services/portalDirectService.js
    • actions/services/documentDirectService.js
  • Updated actions/clients/README.md to reflect the now-implemented relay client extraction and future incremental client split path.

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • Verified no remaining duplicated local buildHashedQueryUrl definitions across actions/services/*DirectService.js

Follow-ups:

  • Optional next TASK22260 increment: extract common axios invocation helpers into dedicated clients (endpointClient, fileClient, notifyClient) while keeping actions/index.js API stable.

Addendum (same TASK22260 slice):

  • Added shared endpointClient with getJson and requestJson helpers (actions/clients/endpointClient.js) and exported it via actions/clients/index.js.
  • Migrated additional direct services to consume shared endpoint client helpers:
    • actions/services/notifyDirectService.js (POST via requestJson)
    • actions/services/integrationDirectService.js (POST via requestJson)
    • actions/services/adminDirectService.js (GET flows via getJson)
  • Updated actions/clients/README.md to include endpointClient in current extracted clients.

CL-040: TASK22260 next slice — reference-data direct service endpointClient adoption

date: 2026-03-25 author: Cline scope: actions/services/referenceDataDirectService.js, actions/clients/README.md type: change rationale: Continue the incremental façade/client adoption stream by migrating another bounded direct-service module to shared endpoint request helpers. impact: Reduces axios boilerplate and centralizes JSON extraction behavior for reference-data requests without changing public call signatures. status: completed

Summary:

  • Migrated actions/services/referenceDataDirectService.js from direct axios.get(...).then(res => res.data) patterns to shared getJson(...) helper from actions/clients/endpointClient.
  • Preserved existing error behavior:
    • logAndReturnEmptyValueErrorResponse for appeals/project/LPA fetches
    • consoleLogger catch handling for form/mandatory/picklist/notice fetches
  • Updated actions/clients/README.md usage notes to include reference-data service reuse.

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Next optional bounded slice: adopt getJson/requestJson for selected low-risk read paths in searchDirectService or caseDirectService while preserving per-function error semantics.

CL-041: TASK22260 next slice — account direct service endpointClient adoption

date: 2026-03-25 author: Cline scope: actions/services/accountDirectService.js type: change rationale: Continue incremental façade migration by moving account direct-service request plumbing onto shared endpoint client helpers while preserving existing error-return behavior contracts. impact: Reduces duplicated axios response extraction boilerplate and aligns account service request handling with the emerging client-layer pattern. status: completed

Summary:

  • Refactored actions/services/accountDirectService.js to consume shared endpoint client helpers:
    • getJson(...) for GET requests
    • requestJson(...) for config-based POST requests
  • Kept existing relay hash-signing behavior unchanged via buildHashedQueryUrl from relayClient.
  • Preserved existing catch-path semantics, including:
    • logging with consoleLogger
    • returning JSON.stringify(error) in portal login functions
    • returning error.response in preferred-language failure path

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: adopt endpoint client helpers in selected portalDirectService GET/POST helper paths while preserving delete/hash flow semantics.

CL-042: TASK22260 next slice — portal direct service partial endpointClient adoption

date: 2026-03-25 author: Cline scope: actions/services/portalDirectService.js type: change rationale: Continue phased client-layer adoption by migrating low-risk portal direct-service read/create paths to shared endpoint request helpers while leaving hash-sensitive delete/message flows unchanged. impact: Reduces duplicated axios response extraction on high-traffic portal retrieval paths and keeps hashed delete/message semantics stable. status: completed

Summary:

  • Refactored selected portalDirectService functions to use shared endpoint client helpers:
    • getJson(...) for read/listing routes (getMyCases, getMyInvolvements, getMyLPACases, representations, watched, awaiting submission variants)
    • requestJson(...) for createWatchedCases
  • Preserved existing hash/delete/message flow implementations (deleteMyRepresentations, deleteWatchedCases, completion message functions) using existing axios + relay signing behavior.
  • Preserved existing catch-path logging behavior for all migrated functions.

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: migrate remaining non-hash POST helpers in portalDirectService (setRepInvolvment, setCaseInvolvment) to requestJson for full internal consistency.

CL-043: TASK22260 next slice — portal involvement helper endpointClient completion

date: 2026-03-25 author: Cline scope: actions/services/portalDirectService.js type: change rationale: Complete the next bounded internal-consistency slice by migrating remaining non-hash portal involvement POST helpers to shared endpoint client request plumbing. impact: Aligns portal service POST helper internals with established requestJson usage while preserving route semantics and error handling. status: completed

Summary:

  • Migrated remaining portal involvement helper POST functions to shared endpoint client:
    • setRepInvolvment
    • setCaseInvolvment
  • Both now use requestJson(config) while preserving existing payload shape, endpoint URLs, and catch-path logging behavior.
  • No changes made to hash-sensitive delete/message pathways.

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: begin selective requestJson adoption for remaining config-based calls in documentDirectService where hash flow is already centralized.

CL-044: TASK22260 next slice — document direct service low-risk getJson adoption

date: 2026-03-25 author: Cline scope: actions/services/documentDirectService.js type: change rationale: Continue incremental façade/client rollout by migrating low-risk document service GET wrappers that already return JSON and do not alter hash-signing semantics. impact: Reduces duplicated axios .get(...).then(res => res.data) boilerplate and aligns document retrieval helpers with shared endpoint client usage. status: completed

Summary:

  • Added getJson usage in selected document service helpers:
    • getRepsFromBlobProxy
    • getAwaitingSubmissionFromBlobProxy
    • getFilesFromBlobproxy
    • getFilesFromBlobHashed
    • getProgressFromBlob
    • createContainerProxy
  • Preserved existing behavior contracts:
    • same query composition and hash query fragments
    • same catch-path logging and return conventions (including JSON string return in createContainerProxy error path)
  • Left hash-sensitive delete/upload/generation flows unchanged in this slice.

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: migrate selected requestJson-eligible upload/generation helpers in documentDirectService (non-download paths) while preserving multipart/blob behavior.

CL-045: TASK22260 next slice — document direct service requestJson adoption (uploads + PDF generation)

date: 2026-03-25 author: Cline scope: actions/services/documentDirectService.js type: change rationale: Continue bounded client migration by moving config-based multipart/PDF POST helpers in document service to shared requestJson while preserving hashed URL generation and responseType behavior. impact: Reduces repeated axios config execution boilerplate and aligns document POST helper internals with shared endpoint client conventions. status: completed

Summary:

  • Migrated selected config-based document helper flows from axios(config) + res.data to requestJson(config):
    • uploadFiles
    • uploadSingleFile
    • uploadRepFiles
    • generateRepPDF
    • generateAppealPDF
  • Preserved behavior-critical aspects:
    • hashed URL creation via buildHashedQueryUrl(...)
    • multipart headers for upload flows
    • conditional responseType: "blob" for download PDF option paths
    • existing catch-path logging semantics
  • Left delete/download/hash-sensitive GET helpers unchanged in this slice.

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: evaluate remaining legacy direct axios.get(...).then(res.data) helpers in documentDirectService (getAwaitingSubmissionFromBlob, getRepsFromBlob, getFilesFromBlob) for safe migration while preserving signed hash path behavior.

CL-046: TASK22260 next slice — document direct service remaining signed GET helper migration

date: 2026-03-25 author: Cline scope: actions/services/documentDirectService.js type: change rationale: Complete the remaining low-risk read helper migration in document service by replacing final direct axios.get(...).then(res.data) patterns with shared getJson while retaining hash/signature query construction. impact: Finishes consistency pass for JSON-returning document read helpers and reduces duplicated response extraction logic. status: completed

Summary:

  • Migrated the remaining signed document read helpers to getJson(...):
    • getAwaitingSubmissionFromBlob
    • getRepsFromBlob
    • getFilesFromBlob
  • Preserved existing behavior:
    • same BASE_URL and hash query composition via hashAPIPath(...)
    • same catch-path logging behavior (consoleLogger)
    • no changes to delete/download flows or hash-signing helper usage in mutation paths

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: targeted hygiene in documentDirectService to isolate remaining non-migrated special-case flows (downloadBlob, delete helpers) and assess if any shared client abstraction is beneficial without altering behavior.

CL-047: TASK22260 next slice — case direct service low-risk getJson adoption

date: 2026-03-25 author: Cline scope: actions/services/caseDirectService.js type: change rationale: Continue incremental endpoint client rollout with a bounded low-risk set of case service GET helpers that already return direct JSON payloads and have simple logging-only catch paths. impact: Reduces repeated axios GET/response extraction boilerplate and aligns additional case retrieval helpers with shared client conventions. status: completed

Summary:

  • Added getJson usage in selected caseDirectService helper functions:
    • getAppealPDFDocs
    • getPortalModuleDetails
    • getPortalModuleDetailsProxy
  • Preserved existing behavior contracts:
    • same request URL/query construction
    • same catch-path logging via consoleLogger
    • no change to handlers with bespoke error-return contracts (getAppealPDFDocument) or other non-targeted flows.

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: migrate additional safe case service GET helpers that currently use axios.get(...).then(res.data) to getJson where custom catch behavior is compatible.

CL-048: TASK22260 next slice — case direct service GET cluster expansion

date: 2026-03-25 author: Cline scope: actions/services/caseDirectService.js type: change rationale: Continue phased endpoint client adoption by migrating another bounded set of case service GET helpers that already use shared catch handling (logAndReturnResponse). impact: Further reduces duplicated axios GET/response extraction boilerplate while preserving existing error-handling contracts for migrated paths. status: completed

Summary:

  • Migrated additional case retrieval helpers from axios.get(...).then(res.data) to getJson(...):
    • getCaseMessage
    • getIncidentbyID
    • getIsPublishedbyID
    • getPartSavedAppeal
    • getSIPSEvents
    • getSIPSMedia
  • Preserved behavior contracts:
    • unchanged URLs/query parameter composition
    • unchanged catch behavior via logAndReturnResponse
    • left non-targeted/bespoke flows untouched (getAppealID, create/update/patch operations, and error-response-specialized helpers)

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: evaluate remaining GET helpers in caseDirectService with custom catches (getCase, getCaseByID, getAppealPDFDocument) for selective migration where return-shape contracts remain stable.

CL-049: TASK22260 next slice — case direct service remaining GET helper migration

date: 2026-03-25 author: Cline scope: actions/services/caseDirectService.js type: change rationale: Complete the remaining safe GET-helper client migration in case service by moving custom-catch read functions to getJson while preserving their existing return-shape/error handling behavior. impact: Removes remaining direct axios GET response-extraction boilerplate in case read helpers and completes endpointClient read-path consistency for this service subset. status: completed

Summary:

  • Migrated remaining targeted case read helpers from direct axios.get(...).then(res.data) to getJson(...):
    • getCase
    • getCaseByID
    • getAppealPDFDocument
  • Preserved behavior contracts:
    • unchanged request URL/query construction
    • unchanged catch semantics:
      • getCase / getCaseByID still log via consoleLogger
      • getAppealPDFDocument still logs and returns error.response on failure
    • left non-targeted POST/update/create/patch flows unchanged.

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: evaluate config-based POST helpers in caseDirectService (createNewCase, createNewCaseBlob, updateCase, updateCaseBlob) for selective requestJson(...) adoption while preserving existing side effects and error contracts.

CL-050: TASK22260 next slice — case direct service POST helper requestJson adoption

date: 2026-03-25 author: Cline scope: actions/services/caseDirectService.js type: change rationale: Continue bounded client-layer migration by moving config-based case service POST helpers from direct axios(config) usage to shared requestJson(...) while preserving current behavior and error semantics. impact: Reduces duplicated config-execution/response-extraction boilerplate and aligns case service write-helper internals with existing endpoint client conventions. status: completed

Summary:

  • Migrated selected case service POST helpers to requestJson(config):
    • createNewCase
    • createNewCaseBlob
    • updateCase
    • updateCaseBlob
  • Preserved behavior contracts:
    • unchanged payload/query construction and URLs
    • unchanged catch-path logging via consoleLogger
    • no changes to non-targeted helper logic (getAppealID, patchCase, and already-migrated GET helpers)

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: assess patchCase and getAppealID for migration opportunities (if/when preserving their specific behavior contracts remains straightforward).

CL-051: TASK22260 next slice — case direct service final axios dependency removal

date: 2026-03-25 author: Cline scope: actions/services/caseDirectService.js type: change rationale: Complete the bounded case service migration by replacing the remaining direct axios usage (getAppealID, patchCase) with shared endpoint client reads, enabling removal of the direct axios import. impact: Finalizes endpoint client consistency in case service and removes remaining direct axios dependency from this module without changing helper contracts. status: completed

Summary:

  • Migrated remaining direct axios-based read helpers to getJson(...):
    • getAppealID
    • patchCase
  • Preserved behavior contracts:
    • getAppealID still extracts first non-underscore key value from returned entity and logs on failure
    • patchCase still swallows failure (no explicit returned error contract introduced)
  • Removed now-unused axios import from caseDirectService.

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: review other direct service modules for any remaining legacy axios import usage now that case service migration is complete.

CL-052: TASK22260 next slice — portal direct service requestJson parity for config-based reads/deletes

date: 2026-03-25 author: Cline scope: actions/services/portalDirectService.js type: change rationale: Continue bounded direct-service consistency by migrating remaining config-based portal helper calls from axios(config) to shared requestJson(...) where no signed-delete/header-specific behavior is required. impact: Reduces repeated config execution/response extraction boilerplate and improves request helper consistency in portal service while preserving behavior. status: completed

Summary:

  • Migrated selected config-based helpers to requestJson(config):
    • deleteAwaitingSubmissions
    • sendCaseCompleteMessage
    • sendCaseCompleteMessageProxy
    • sendRepCompleteMessage
  • Preserved behavior contracts:
    • unchanged query/hash composition and request methods
    • unchanged catch-path logging via consoleLogger
    • left signed delete helpers with bespoke axios/header behavior unchanged (deleteMyRepresentations, deleteWatchedCases)

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: assess whether signed-delete helpers in portalDirectService should remain explicit axios calls (for clarity on headers/hash semantics) or move to a dedicated signed-request client helper.

CL-053: Architecture documentation update — current-state assessment + prioritised roadmap

date: 2026-03-25 author: Cline scope: context/architecture.md type: change rationale: User requested the architectural assessment be documented in-project so current state, residual risks, and recommended sequencing are explicitly captured for team planning. impact: Improves documentation clarity and planning alignment; no runtime behavior changes. status: completed

Summary:

  • Added a new section to context/architecture.md:
    • Current State Assessment and Prioritised Next Steps (2026-03-25)
  • Documented:
    • strengths (governance maturity, API/relay hardening progress, façade decomposition progress)
    • residual risks/gaps (remaining direct-service inconsistency, coverage concentration, logging variance, i18n parity automation gap)
    • prioritised next steps with rationale
    • recommended execution sequencing (immediate/next/after)

Validation:

  • Manual coherence check against existing guardrails/runbook/integration-map and memory-bank context.

Follow-ups:

  • Optional: convert the prioritised steps into a sprint-ready plan (tasks, acceptance criteria, validation matrix, rollback notes).

CL-054: Branch correction — include residual duplication/axios risk slices (search + signed-delete portal flows)

date: 2026-03-25 author: Cline scope: actions/services/{portalDirectService,searchDirectService}.js, context/architecture.md type: change rationale: User requested the previously identified primary remaining technical risk slices be explicitly included on this branch, specifically residual axios/duplication in search direct service and signed-delete portal helper flows. impact: Further reduces direct-service axios boilerplate/duplication and aligns additional high-use service paths to shared endpoint client patterns while preserving existing behavior contracts. status: completed

Summary:

  • Implemented the requested risk slices on branch:
    1. Portal signed-delete flow parity (portalDirectService)
      • migrated signed delete helpers from direct axios({...}).then(res.data) to requestJson({...}):
        • deleteMyRepresentations
        • deleteWatchedCases
      • removed now-unused axios import from module
      • preserved hash-signing flow and custom headers semantics
    2. Search direct service axios reduction (searchDirectService)
      • migrated service GET helpers from direct axios.get(...).then(res.data) and axios(config) to getJson(...)
      • preserved existing catch semantics (logAndReturnResponse, logAndReturnEmptyValueErrorResponse, and explicit throw path in getSearchDocumentDetails)
      • removed direct axios import from module
  • Documentation alignment:
    • retained architecture assessment section in context/architecture.md that calls out these residual-risk slices and prioritisation.

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: assess whether a dedicated signed-request client helper should encapsulate hash + headers + method conventions to prevent future drift in remaining signed flows.

CL-055: TASK22260 next slice — document direct service signed/delete axios reduction

date: 2026-03-25 author: Cline scope: actions/services/documentDirectService.js type: change rationale: Execute next bounded risk-reduction slice by migrating remaining non-download document-service delete and signed-get helper calls away from direct axios response extraction to shared endpoint clients. impact: Further reduces duplicated axios boilerplate and aligns document service internals with shared request-client conventions while preserving existing hash/query and catch-path behavior. status: completed

Summary:

  • Migrated signed hashed delete-helper flows from direct axios({...}).then(res.data) to requestJson({...}):
    • deleteAwaitingSubmissionsFromBlob
    • deleteMyRepresentationsFromBlob
  • Migrated delete helper GET calls from axios.get(...).then(res.data) to getJson(...):
    • deleteBlob
    • deleteRepBlob
  • Preserved behavior contracts:
    • unchanged query/hash composition and endpoint URLs
    • unchanged catch-path logging with consoleLogger
  • Left downloadBlob unchanged in this slice (special-case behavior path retained for separate focused handling).

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: isolate and correct downloadBlob behavior in documentDirectService (including legacy res usage) behind an explicit, tested contract.

CL-056: TASK22260 next slice — document direct service download helper contract fix

date: 2026-03-25 author: Cline scope: actions/services/documentDirectService.js type: change rationale: Execute the next bounded follow-up by correcting the legacy downloadBlob service helper path that still relied on invalid res references and direct axios usage, aligning it to shared request client behavior. impact: Fixes a service-layer contract defect risk in document download helper and improves consistency by using shared request client patterns; no endpoint contract change. status: completed

Summary:

  • Refactored downloadBlob(containerName, blobName) in documentDirectService:
    • removed legacy direct axios.get(...).then(response => res.status(...)) pattern that referenced undefined res in service layer
    • now returns blob response data via requestJson({ method: "get", url, responseType: "blob" })
    • preserved catch-path logging (consoleLogger)
  • Removed now-unused module-level axios import from documentDirectService.

Validation:

  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: add a focused test (or integration harness assertion) around downloadBlob service return contract to prevent regression to response-object assumptions.

CL-057: TASK22260 next slice — phase7 behavioural harness compatibility update

date: 2026-03-25 author: Cline scope: tests/phase7/service-behaviour.test.cjs type: change rationale: After service-layer client migration (getJson/requestJson/buildHashedQueryUrl), phase7 behavioural harness still assumed direct axios imports only; update harness defaults so legacy behavior assertions remain executable. impact: Restores service behavioural regression coverage (12/12) without changing production runtime code. status: completed

Summary:

  • Enhanced phase7 VM loader default injections for migrated service helpers:
    • added default getJson(...) mock delegating to axios.get(...).then(res.data)
    • added default requestJson(...) mock delegating to axios(config).then(res.data)
    • added default buildHashedQueryUrl(...) mock resolving hash via /api/endpoint/gethash_api compatibility path
  • Updated notify behavior assertions to align with shared request client usage (requestJson invokes axios config-style call):
    • switched notify test handlers from axios.postHandler to axios.requestHandler
    • assertions now inspect axios.calls[0].config.{url,method,data}

Validation:

  • node tests/phase7/service-behaviour.test.cjs -> pass (12/12)

Follow-ups:

  • Optional next bounded slice: add a small shared test utility for service harness client mocks to reduce future per-file drift as façade migration continues.

CL-058: TASK22260 next slice — phase6 behavioural harness compatibility parity

date: 2026-03-25 author: Cline scope: tests/phase6/service-behaviour.test.cjs type: change rationale: Keep older phase6 behavioural harness aligned with service client-wrapper migration by adding default helper injections required by getJson/requestJson-based direct services. impact: Restores phase6 behavioural regression execution parity (8/8) with no runtime code changes. status: completed

Summary:

  • Updated loadServiceModule default context in tests/phase6/service-behaviour.test.cjs:
    • added default getJson(...) mock backed by axios.get(...).then(res.data)
    • added default requestJson(...) mock backed by axios(config).then(res.data)
  • Preserved existing test assertions and behavior semantics; this is harness-compatibility only.

Validation:

  • node tests/phase6/service-behaviour.test.cjs -> pass (8/8)

Follow-ups:

  • Optional consolidation: extract shared phase6/phase7 VM loader helpers into a single test utility to reduce duplication.

CL-059: TASK22260 next slice — shared service harness extraction (continued bounded risk-reduction)

date: 2026-03-25 author: Cline scope: tests/{serviceHarness,phase6/service-behaviour,phase7/service-behaviour}.cjs type: change rationale: Continue the bounded risk-reduction stream by removing duplicated test harness infrastructure across phase6/phase7 service behavioural suites and centralizing client-wrapper-compatible mocks. impact: Reduces test harness drift risk and keeps client-wrapper migration verification stable across multiple suites, without runtime code changes. status: completed

Summary:

  • Added shared helper module tests/serviceHarness.cjs with reusable:
    • createAxiosMock
    • createLoggerMock
    • createAxiosError
    • loadServiceModule (with default getJson/requestJson/buildHashedQueryUrl injections)
    • normalize
  • Refactored tests/phase6/service-behaviour.test.cjs to import shared harness utilities and remove duplicated local harness implementation.
  • Refactored tests/phase7/service-behaviour.test.cjs to import shared harness utilities and remove duplicated local harness implementation.

Validation:

  • node tests/phase6/service-behaviour.test.cjs -> pass (8/8)
  • node tests/phase7/service-behaviour.test.cjs -> pass (12/12)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded risk-reduction slice: evaluate whether other legacy service test suites can adopt tests/serviceHarness.cjs to standardize migration-era service mocking behavior.

CL-060: TASK22260 next slice — core token helper client-wrapper migration (continued bounded risk-reduction)

date: 2026-03-25 author: Cline scope: actions/core/token.js type: change rationale: Include the identified remaining candidate outside actions/services and continue the bounded risk-reduction stream by removing direct axios response extraction from core token retrieval. impact: Aligns token helper request execution with shared endpoint client conventions while preserving existing token caching and error-return behavior. status: completed

Summary:

  • Refactored getToken in actions/core/token.js:
    • replaced direct axios.post(...).then(res => res.data) chain with shared requestJson({...})
    • migrated function to async/await with equivalent try/catch behavior
    • retained existing semantics:
      • successful token payload cached in cache.tokenResponse
      • failures logged via consoleLogger and returned to caller
  • Removed direct axios dependency from actions/core/token.js in favor of actions/clients/endpointClient.

Validation:

  • node tests/phase21/api-contract-slice1.test.cjs -> pass
    • helper: 4/4
    • file-handler: 53/53
    • email-handler: 12/12
    • endpoint-handler: 164/164
    • documents-handler: 3/3
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded risk-reduction slice: assess whether any remaining non-service utility modules still use promise-chain axios extraction patterns and migrate them to shared clients where behavior contracts remain unchanged.

CL-061: TASK22260 next slice — core token regression coverage addition (bounded hardening)

date: 2026-03-25 author: Cline scope: tests/phase22/core-token-behaviour.test.cjs type: change rationale: Follow the previous core token migration with a bounded verification slice to lock the request-client contract and prevent regression to direct axios extraction patterns. impact: Improves confidence in actions/core/token.js behavior (success + failure semantics) without runtime code changes. status: completed

Summary:

  • Added new focused Phase 22 behavioural test suite:
    • tests/phase22/core-token-behaviour.test.cjs
  • Coverage asserts:
    • getToken success path returns token payload and calls requestJson with expected URL/method/body/headers
    • failure path logs via consoleLogger and returns the original error object
  • Test harness uses VM import stripping consistent with existing phase behavioural suites.

Validation:

  • node tests/phase22/core-token-behaviour.test.cjs -> pass (2/2)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: add this phase22 suite to any aggregate test runner used in CI if/when phase-level suites are centrally orchestrated.

CL-062: TASK22260 next slice — phase22 runner export guard parity (bounded test-harness consistency)

date: 2026-03-25 author: Cline scope: tests/phase22/core-token-behaviour.test.cjs type: change rationale: Continue bounded test-hardening by aligning phase22 test entry behavior with established suite conventions so it can be executed both directly and from aggregate runners. impact: Improves test harness composability and reduces accidental double-execution risk when importing phase22 test suites. status: completed

Summary:

  • Updated tests/phase22/core-token-behaviour.test.cjs to export run and add require.main === module guard.
  • Preserved direct CLI execution behavior while enabling safe module import by aggregate runners.

Validation:

  • node tests/phase22/core-token-behaviour.test.cjs -> pass (2/2)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: add a dedicated tests/phase22/index.test.cjs aggregate entrypoint if additional phase22 suites are introduced.

CL-063: TASK22260 next slice — phase22 aggregate runner entrypoint (bounded test-runner hardening)

date: 2026-03-25 author: Cline scope: tests/phase22/index.test.cjs type: change rationale: Continue the bounded test-harness stream by introducing a phase-level aggregate runner for phase22, matching established conventions used in other phase suites. impact: Improves consistency and future scalability of phase22 tests by enabling a single entry command as additional phase22 suites are added. status: completed

Summary:

  • Added tests/phase22/index.test.cjs aggregate runner.
  • Runner currently executes core-token-behaviour.test.cjs and prints a phase-level completion line.
  • Exported run and kept direct CLI execution guard parity (require.main === module).

Validation:

  • node tests/phase22/index.test.cjs -> pass
    • core-token: 2/2
    • phase22 combined: pass
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: add additional phase22 suites (e.g., shared-client utility behaviour tests) under this aggregate runner as migration coverage expands.

CL-064: TASK22260 next slice — phase22 client utility behaviour coverage expansion

date: 2026-03-25 author: Cline scope: tests/phase22/{client-utils-behaviour,index}.test.cjs type: change rationale: Execute the next bounded phase22 slice by adding focused behavioural coverage for shared client utilities to reduce regression risk as direct-service/client-wrapper migration continues. impact: Improves confidence in shared client helper contracts (endpointClient, relayClient) and keeps phase22 aggregate suite aligned with new coverage. status: completed

Summary:

  • Added new suite: tests/phase22/client-utils-behaviour.test.cjs.
  • Added assertions for shared client utility behavior:
    • endpointClient.getJson returns axios.get(...).data
    • endpointClient.requestJson returns axios(config).data
    • relayClient.buildHashedQueryUrl appends browser hash-service response
    • server fallback path uses hashAPIPath when HASHKEY is present
    • browser/no-HASHKEY failure path rethrows hash-service error
  • Updated tests/phase22/index.test.cjs to include the new client-utils suite in the phase aggregate runner.

Validation:

  • node tests/phase22/client-utils-behaviour.test.cjs -> pass (5/5)
  • node tests/phase22/index.test.cjs -> pass
    • core-token: 2/2
    • client-utils: 5/5
    • phase22 combined: pass
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: add focused phase22 behavioural coverage for any future shared client wrappers introduced beyond endpointClient/relayClient.

CL-065: TASK22260 next slice — fileClient extraction + phase22 behavioural coverage

date: 2026-03-25 author: Cline scope: actions/clients/{fileClient,index}.js, actions/services/documentDirectService.js, tests/phase22/{file-client-behaviour,index}.test.cjs type: change rationale: Continue bounded shared-client decomposition by extracting repeated file-route request patterns into fileClient and hardening behaviour with dedicated phase22 tests. impact: Reduces request-boilerplate duplication in document service and increases regression confidence for extracted file client helper contracts. status: completed

Summary:

  • Added new shared client wrapper: actions/clients/fileClient.js:
    • getFileJson(url)
    • getSignedFileJson(queryUrl)
    • downloadFileBlob(url)
  • Exported fileClient from actions/clients/index.js (and therefore via actions/index.js barrel path).
  • Migrated a bounded subset of actions/services/documentDirectService.js call sites to fileClient while preserving catch-path logging behavior:
    • getFilesFromBlobproxy -> getFileJson
    • deleteAwaitingSubmissionsFromBlob -> getSignedFileJson
    • deleteMyRepresentationsFromBlob -> getSignedFileJson
    • deleteBlob -> getFileJson
    • deleteRepBlob -> getFileJson
    • downloadBlob -> downloadFileBlob
  • Added tests/phase22/file-client-behaviour.test.cjs covering:
    • delegation to getJson
    • signed URL generation + requestJson invocation
    • blob download config contract (responseType: "blob")
  • Updated aggregate runner tests/phase22/index.test.cjs to include file-client suite.

Validation:

  • node tests/phase22/file-client-behaviour.test.cjs -> pass (3/3)
  • node tests/phase22/index.test.cjs -> pass
    • core-token: 2/2
    • client-utils: 5/5
    • file-client: 3/3
    • phase22 combined: pass
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: evaluate whether additional documentDirectService file-route call sites can adopt fileClient without altering current behavior contracts.

CL-066: TASK22260 next slice — service harness parity for fileClient helper injections

date: 2026-03-25 author: Cline scope: tests/serviceHarness.cjs type: change rationale: Follow the previous fileClient extraction with a bounded harness-compatibility slice so legacy VM-based service behavioural suites continue to execute without requiring per-test manual injections. impact: Restores migration-era behavioural regression stability (phase7) by aligning shared harness defaults with newly introduced file client helper symbols. status: completed

Summary:

  • Updated shared test harness defaults in tests/serviceHarness.cjs to inject file-client compatible helpers when not explicitly provided:
    • getFileJson (delegates to default getJson)
    • getSignedFileJson (signs via buildHashedQueryUrl then calls requestJson with GET config)
    • downloadFileBlob (calls requestJson with GET + responseType: "blob")
  • This preserves existing VM import-stripping strategy while preventing ReferenceError in migrated services that import from ../clients/fileClient.

Validation:

  • node tests/phase7/service-behaviour.test.cjs -> pass (12/12)
  • node tests/phase22/index.test.cjs -> pass
    • core-token: 2/2
    • client-utils: 5/5
    • file-client: 3/3
    • phase22 combined: pass
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: add a focused phase6/phase7 behavioural assertion for documentDirectService.downloadBlob to explicitly lock the downloadFileBlob delegation contract.

CL-067: TASK22260 next slice — phase7 downloadBlob delegation behavioural lock

date: 2026-03-25 author: Cline scope: tests/phase7/service-behaviour.test.cjs type: change rationale: Execute the queued follow-up by adding explicit phase7 behavioural coverage for documentDirectService.downloadBlob so file-client delegation and blob request config remain contract-stable. impact: Increases regression confidence for document download helper behaviour after fileClient extraction, without runtime code changes. status: completed

Summary:

  • Added new phase7 behavioural test:
    • document/downloadBlob delegates blob request config via file client helper
  • The test asserts:
    • downloadBlob lower-cases container in URL composition
    • request is issued through config-style request path (axios.request in harness)
    • request method is get
    • responseType is blob
    • returned payload contract is preserved from request helper data.

Validation:

  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)
  • node tests/phase22/index.test.cjs -> pass
    • core-token: 2/2
    • client-utils: 5/5
    • file-client: 3/3
    • phase22 combined: pass
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next bounded slice: evaluate adding equivalent focused behavioural coverage in phase6 if download helper behavior becomes part of that suites scope.

CL-068: TASK22260 widened module-completion slice — documentDirectService signed POST flows via fileClient

date: 2026-03-25 author: Cline scope: actions/clients/fileClient.js, actions/services/documentDirectService.js, tests/phase22/file-client-behaviour.test.cjs type: change rationale: Per user request to widen slices, complete a larger coherent module-level increment by moving the remaining signed POST file-route flows in document service onto fileClient. impact: Further reduces request/signing boilerplate in document service and centralizes signed file-route behavior in client wrapper layer with added regression coverage. status: completed

Summary:

  • Extended actions/clients/fileClient.js with:
    • postSignedFileJson(queryUrl, data, config = {})
    • signs query URL via buildHashedQueryUrl and executes POST via requestJson
  • Migrated all remaining signed POST helper paths in actions/services/documentDirectService.js to postSignedFileJson:
    • uploadFiles
    • uploadSingleFile
    • uploadRepFiles
    • generateRepPDF
    • generateAppealPDF
  • Preserved existing catch-path logging and request-option semantics:
    • multipart headers for upload flows
    • conditional responseType: "blob" for download variants
  • Expanded phase22 file-client behavior suite with explicit POST-signed contract test:
    • URL signing + POST method
    • data passthrough
    • config/header passthrough

Validation:

  • node tests/phase22/file-client-behaviour.test.cjs -> pass (4/4)
  • node tests/phase22/index.test.cjs -> pass
    • core-token: 2/2
    • client-utils: 5/5
    • file-client: 4/4
    • phase22 combined: pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next widened slice: evaluate consolidating remaining direct getJson file-read flows in documentDirectService behind fileClient for full per-module client symmetry.

CL-069: TASK22260 widened module-completion slice — documentDirectService file-read client symmetry

date: 2026-03-25 author: Cline scope: actions/services/documentDirectService.js type: change rationale: Continue widened slice cadence by completing per-module client symmetry in documentDirectService, moving all file-read helper calls to fileClient instead of mixed endpoint client usage. impact: Simplifies module dependency shape and centralizes file-route read behavior through a single client abstraction without changing runtime contracts. status: completed

Summary:

  • Removed mixed endpointClient usage from documentDirectService for file reads.
  • Migrated remaining file-read/helper routes from getJson to getFileJson:
    • getAwaitingSubmissionFromBlob
    • getRepsFromBlob
    • getRepsFromBlobProxy
    • getAwaitingSubmissionFromBlobProxy
    • getFilesFromBlob
    • getFilesFromBlobHashed
    • getProgressFromBlob
    • createContainerProxy
  • Removed now-unused imports from documentDirectService:
    • buildHashedQueryUrl
    • getJson
    • requestJson

Validation:

  • node tests/phase22/index.test.cjs -> pass
    • core-token: 2/2
    • client-utils: 5/5
    • file-client: 4/4
    • phase22 combined: pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next widened slice: introduce a small fileClient URL-builder helper set for repeated query-string composition in documentDirectService (container/casefolder/blob parameters) to reduce string-concat drift risk.

CL-070: TASK22260 widened cross-file slice — file route builder extraction + document service query normalization

date: 2026-03-25 author: Cline scope: actions/clients/{fileRouteBuilder,index}.js, actions/services/documentDirectService.js, tests/{serviceHarness,phase22/client-utils-behaviour}.cjs type: change rationale: Deliver a wider-than-previous slice by extracting reusable file-route query composition helpers and applying them across document service paths, reducing repeated string concatenation and encoding drift risk. impact: Improves maintainability and consistency of file-route URL construction while preserving existing runtime contracts and hash behavior. status: completed

Summary:

  • Added new shared helper module:
    • actions/clients/fileRouteBuilder.js
    • exports:
      • buildFileQuery(path, params, options) (supports optional encoded query composition)
      • withBaseUrl(baseUrl, route)
      • appendQuerySuffix(route, suffix)
  • Exported route-builder helpers via actions/clients/index.js.
  • Refactored actions/services/documentDirectService.js to use route-builder helpers across read/delete/download/query flows:
    • normalized composition for file routes and hash suffix append behavior
    • preserved encoded-path behavior for sensitive params (casefolderID, blobname) where previously encoded
    • preserved base URL prefix behavior and existing logger/catch semantics
  • Updated test harness defaults in tests/serviceHarness.cjs for new helper symbols:
    • buildFileQuery
    • withBaseUrl
    • appendQuerySuffix
  • Expanded phase22 utility coverage in tests/phase22/client-utils-behaviour.test.cjs with file route-builder behavior assertions.

Validation:

  • node tests/phase22/index.test.cjs -> pass
    • core-token: 2/2
    • client-utils: 6/6
    • file-client: 4/4
    • phase22 combined: pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next widened slice: evaluate applying fileRouteBuilder to portal/case service file-route call sites for cross-module query-builder consistency.

CL-071: TASK22260 next widened cross-module slice — portal service query normalization via fileRouteBuilder

date: 2026-03-25 author: Cline scope: actions/services/portalDirectService.js type: change rationale: Deliver the requested next wider slice by extending fileRouteBuilder adoption beyond document service into portal service, reducing duplicated query string concatenation and improving consistency in signed/unsigned route construction. impact: Improves maintainability and query-construction consistency across high-use portal service flows while preserving existing runtime behavior and hash-signing contracts. status: completed

Summary:

  • Refactored actions/services/portalDirectService.js to use shared route helpers:
    • buildFileQuery
    • withBaseUrl
    • appendQuerySuffix
  • Normalized query composition across portal service GET/DELETE/message flows:
    • read/list endpoints (getMyCases, getMyInvolvements, getMyLPACases, getMyRepresentations, proxy and watched/awaiting variants)
    • delete endpoints (deleteMyRepresentations, deleteAwaitingSubmissions, deleteWatchedCases)
    • file-message endpoints (sendCaseCompleteMessage, sendCaseCompleteMessageProxy, sendRepCompleteMessage)
  • Preserved behavior contracts:
    • retained BASE_URL usage patterns for existing BASE_URL-prefixed routes
    • retained hash-signing flow via buildHashedQueryUrl
    • retained append semantics for signed suffixes in sendCaseCompleteMessage
    • retained request methods, headers, payloads, and catch-path logging

Validation:

  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)
  • node tests/phase22/index.test.cjs -> pass
    • core-token: 2/2
    • client-utils: 6/6
    • file-client: 4/4
    • phase22 combined: pass
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next widened slice: apply the same query-normalization helpers in caseDirectService and add dedicated phase22 behavioral assertions for portalDirectService route-building/signing composition.

CL-072: TASK22260 next widened cross-module slice — case service query normalization via fileRouteBuilder

date: 2026-03-25 author: Cline scope: actions/services/caseDirectService.js, tests/phase6/service-behaviour.test.cjs type: change rationale: Continue widened cross-module rollout by applying shared query/route composition helpers to caseDirectService, reducing repeated string concatenation and aligning route construction style with document/portal services. impact: Improves maintainability and consistency in case service URL/query composition while preserving existing runtime behavior and error contracts. status: completed

Summary:

  • Refactored actions/services/caseDirectService.js to use fileRouteBuilder helpers:
    • buildFileQuery
    • withBaseUrl
  • Normalized route composition for read and write helpers, including:
    • case retrieval/search flows (getCaseMessage, getIncidentbyID, getIsPublishedbyID, getPartSavedAppeal, getSIPSEvents, getSIPSMedia)
    • appeal resolution/update/create flows (getAppealID, createNewCase, createNewCaseBlob, updateCase, updateCaseBlob, patchCase)
    • case/detail/document/module reads (getCase, getCaseByID, getAppealPDFDocs, getAppealPDFDocument, getPortalModuleDetails, getPortalModuleDetailsProxy)
  • Preserved existing contracts:
    • BASE_URL usage patterns where previously applied
    • method/payload semantics for requestJson paths
    • catch-path logging and return behavior (logAndReturnResponse, consoleLogger, error.response paths)
  • Expanded phase6 behavioural coverage with a focused assertion for case route composition:
    • case/getPortalModuleDetails composes BASE_URL route with encoded case reference

Validation:

  • node tests/phase6/service-behaviour.test.cjs -> pass (9/9)
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)
  • node tests/phase22/index.test.cjs -> pass
    • core-token: 2/2
    • client-utils: 6/6
    • file-client: 4/4
    • phase22 combined: pass
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional next widened slice: add a focused phase22 behavioral suite for caseDirectService and normalize any remaining specialized encoding usage behind explicit helper options where appropriate.

CL-073: TASK22260 essential condensed slice — case phase22 behavioural coverage + encoding contract lock

date: 2026-03-25 author: Cline scope: tests/phase22/{case-service-behaviour,index}.test.cjs type: change rationale: Condense remaining core work into one essential slice by adding explicit phase22 behavioural coverage for caseDirectService route composition and encoding-sensitive contracts. impact: Improves regression confidence for case service route-building behavior (BASE_URL composition, case-reference encoding/escaping, error-return contracts) without runtime behavior changes. status: completed

Summary:

  • Added new suite tests/phase22/case-service-behaviour.test.cjs with focused behavioural assertions for:
    • getPortalModuleDetails BASE_URL + encoded case reference composition
    • getPortalModuleDetailsProxy apostrophe escape behavior in case reference
    • getAppealID query composition and non-underscore value extraction
    • getAppealPDFDocument failure-path logging + error.response passthrough
  • Updated tests/phase22/index.test.cjs aggregate runner to include case-service-behaviour suite.

Validation:

  • node tests/phase22/index.test.cjs -> pass
    • core-token: 2/2
    • client-utils: 6/6
    • file-client: 4/4
    • case-service: 4/4
    • phase22 combined: pass
  • node tests/phase6/service-behaviour.test.cjs -> pass (9/9)
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Optional closure slice: add a dedicated phase22 portal-service behavioural suite and finish harmonization of remaining direct query string composition in portalDirectService write helpers.

CL-074: TASK22260 optional closure condensed slice — portal phase22 behavioural coverage + final write-helper query harmonization

date: 2026-03-25 author: Cline scope: actions/services/portalDirectService.js, tests/phase22/{portal-service-behaviour,index}.test.cjs type: change rationale: Complete optional closure work as one condensed slice by adding explicit portal behavioral coverage and removing remaining direct literal query strings in portal write helpers. impact: Improves regression confidence for signed portal flows and closes remaining route-composition harmonization gap in portal service write helpers without changing runtime behavior contracts. status: completed

Summary:

  • Harmonized remaining portal write-helper route literals to buildFileQuery(...):
    • createWatchedCases
    • setRepInvolvment
    • setCaseInvolvment
  • Added new phase22 suite tests/phase22/portal-service-behaviour.test.cjs covering:
    • signed hash suffix append path in sendCaseCompleteMessage
    • signed delete request contract + headers in deleteMyRepresentations
    • harmonized route helper URL in createWatchedCases
    • signed pre-request rejection contract in sendRepCompleteMessage when hash-signing fails
  • Updated tests/phase22/index.test.cjs aggregate runner to include portal-service-behaviour.

Validation:

  • node tests/phase22/portal-service-behaviour.test.cjs -> pass (4/4)
  • node tests/phase22/index.test.cjs -> pass
    • core-token: 2/2
    • client-utils: 6/6
    • file-client: 4/4
    • case-service: 4/4
    • portal-service: 4/4
    • phase22 combined: pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Closure for this condensed stream complete; any further work should be a separate expansion stream (e.g., additional service-level phase22 coverage breadth).

CL-075: TASK22260 sequence-A step3 completion slice — auth redirect safety + EN/CY route parity automation

date: 2026-03-25 author: Cline scope: tests/phase22/{auth-redirect-safety,i18n-route-parity,index}.test.cjs type: change rationale: Continue on this branch to complete the remaining sequence-A step3 gaps by adding explicit automated checks for auth callback/redirect safety and EN/CY route parity. impact: Improves confidence in auth redirect safety behavior and bilingual rewrite parity with targeted, low-risk regression checks and no runtime code changes. status: completed

Summary:

  • Added tests/phase22/auth-redirect-safety.test.cjs:
    • validates locale resolution precedence (query -> body -> cookie -> default en)
    • validates redirect callback behavior for:
      • relative URL to same base
      • same-origin absolute URL passthrough
      • external URL rewritten to locale-safe base origin with preserved path/query
  • Added tests/phase22/i18n-route-parity.test.cjs:
    • asserts presence of required CY rewrite aliases for auth/policy routes in next.config.js
    • includes checks for signin/email/error/verify-request + privacy/accessibility/terms routes
  • Updated tests/phase22/index.test.cjs to include both new suites in aggregate phase22 execution.

Validation:

  • node tests/phase22/auth-redirect-safety.test.cjs -> pass (4/4)
  • node tests/phase22/i18n-route-parity.test.cjs -> pass (1/1)
  • node tests/phase22/index.test.cjs -> pass
    • core-token: 2/2
    • client-utils: 6/6
    • file-client: 4/4
    • case-service: 4/4
    • portal-service: 4/4
    • auth-redirect: 4/4
    • i18n-route: 1/1
    • phase22 combined: pass
  • npm run lint -> warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)

Follow-ups:

  • Sequence A step3 targeted gaps are now covered; further test expansion should be treated as new scope (e.g., deeper end-to-end journey assertions).

CL-076: TASK22269 Slice B1.1 — signed-request helper set + portal pilot signed-flow migration

date: 2026-03-25 author: Cline scope: actions/clients/{signedRequestClient,index}.js, actions/services/portalDirectService.js, tests/{serviceHarness,phase22/portal-service-behaviour}.cjs type: change rationale: Execute Sequence B Workstream B1 pilot by introducing shared signed request helpers (GET/POST/DELETE) and migrating one bounded portal signed flow without broader module rollout. impact: Reduces duplication and drift risk in hash-signing + method execution paths while preserving existing signed-flow behavior contracts. status: completed

Summary:

  • Added new shared signed-request client helper module:
    • actions/clients/signedRequestClient.js
    • exports:
      • getSignedJson(queryUrl, config?)
      • postSignedJson(queryUrl, data, config?)
      • deleteSignedJson(queryUrl, config?)
    • all helpers use existing buildHashedQueryUrl(...) + requestJson(...) composition to preserve signing semantics
  • Exported new helper module via actions/clients/index.js.
  • Migrated exactly one pilot signed flow in portal service:
    • deleteWatchedCases in actions/services/portalDirectService.js
    • from inline buildHashedQueryUrl(...).then(requestJson(...)) to deleteSignedJson(queryUrl)
    • preserved existing catch/log behavior (consoleLogger + undefined return on catch)
  • Added test harness compatibility for VM import-stripping suites:
    • tests/serviceHarness.cjs now injects default deleteSignedJson mock behavior.
  • Expanded portal behavioral tests with explicit negative-path assertion:
    • tests/phase22/portal-service-behaviour.test.cjs
    • verifies deleteWatchedCases logs and safely returns undefined when signed delete fails.

Validation:

  • npm run lint -> pass with warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • node tests/phase22/index.test.cjs -> pass
    • portal-service suite now 5/5 including signed-delete failure path
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)

Follow-ups:

  • Continue Sequence B B1 in future bounded slices by migrating additional signed flows one module/function cluster at a time (outside this slice).

CL-077: TASK22269 Slice B1.2 — portal signed-delete bundle (headered delete migration)

date: 2026-03-25 author: Cline scope: actions/services/portalDirectService.js type: change rationale: Continue signed-request consolidation using bounded grouping by migrating the remaining portal signed delete flow (deleteMyRepresentations) onto shared signed helper while preserving required OData headers. impact: Further reduces duplicated sign+delete boilerplate in portal service and centralizes signed DELETE execution semantics. status: completed

Summary:

  • Migrated deleteMyRepresentations from inline buildHashedQueryUrl(...).then(requestJson(...)) to shared deleteSignedJson(queryUrl, { headers }).
  • Preserved behavior-critical headers exactly:
    • OData-MaxVersion
    • OData-Version
    • Accept
    • Prefer
    • Content-Type
  • Preserved existing catch/log behavior (consoleLogger with safe undefined return on failure).

Validation:

  • npm run lint -> pass with warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • node tests/phase22/index.test.cjs -> pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)

Follow-ups:

  • Next bounded signed GET candidate in portal service is sendRepCompleteMessage (single signed URL + GET request path).

CL-078: TASK22269 Slice B1.3 — fileClient signed helper delegation bundle

date: 2026-03-25 author: Cline scope: actions/clients/fileClient.js, tests/phase22/file-client-behaviour.test.cjs type: change rationale: Continue grouped signed-request consolidation by reducing duplicate signing logic in fileClient and delegating signed GET/POST operations to shared signedRequestClient helpers. impact: Centralizes signed method execution behavior in one helper layer and lowers drift risk across file-service call paths. status: completed

Summary:

  • Updated actions/clients/fileClient.js:
    • replaced direct buildHashedQueryUrl + requestJson logic in:
      • getSignedFileJson -> now delegates to getSignedJson
      • postSignedFileJson -> now delegates to postSignedJson
    • retained downloadFileBlob and getFileJson behavior unchanged.
  • Updated tests/phase22/file-client-behaviour.test.cjs to assert delegation contracts for getSignedJson and postSignedJson rather than direct signing internals.

Validation:

  • npm run lint -> pass with warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • node tests/phase22/index.test.cjs -> pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)

Follow-ups:

  • Candidate map now indicates remaining explicit signed request composition is primarily in account/portal signed GET edge paths (getPortalLogin, sendRepCompleteMessage, and signed suffix append flow in sendCaseCompleteMessage) for future bounded slices.

CL-079: TASK22269 Slice B1.4 — signed GET consolidation bundle (portal + account)

date: 2026-03-25 author: Cline scope: actions/clients/signedRequestClient.js, actions/services/{portalDirectService,accountDirectService}.js, tests/{serviceHarness,phase22/portal-service-behaviour,phase7/service-behaviour}.cjs type: change rationale: Continue grouped signed-request migration by consolidating remaining direct signed-GET composition paths onto shared signed helper primitives while preserving route behavior contracts. impact: Reduces residual signing duplication and standardizes signed URL creation across portal/account service read/message flows. status: completed

Summary:

  • Enhanced signedRequestClient:
    • added buildSignedUrl(queryUrl, { baseUrl? }) helper for signed URL generation reuse
    • updated internal signed request execution to use buildSignedUrl
  • Migrated account signed GET candidate:
    • accountDirectService.getPortalLogin now uses getSignedJson(queryUrl, { baseUrl: BASE_URL })
    • preserved existing error semantics (consoleLogger + JSON.stringify(error))
  • Migrated portal signed GET candidates:
    • portalDirectService.sendRepCompleteMessage now uses buildSignedUrl(hashQueryPath)
    • portalDirectService.sendCaseCompleteMessage now uses buildSignedUrl(hashQueryPath) + existing signed suffix append behavior
    • preserved existing request method/URL shape and catch-path behavior
  • Updated test harness and suites:
    • tests/serviceHarness.cjs now provides defaults for buildSignedUrl, getSignedJson, postSignedJson
    • tests/phase22/portal-service-behaviour.test.cjs includes assertion for sendRepCompleteMessage signed-helper delegation
    • tests/phase7/service-behaviour.test.cjs account portal-login expectations aligned to request-config path used by shared signed helper

Validation:

  • npm run lint -> pass with warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • node tests/phase22/index.test.cjs -> pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)

Follow-ups:

  • Remaining special-case signed pattern is now primarily the signed-suffix append composition in sendCaseCompleteMessage (already using shared buildSignedUrl), with broader module migrations to be planned in future bounded slices.

CL-080: TASK22269 Slice B1.5 — document hash-suffix route normalization helper

date: 2026-03-25 author: Cline scope: actions/clients/fileRouteBuilder.js, actions/services/documentDirectService.js, tests/{serviceHarness,phase22/client-utils-behaviour}.cjs type: change rationale: Continue grouped follow-on candidates by normalizing repeated deterministic hash-suffix route assembly in document service behind one shared route-builder helper. impact: Reduces repeated appendQuerySuffix(route, hashAPIPath(route)) composition drift risk while preserving route/query/hash behavior. status: completed

Summary:

  • Added appendHashSuffix(route, hashBuilder) to fileRouteBuilder.
  • Migrated document service deterministic hash-suffix paths to new helper:
    • getAwaitingSubmissionFromBlob
    • getRepsFromBlob
    • getFilesFromBlob
    • getProgressFromBlob
    • createContainerProxy
  • Updated shared VM harness defaults (tests/serviceHarness.cjs) to inject appendHashSuffix.
  • Expanded phase22 utility test to cover new helper behavior (tests/phase22/client-utils-behaviour.test.cjs).

Validation:

  • npm run lint -> pass with warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • node tests/phase22/index.test.cjs -> pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)

Follow-ups:

  • Remaining non-service candidate for this stream is actions/azurestorage.js direct hashAPIPath metadata assembly (separate bounded slice if desired).

CL-081: TASK22269 Slice B1.6 — azurestorage hash-query metadata builder normalization

date: 2026-03-25 author: Cline scope: actions/azurestorage.js type: change rationale: Continue requested follow-on slice by reducing repeated hash-query path string composition in azure storage metadata builders behind local helper functions. impact: Lowers duplication/drift risk in hashed metadata path generation while preserving existing route and encoding behavior. status: completed

Summary:

  • Added internal helper builders in actions/azurestorage.js:
    • buildDownloadBlobQueryPath(...)
    • buildDeleteBlobQueryPath(...)
    • buildGetBlobListQueryPath(...)
  • Replaced repeated inline hash path literals with helper usage in targeted metadata object builders:
    • getBlobs
    • getProgressBlobs
    • getRepsFilesBlobs
  • Preserved existing behavior semantics for hash path construction:
    • encoded casefolderID/blobname where previously encoded
    • unchanged containerName and casefolderID value sourcing
    • unchanged returned object field names and shape

Validation:

  • npm run lint -> pass with warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • node tests/phase22/index.test.cjs -> pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)

Follow-ups:

  • Remaining potential cleanups in actions/azurestorage.js are broader non-slice refactors (legacy logging verbosity, large function decomposition) and should be handled separately to keep risk bounded.

CL-082: TASK22269 Slice B1.7 — azurestorage hash metadata helper consolidation

date: 2026-03-25 author: Cline scope: actions/azurestorage.js type: change rationale: Continue bounded normalization by consolidating repeated hash metadata object field population into a single local helper. impact: Reduces duplicated metadata field assembly and drift risk while preserving existing output shape and hash behavior. status: completed

Summary:

  • Added buildHashMetadataPaths({ containerName, casefolderID, blobname }) helper.
  • Replaced repeated per-object hash metadata assignment in:
    • getBlobs
    • getProgressBlobs
    • getRepsFilesBlobs
  • Preserved existing metadata contracts:
    • keys unchanged: hashedfilepath, hasheddeletepath, hashgetblobs
    • same encoded query path inputs and route semantics.

Validation:

  • npm run lint -> pass with warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • node tests/phase22/index.test.cjs -> pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)

Follow-ups:

  • Any further azurestorage.js cleanup should remain bounded (e.g., logging-only normalization) and separated from behavior-affecting refactors.

CL-083: TASK22269 Slice B1.8 — phase22 azurestorage helper contract coverage

date: 2026-03-26 author: Cline scope: tests/phase22/{azurestorage-helper-behaviour,index}.test.cjs type: change rationale: Execute the selected bounded test-only follow-up by adding focused regression coverage for recently added azurestorage helper contracts. impact: Improves confidence in query-path and hash-metadata helper output stability without changing runtime behavior. status: completed

Summary:

  • Added new phase22 suite: tests/phase22/azurestorage-helper-behaviour.test.cjs.
  • Test suite isolates helper block from actions/azurestorage.js and verifies:
    • buildDownloadBlobQueryPath default encoding output
    • buildDeleteBlobQueryPath non-encoded option behavior
    • buildGetBlobListQueryPath query output contract
    • buildHashMetadataPaths key/value shape (hashedfilepath, hasheddeletepath, hashgetblobs)
  • Wired suite into aggregate runner tests/phase22/index.test.cjs.

Validation:

  • npm run lint -> pass with warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • node tests/phase22/index.test.cjs -> pass (includes new azurestorage-helper 4/4)
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)

Follow-ups:

  • Optional next bounded slice: add an explicit assertion for encoded casefolderID variants containing reserved query characters (?, &) if those inputs are expected in future flows.

CL-084: TASK22269 Slice B1.9 — azurestorage local split-value tidy in touched helper consumers

date: 2026-03-26 author: Cline scope: actions/azurestorage.js type: change rationale: Execute the selected next bounded readability-only slice by reducing repeated blob.name.split("/") access in the recently touched helper-consumer functions. impact: Non-behavioral maintainability improvement in azurestorage helper-consumer paths; no API/route contract changes. status: completed

Summary:

  • In targeted functions (getBlobs, getProgressBlobs, getRepsFilesBlobs), introduced local path-part variables to avoid repeated inline splitting:
    • blobPathParts
    • fileName / appealBlobName / repFileName
    • casefolderPath
  • Replaced repeated field reads and helper arguments with these locals in object construction and hash metadata composition.
  • Preserved existing query composition and output shape/keys (including hashed path metadata fields).

Validation:

  • npm run lint -> pass with warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • node tests/phase22/index.test.cjs -> pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)

Follow-ups:

  • Optional next bounded slice: logging-only normalization in these same azurestorage functions (no behavior change), done separately from structural refactors.

CL-085: TASK22269 Slice B1.10 — azurestorage touched-function logging normalization

date: 2026-03-26 author: Cline scope: actions/azurestorage.js type: change rationale: Execute the next bounded, logging-only slice by normalizing selected touched-function logs to consoleLogger for consistency with current helper/error logging style. impact: Observability consistency improvement only; no API/route behavior or payload contract changes. status: completed

Summary:

  • In previously touched helper-consumer functions only:
    • getProgressBlobs
    • getRepsFilesBlobs
  • Replaced selected direct console.log(...) calls with consoleLogger(...):
    • progress blob path-parts trace
    • per-blob name trace in reps file listing
    • final blobObj trace in reps file listing
  • Scope intentionally excludes broader file-wide logging normalization to keep risk bounded.

Validation:

  • npm run lint -> pass with warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • node tests/phase22/index.test.cjs -> pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)

Follow-ups:

  • Optional next bounded slice: prune currently-unused local blobDocumentType variables in the same touched functions (readability-only, no behavior change).

CL-086: TASK22269 Slice B1.11 — azurestorage touched-function unused-local prune

date: 2026-03-26 author: Cline scope: actions/azurestorage.js type: change rationale: Execute the next bounded readability-only slice by removing now-unused local variables left in recently touched helper-consumer functions. impact: Maintainability/readability improvement only; no API/route behavior changes. status: completed

Summary:

  • Removed unused local blobDocumentType declarations from:
    • getBlobs
    • getRepsFilesBlobs
  • No object shape, query generation, hash metadata logic, or routing behavior changed.

Validation:

  • npm run lint -> pass with warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • node tests/phase22/index.test.cjs -> pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)

Follow-ups:

  • Optional next bounded slice: align remaining low-risk direct console.log calls in these functions to consoleLogger only where already touched and safe.

CL-087: TASK22269 Slice B1.12 — azurestorage touched-function path assembly helper reuse

date: 2026-03-26 author: Cline scope: actions/azurestorage.js type: change rationale: Execute the next bounded maintainability slice by reusing existing local query-path helpers for touched filepath/deletepath assembly, reducing repeated literal concatenation. impact: Readability/consistency improvement only; preserves query parameter values and route behavior. status: completed

Summary:

  • In touched functions:
    • getBlobs
    • getRepsFilesBlobs
  • Replaced inline filepath string concatenation with buildDownloadBlobQueryPath(...).
  • Replaced inline deletepath string concatenation with buildDeleteBlobQueryPath(...).
  • Preserved previous encoding behavior where required by passing explicit options:
    • kept non-encoded casefolderID/blobname behavior in getRepsFilesBlobs.deletepath via helper options.

Validation:

  • npm run lint -> pass with warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • node tests/phase22/index.test.cjs -> pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)

Follow-ups:

  • Optional next bounded slice: targeted helper-consumer tidy in the same functions for any remaining repeated query-path literals outside touched object fields.

CL-088: TASK22269 Slice B1.13 — azurestorage touched-function caseObj helper reuse

date: 2026-03-26 author: Cline scope: actions/azurestorage.js type: change rationale: Execute the next bounded readability slice by centralizing repeated case-object path composition in touched helper-consumer functions. impact: Maintainability/readability improvement only; no route/query behavior changes. status: completed

Summary:

  • Added local helper buildCaseObjectPath(casefolderID).
  • Replaced repeated caseObj string assembly in touched functions:
    • getBlobs
    • getProgressBlobs
  • Preserved existing caseObj output format (<caseRef>/<caseRef>_case.json).

Validation:

  • npm run lint -> pass with warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • node tests/phase22/index.test.cjs -> pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)

Follow-ups:

  • Optional next bounded slice: continue tiny helper reuse in touched functions only if any duplicated path literals remain and can be reduced without behavior change.

CL-089: TASK22269 Slice B1.14 — azurestorage touched-function contentLength local reuse

date: 2026-03-26 author: Cline scope: actions/azurestorage.js type: change rationale: Execute the next tiny bounded readability slice by reusing local contentLength values in touched helper-consumer functions to reduce repeated property access and keep object assembly consistent. impact: Maintainability/readability improvement only; no route/query/output behavior changes. status: completed

Summary:

  • In touched functions:
    • getBlobs
    • getProgressBlobs
    • getRepsFilesBlobs
  • Added local contentLength variable (blob.properties.contentLength) per loop iteration.
  • Replaced repeated inline blob.properties.contentLength assignments in object assembly with the local variable.
  • Preserved field contracts (contentLength, size) and values.

Validation:

  • npm run lint -> pass with warnings only (pre-existing react-hooks/exhaustive-deps; no new lint errors)
  • node tests/phase22/index.test.cjs -> pass
  • node tests/phase7/service-behaviour.test.cjs -> pass (13/13)

Follow-ups:

  • Optional next bounded slice: stop or switch scope; touched-function micro-tidies in this area are now largely exhausted.

CL-090: uploadsinglefile API batch upload chunking (10 files per pass)

date: 2026-04-02 author: Cline scope: pages/api/file/uploadsinglefile.js type: change rationale: Prevent oversized single-pass uploads by splitting validated files into bounded batches and invoking existing upload logic per batch. impact: Upload flow now processes validated files in deterministic chunks of 10 while preserving existing hash guards, validation behavior, and response contract (data, invalidFiles). status: completed

Summary:

  • Added MAX_FILES_PER_UPLOAD_BATCH = 10 in uploadsinglefile API route.
  • Replaced single uploadSingleFile(...) invocation with chunked processing:
    • converts allowed files object to entries
    • slices into batches of 10
    • awaits uploadSingleFile(...) once per batch
    • aggregates batch results into one data array for response
  • Kept existing error handling unchanged (UPLOAD_SINGLE_FILE_FAILED on catch).

Validation:

  • npx eslint pages/api/file/uploadsinglefile.js -> executed with no lint output.

Follow-ups:

  • Optional: add a focused API contract/unit test to assert that 11+ files cause multiple uploadSingleFile invocations with max chunk size 10.

CL-091: uploadsinglefile client-side batch upload chunking parity (10 files per pass)

date: 2026-04-02 author: Cline scope: actions/services/documentDirectService.js type: change rationale: Ensure client upload flow aligns with server batching requirement by splitting outbound uploadsinglefile requests into chunks of 10 files. impact: Client now sends multiple sequential API requests (max 10 files each) and merges per-chunk responses into one contract-compatible payload (data, invalidFiles) for existing UI handlers. status: completed

Summary:

  • Added MAX_FILES_PER_UPLOAD_BATCH = 10 to document direct service.
  • Updated uploadSingleFile(filesObj, containerID, casefolderID) to:
    • split selected files into batches of 10
    • create a fresh FormData per batch
    • call /api/file/uploadsinglefile once per batch (sequential await)
    • aggregate data and invalidFiles from all chunk responses
  • Preserved function return shape used by current upload UIs.

Validation:

  • npx eslint pages/api/file/uploadsinglefile.js actions/services/documentDirectService.js -> executed with no lint output.

Follow-ups:

  • Optional: add a focused service-behaviour test that verifies uploadSingleFile issues N requests for N/10 chunks and merges all chunk responses.

CL-092: per-chunk upload UI progress updates (new appeal + representations)

date: 2026-04-02 author: Cline scope: actions/services/documentDirectService.js, components/elements/index.js, components/case/representation/representationElements.js type: change rationale: Update UX so upload progress reflects completed chunks incrementally instead of waiting for all chunk requests to finish. impact: Upload status message now updates after each chunk completes, showing users progressive completion for large multi-file uploads while preserving existing response/error contracts. status: completed

Summary:

  • Extended uploadSingleFile(...) service signature with optional callback support:
    • new options argument
    • invokes options.onChunkComplete(...) after each chunk upload
    • emits cumulative counters (cumulativeUploaded, cumulativeInvalid, processedFiles, chunk index metadata)
  • Wired per-chunk UI progress updates in both upload entry points:
    • components/elements/index.js
    • components/case/representation/representationElements.js
  • Both now pass onChunkComplete and update uploadCountMessage from cumulative uploaded count as each chunk finishes.

Validation:

  • npx eslint pages/api/file/uploadsinglefile.js actions/services/documentDirectService.js components/elements/index.js components/case/representation/representationElements.js -> executed with no lint output.

Follow-ups:

  • Optional: add a focused UI behaviour test to assert upload progress increments per completed chunk for >10 file uploads.

CL-093: upload progress message format X of Y during chunked uploads

date: 2026-04-02 author: Cline scope: components/elements/index.js, components/case/representation/representationElements.js type: change rationale: User requested clearer in-progress feedback showing uploaded count relative to total files (e.g. 10 of 30) during chunked uploads. impact: Upload UI now shows progressive message in X of Y format while upload is in flight, improving clarity for large batch uploads. status: completed

Summary:

  • Added totalUploadFiles state in both upload UIs (new appeal + representations).
  • In-progress upload message now renders as:
    • ${uploadCountMessage} of ${totalUploadFiles}
  • Upload start now initializes as 0 of Y and updates after each completed chunk callback.
  • Empty-drop/reset path clears both counters to avoid stale totals.

Validation:

  • npx eslint actions/services/documentDirectService.js components/elements/index.js components/case/representation/representationElements.js -> executed with no lint output.

Follow-ups:

  • Optional: localize a dedicated upload-progress-x-of-y translation key if copy needs stronger grammatical control per locale.

CL-094: 22500 components/elements/index.js Phase 2 bounded cleanup (remove dead renderField)

date: 2026-04-07 author: Cline scope: components/elements/index.js type: change rationale: Execute the next smallest low-risk Phase 2 slice by removing the local renderField utility after confirming it is unused in the repo. impact: No behavior change intended; dead code removal only. No auth/security/middleware/API changes. EN/CY and accessibility behavior remain unchanged. status: completed

Summary:

  • Confirmed renderField had no usages outside its declaration.
  • Removed the unused local renderField function from components/elements/index.js.
  • Kept all field component exports, routing, and existing render paths unchanged.

Validation:

  • npx eslint components/elements/index.js -> pass

Follow-ups:

  • Continue Phase 2 with one bounded no-behavior-change slice, likely next lowest-risk renderer extraction from components/elements/index.js.

CL-095: 22500 components/elements/index.js hook-order hotfix (MultiLinefield)

date: 2026-04-07 author: Cline scope: components/elements/index.js type: change rationale: Fix runtime React warning caused by conditional hook execution path in MultiLinefield after dead-code cleanup. impact: No functional behavior change intended; resolves Rules of Hooks ordering warning by making label translation hook usage unconditional per render. status: completed

Summary:

  • Root cause: FieldsTranslations(props.label) (which internally uses useRouter) was invoked within conditional render branches in MultiLinefield, causing hook order mismatch when branch conditions changed.
  • Fix: precomputed translatedLabel once in MultiLinefield render body and reused in both branches.
  • Kept EN/CY text resolution logic and rendered output unchanged.

Validation:

  • npx eslint components/elements/index.js -> pass

Follow-ups:

  • Continue bounded Phase 2 slices; when touching field components, prefer top-level computed hook-backed values reused across conditional branches.

CL-096: 22500 RenderSubFields render-phase update warning hotfix

date: 2026-04-07 author: Cline scope: components/elements/fields/renderSubFields.js type: change rationale: Fix React warning about updating parent-connected state during RenderSubFields render. impact: No intended behavior change; initial empty FieldArray row initialization moved out of render phase to effect phase to satisfy React rendering constraints. status: completed

Summary:

  • Root cause: fields.length == 0 && fields.push({}) executed inside render, triggering state updates while rendering RenderSubFields.
  • Fix: moved initial row insertion into useEffect, guarded by fields.length === 0.
  • Preserved existing UX intent: ensure at least one subfield row appears when array starts empty.

Validation:

  • npx eslint components/elements/fields/renderSubFields.js -> pass

Follow-ups:

  • Keep redux-form fields.push/remove calls event/effect-driven (not render-driven) in future slices.

CL-097: 22500 FieldArrayForm render-phase dispatch warning hotfix

date: 2026-04-07 author: Cline scope: components/elements/index.js type: change rationale: Fix React warning caused by dispatching redux-form state updates during FieldArrayForm render. impact: No intended behavior change; clearing hidden FieldArray values remains intact but now executes in effect phase instead of render phase. status: completed

Summary:

  • Root cause: dispatch(change("appealForm", name, null)) was called inline in render when parent condition was false.
  • Fix: moved that dispatch into useEffect guarded by !showIfHasParentShowValue.
  • Added useEffect import in components/elements/index.js.

Validation:

  • npx eslint components/elements/index.js -> pass

Follow-ups:

  • Continue avoiding dispatch/state mutations inside render for field visibility toggles.

CL-098: 22500 FieldArrayForm bounded dead-code cleanup

date: 2026-04-07 author: Cline scope: components/elements/index.js type: change rationale: Continue bounded Phase 2 cleanup with a lowest-risk slice by removing unused locals/destructured props in FieldArrayForm. impact: No intended behavior change; purely removes unused values left from legacy implementation. status: completed

Summary:

  • Removed unused destructured props from FieldArrayForm: label, validation, maxFieldLength.
  • Removed no-op/dead lines in FieldArrayForm:
    • redundant boolean expression line
    • unused parentFieldShowOnValue; expression
    • unused handleSubmit/pristine/reset/submitting destructure
  • Kept visibility logic, effect-driven clearing behavior, and FieldArray rendering path unchanged.

Validation:

  • npx eslint components/elements/index.js -> pass

Follow-ups:

  • Continue Phase 2 with one bounded slice at a time; next low-risk target can be similar dead-code/no-op cleanup in another isolated renderer block.

CL-099: 22500 components/elements/index.js bounded extraction bundle (RenderFileUpload + FieldArrayForm)

date: 2026-04-07 author: Cline scope: components/elements/index.js, components/elements/fields/renderFileUpload.js, components/elements/fields/fieldArrayForm.js type: change rationale: Execute a slightly larger but still bounded Phase 2 slice by extracting two self-contained blocks from the monolith (RenderFileUpload and FieldArrayForm) into dedicated field modules. impact: No intended behavior change; preserves EN/CY output, upload flow, and accessibility semantics while reducing index.js size/coupling. status: completed

Summary:

  • Added components/elements/fields/renderFileUpload.js and moved the full existing RenderFileUpload implementation unchanged.
  • Added components/elements/fields/fieldArrayForm.js and moved the full existing FieldArrayForm implementation unchanged.
  • Updated components/elements/index.js imports to consume extracted modules.
  • Removed inline RenderFileUpload/FieldArrayForm implementations and related now-unused imports/constants from index.js.

Validation:

  • npx eslint components/elements/index.js components/elements/fields/renderFileUpload.js components/elements/fields/fieldArrayForm.js -> pass

Follow-ups:

  • Continue Phase 2 with bounded renderer/module extractions from components/elements/index.js (one cohesive bundle per commit).

CL-100: 22500 components/elements/index.js helper normalization (validation messages + visibility checks)

date: 2026-04-07 author: Cline scope: components/elements/index.js type: change rationale: Apply the requested next bounded refactor slice by consolidating repeated validation message setup and parent-field visibility logic into shared local helpers. impact: No intended behavior change; EN/CY and accessibility behavior preserved while reducing repeated logic and future drift risk. status: completed

Summary:

  • Added getValidationMessages(t) helper for repeated required/emoji/postcode message retrieval.
  • Added visibility helpers:
    • hasParentFieldValue(...)
    • isVisibleByEquality(...)
    • isVisibleByInclusion(...)
  • Replaced repeated inline visibility and validation-message setup across field wrappers with helper usage (Textfield, MultiLinefield, RichMultiLinefield, DateFieldPicker, YesNofield, Radiofield, NumericField, DecimalField).
  • Kept existing field render paths, conditions, and validation calls intact.

Validation:

  • npx eslint components/elements/index.js -> pass

Follow-ups:

  • Continue bounded no-behavior-change slices by removing dead locals/comments and extracting one additional low-risk field wrapper at a time.

CL-101: 22500 PickList wrapper extraction from components/elements/index.js

date: 2026-04-08 author: Cline scope: components/elements/index.js, components/elements/fields/pickListField.js type: change rationale: Execute one bounded Phase 2 renderer/wrapper extraction slice by moving the PickList wrapper out of the elements monolith into a dedicated field module. impact: No intended behavior change; preserves existing EN/CY translation behavior and validation wiring while reducing index.js size/coupling. status: completed

Summary:

  • Added components/elements/fields/pickListField.js and moved the existing PickList wrapper implementation.
  • Updated components/elements/index.js to import/export PickList from the new field module.
  • Removed inline PickList wrapper implementation from index.js.
  • Removed now-unused RenderPickList import from index.js after extraction.

Validation:

  • npx eslint components/elements/index.js components/elements/fields/pickListField.js -> pass

Follow-ups:

  • Continue bounded no-behavior-change slices by extracting one additional low-risk wrapper (e.g., CheckBoxfield) or removing dead locals/debug logging in place.

CL-102: 22500 components/elements/index.js dead/debug-only cleanup slice

date: 2026-04-08 author: Cline scope: components/elements/index.js type: change rationale: Execute requested bounded cleanup slice by removing dead code and debug-only artifacts from the elements monolith without changing behavior. impact: No intended behavior change; EN/CY and accessibility behavior preserved while reducing noise and unused code paths. status: completed

Summary:

  • Removed debug-only runtime log in CheckBoxfield (console.log(props)).
  • Removed unused/dead locals and helpers inside components/elements/index.js, including:
    • top-level unused imports (axios, pickListLookup, setFileCount action import, redux hooks import)
    • unused local validators and helpers in wrappers (e.g., unused required/postcode/normalizeDecimal variants)
    • unused placeholder constant RenderCaseID
    • unused local fallbacks in FileUploadField (uploadCount, local setFileCount)
  • Kept functional field wiring, labels/translations, and validation behavior in active render paths unchanged.

Validation:

  • npx eslint components/elements/index.js -> pass

Follow-ups:

  • Continue bounded no-behavior-change slices only (e.g., extract one additional low-risk wrapper such as CheckBoxfield).

CL-103: 22500 wrapper extraction bundle (CheckBoxfield, DateFieldPicker, YesNofield)

date: 2026-04-08 author: Cline scope: components/elements/index.js, components/elements/fields/{checkBoxField,dateFieldPicker,yesNoField}.js type: change rationale: Execute the requested bundled wrapper slice by extracting wrappers 1/2/3 in one commit while keeping behavior unchanged. impact: No intended behavior change; keeps EN/CY output, visibility logic, and accessibility structure intact while reducing components/elements/index.js size. status: completed

Summary:

  • Added components/elements/fields/checkBoxField.js and moved CheckBoxfield wrapper.
  • Added components/elements/fields/dateFieldPicker.js and moved DateFieldPicker wrapper logic.
  • Added components/elements/fields/yesNoField.js and moved YesNofield wrapper logic.
  • Updated components/elements/index.js to import/export these wrappers from field modules.
  • Removed inline implementations of CheckBoxfield, DateFieldPicker, and YesNofield from index.js.

Validation:

  • npx eslint components/elements/index.js components/elements/fields/checkBoxField.js components/elements/fields/dateFieldPicker.js components/elements/fields/yesNoField.js -> pass

Follow-ups:

  • Remaining wrappers can continue as bounded slices (Radiofield, NumericField, DecimalField) if required.

CL-104: 22500 wrapper extraction bundle (Radiofield, NumericField, DecimalField)

date: 2026-04-08 author: Cline scope: components/elements/index.js, components/elements/fields/{radioField,numericField,decimalField}.js type: change rationale: Continue Phase 2 with the next bounded wrapper bundle by extracting wrappers 4/5/6 from components/elements/index.js into dedicated field modules without behavior change. impact: No intended behavior change; preserves EN/CY behavior, validation wiring, and accessibility semantics while reducing monolith size. status: completed

Summary:

  • Added components/elements/fields/radioField.js for Radiofield.
  • Added components/elements/fields/numericField.js for NumericField.
  • Added components/elements/fields/decimalField.js for DecimalField.
  • Updated components/elements/index.js to import/export these wrappers from field modules.
  • Removed inline Radiofield, NumericField, and DecimalField implementations from index.js.

Validation:

  • npx eslint components/elements/index.js components/elements/fields/radioField.js components/elements/fields/numericField.js components/elements/fields/decimalField.js -> pass

Follow-ups:

  • Next bounded wrappers (if needed): ReadOnlyfield/remaining small wrappers or additional dead-code cleanup slices.