Files
pedwfrontend/memory-bank/change-log.md
2026-07-01 11:29:15 +00:00

448 KiB
Raw Permalink 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:

CL-2026-07-01-CRM-DISPLAY-INFRASTRUCTURE-COMPLETION: document completed workstream and helper boundary

date: 2026-07-01 author: Cline scope: context/architecture.md, context/maintainability-adoption-programme.md, context/implementation-governance.md, memory-bank/change-log.md type: change rationale: Record the completed CRM Display Infrastructure workstream in core architecture/programme/governance documentation so future contributors use the shared helper correctly and do not broaden it into separate presentation concerns. impact: Documentation/governance only. Records the completed stream, final helper contract, preserved helper boundary, remaining intentional non-helper concerns, and future implementation guidance. No application code, tests, translations, routes, CRM queries, or runtime behaviour changed. status: completed

Summary:

  • Continued on branch:
    • feature/crm-display-resolver-slice1
  • Documented CRM Display Infrastructure as completed in:
    • context/architecture.md
    • context/maintainability-adoption-programme.md
    • context/implementation-governance.md
  • Recorded the shared helper:
    • lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Recorded the final helper contract:
    • missing CRM display value -> return fallbackValue
    • English -> return CRM display value
    • Welsh -> return translated value when available
    • Welsh without translation -> return CRM display value
  • Recorded delivered workstream outcomes:
    • shared resolver introduced
    • helper test coverage
    • helper contract verified and corrected
    • compatible consumers migrated across admin, search, My Portal, and summary types
    • stale JSONPath imports removed where safe
    • behaviour-preserving migration completed
  • Recorded architectural boundary exclusions that must remain separate from the helper:
    • keyed lookup translation
    • project-type list translation
    • hyperlink / website rendering
    • mixed presentation rows
    • lifecycle interpretation
    • status interpretation
    • post-decision interpretation
    • other presentation-specific derived values
  • Recorded governance rule for future implementation:
    • use resolveCrmDisplayValue(...) for the standard CRM formatted/display-value translation concern
    • do not introduce new inline JSONPath lookups for that standard pattern
    • introduce separate abstractions rather than broadening resolveCrmDisplayValue(...)

Validation:

  • git diff --check -- context/architecture.md context/maintainability-adoption-programme.md context/implementation-governance.md memory-bank/change-log.md

Follow-ups:

  • Future work should introduce separate abstractions rather than broadening resolveCrmDisplayValue().

CL-2026-07-01-CRM-DISPLAY-RESOLVER-FALLBACK-CLEANUP: simplify remaining safe redundant fallbacks

date: 2026-07-01 author: Cline scope: components/case/summaryTypes/pinswg_callinss77id.js, components/myportal/viewall.js, memory-bank/change-log.md type: change rationale: A final focused cleanup was requested to remove redundant fallbackValue patterns that repeated the same CRM display value already supplied as value, while preserving existing page-specific absent-value behaviour. impact: Behaviour-preserving cleanup only. Simplified six redundant fallback patterns in pinswg_callinss77id.js. components/myportal/viewall.js was reviewed and left unchanged where remaining fallback scenarios were conditional or ambiguous. No helper logic, tests, search/admin files, routing, grouping, list logic, or derived value behaviour changed. status: completed

Summary:

  • Continued on branch:
    • feature/crm-display-resolver-slice1
  • Narrow scope followed exactly:
    • edited only components/case/summaryTypes/pinswg_callinss77id.js
    • reviewed components/myportal/viewall.js and left ambiguous fallback cases unchanged
    • updated memory-bank/change-log.md
  • Simplified six redundant fallback patterns in pinswg_callinss77id.js:
    • case type -> t("case:summary-no-date-entered-label")
    • EIA required -> t("case:summary-to-be-confirmed-label")
    • procedure -> ""
    • type of event -> ""
    • status -> t("case:summary-no-date-entered-label")
    • decision -> t("case:summary-no-information-to-date-label")
  • Intentionally left unchanged in components/myportal/viewall.js:
    • authority fallback using searchDetailsObj[key].value / nested .value[0] shape because the fallback source is conditional and structure-dependent
    • case type fallback using getFormCollectionByID(searchDetailsObj[key].appealType).value in the isMyRepresentations branch because removing it could alter non-CRM derived display behaviour

Validation:

  • Pending in current slice:
    • node tests/phase22/crm-display-resolver.test.cjs
    • eslint on touched files only

Follow-ups:

  • If desired, the remaining components/myportal/viewall.js fallbacks should be handled in a separate characterization-first slice because they combine conditional view-specific data shapes with derived values.

CL-2026-07-01-CRM-DISPLAY-RESOLVER-STALE-JSONPATH-IMPORTS: remove stale JSONPath imports from adopted summaries

date: 2026-07-01 author: Cline scope: components/case/summaryTypes/pinswg_advertsid.js, components/case/summaryTypes/pinswg_communityinfrastructurelevyid.js, components/case/summaryTypes/pinswg_environmentalpermittingid.js, components/case/summaryTypes/pinswg_lawfuldevelopmentcertificatappid.js, components/case/summaryTypes/pinswg_maintenanceoflands217id.js, components/case/summaryTypes/pinswg_miscellaneouscasewordid.js, components/case/summaryTypes/pinswg_planningobligationappeals106id.js, components/case/summaryTypes/pinswg_wayleaveid.js, components/case/summaryTypes/pinswg_compulsorypurchaseordersid.js, memory-bank/change-log.md type: change rationale: Clean up stale JSONPath imports in already-adopted summary files where active jsonpath(...) usage no longer remains, without altering any runtime translation or rendering logic. impact: Behaviour-preserving cleanup only. Removed unused JSONPath as jsonpath imports from nine adopted summary files. No helper logic, summary row behavior, fallback behavior, translation data, tests, routes, or linked-case logic changed. status: completed

Summary:

  • Continued on branch:
    • feature/crm-display-resolver-slice1
  • Narrowed cleanup scope followed exactly:
    • only the fixed user-provided adopted summary file list was inspected
    • only files with a stale import { JSONPath as jsonpath } from "jsonpath-plus"; and no active jsonpath(...) call were edited
  • Stale import removed from:
    • components/case/summaryTypes/pinswg_advertsid.js
    • components/case/summaryTypes/pinswg_communityinfrastructurelevyid.js
    • components/case/summaryTypes/pinswg_environmentalpermittingid.js
    • components/case/summaryTypes/pinswg_lawfuldevelopmentcertificatappid.js
    • components/case/summaryTypes/pinswg_maintenanceoflands217id.js
    • components/case/summaryTypes/pinswg_miscellaneouscasewordid.js
    • components/case/summaryTypes/pinswg_planningobligationappeals106id.js
    • components/case/summaryTypes/pinswg_wayleaveid.js
    • components/case/summaryTypes/pinswg_compulsorypurchaseordersid.js
  • Intentionally left unchanged because they had no stale JSONPath import in the narrowed list:
    • pinswg_callinss77id.js
    • pinswg_commonlandid.js
    • pinswg_enforcementlistedbuildingconseid.js
    • pinswg_enforcementnoticeappeals174id.js
    • pinswg_householderappealhasid.js
    • pinswg_ldpid.js
    • pinswg_listedbuildingandconservationrid.js
    • pinswg_nonvalidationid.js
    • pinswg_planningconditionss73s79id.js
    • pinswg_rowid.js
  • No runtime logic changed:
    • no active jsonpath(...) branches were edited
    • no helper calls or fallback values were changed
    • no unrelated working-tree changes were touched

Validation:

  • CI=1 ./node_modules/.bin/eslint components/case/summaryTypes/pinswg_advertsid.js components/case/summaryTypes/pinswg_communityinfrastructurelevyid.js components/case/summaryTypes/pinswg_environmentalpermittingid.js components/case/summaryTypes/pinswg_lawfuldevelopmentcertificatappid.js components/case/summaryTypes/pinswg_maintenanceoflands217id.js components/case/summaryTypes/pinswg_miscellaneouscasewordid.js components/case/summaryTypes/pinswg_planningobligationappeals106id.js components/case/summaryTypes/pinswg_wayleaveid.js components/case/summaryTypes/pinswg_compulsorypurchaseordersid.js

Follow-ups:

  • Any further JSONPath cleanup should stay characterization-first and should only remove imports when active jsonpath(...) usage is absent in the exact target file.

CL-2026-06-30-CRM-DISPLAY-RESOLVER-BUGFIX: fix CRM display resolver fallback behaviour

date: 2026-06-30 author: Cline scope: tests/phase22/crm-display-resolver.test.cjs, components/search/addresssearchresults.js, components/myportal/viewall.js, components/case/summaryTypes/pinswg_nonvalidationid.js, components/case/summaryTypes/pinswg_ldpid.js, components/case/summaryTypes/pinswg_householderappealhasid.js, components/case/summaryTypes/pinswg_callinss77id.js, components/case/summaryTypes/pinswg_environmentalpermittingid.js, components/case/summaryTypes/pinswg_dnsid.js, components/case/summaryTypes/pinswg_sipscase.js, memory-bank/change-log.md type: change rationale: A manual verification defect showed some consumers were compensating for the prior resolver contract by passing fallbackValue values that duplicated the CRM display value. After the helper was corrected to prefer the CRM display value whenever present, those redundant fallbacks needed to be re-characterized in tests and simplified without changing user-facing behaviour. impact: Behaviour-preserving CRM display helper bug-fix slice. Tests now verify the corrected contract for English, Welsh, and missing-value cases. Redundant fallbackValue: value || ... and fallbackValue: value || "" patterns were simplified only where the fallback merely duplicated the CRM display value. No helper API, translation datasets, routes, CRM queries, lifecycle logic, business policy, rendering structure, or field names changed. status: completed

Summary:

  • Continued on existing branch:
    • feature/crm-display-resolver-slice1
  • Helper behavioural contract verified by characterization tests:
    • if no CRM display value exists, return fallbackValue
    • English always returns the CRM display value
    • Welsh returns the Welsh translation when available
    • Welsh otherwise returns the CRM display value
  • Tests updated to cover:
    • English with value returns value
    • Welsh with translation returns Welsh
    • Welsh without translation returns English value
    • Missing value returns fallback
    • Empty string returns fallback
    • Null returns fallback
    • Undefined returns fallback
  • Redundant fallback simplifications applied only where safe:
    • components/search/addresssearchresults.js
    • components/myportal/viewall.js
    • components/case/summaryTypes/pinswg_nonvalidationid.js
    • components/case/summaryTypes/pinswg_ldpid.js
    • components/case/summaryTypes/pinswg_householderappealhasid.js
    • components/case/summaryTypes/pinswg_callinss77id.js
    • components/case/summaryTypes/pinswg_environmentalpermittingid.js
    • components/case/summaryTypes/pinswg_dnsid.js
    • components/case/summaryTypes/pinswg_sipscase.js
  • Left unchanged where fallback meaning differed from the CRM display value or was ambiguous.
  • Behaviour preserved:
    • no helper API change
    • no translation data change
    • no route or CRM query change
    • no lifecycle or business-policy change
    • no rendering-structure or field-name change

Validation:

  • Required:
    • node tests/phase22/crm-display-resolver.test.cjs
    • npx eslint tests/phase22/crm-display-resolver.test.cjs components/search/addresssearchresults.js components/myportal/viewall.js components/case/summaryTypes/pinswg_nonvalidationid.js components/case/summaryTypes/pinswg_ldpid.js components/case/summaryTypes/pinswg_householderappealhasid.js components/case/summaryTypes/pinswg_callinss77id.js components/case/summaryTypes/pinswg_environmentalpermittingid.js components/case/summaryTypes/pinswg_dnsid.js components/case/summaryTypes/pinswg_sipscase.js
  • Optional baseline check:
    • node tests/phase22/index.test.cjs
    • unrelated existing drift retained in tests/phase22/case-service-behaviour.test.cjs

Follow-ups:

  • None in this slice.

CL-2026-06-30-CRM-DISPLAY-RESOLVER-SUMMARY-BATCH: adopt CRM display resolver in summary batch

date: 2026-06-30 author: Cline scope: components/case/summaryTypes/pinswg_maintenanceoflands217id.js, components/case/summaryTypes/pinswg_lawfuldevelopmentcertificatappid.js, components/case/summaryTypes/pinswg_communityinfrastructurelevyid.js, components/case/summaryTypes/pinswg_planningobligationappeals106id.js, memory-bank/change-log.md type: change rationale: Continue the CRM display resolver adoption stream in one bounded summary batch by migrating only structurally equivalent direct CRM formatted/display value translation branches to the already-verified resolver helper, while leaving unrelated working-tree changes and non-compatible summary concerns untouched. impact: Behaviour-preserving maintainability slice. Four structurally equivalent summary types now use resolveCrmDisplayValue(...) for compatible CRM display-value branches only. No helper contract, tests, translation datasets, routes, CRM queries, lifecycle logic, linked-case behaviour, or rendering structure changed. status: completed

Summary:

  • Branch confirmed before work:
    • feature/crm-display-resolver-slice1
  • Protected/unrelated files left untouched:
    • components/case/summaryTypes/pinswg_planningappeals78id.js
    • .env.local
    • untracked analysis files
  • Summary types adopted in this batch:
    • components/case/summaryTypes/pinswg_maintenanceoflands217id.js
    • components/case/summaryTypes/pinswg_lawfuldevelopmentcertificatappid.js
    • components/case/summaryTypes/pinswg_communityinfrastructurelevyid.js
    • components/case/summaryTypes/pinswg_planningobligationappeals106id.js
  • Characterization findings across all four files:
    • English behaviour:
      • direct CRM formatted/display values were rendered for case type, EIA required, procedure, type of event, status, and decision
    • Welsh behaviour:
      • the same fields used direct JSONPath lookup against lookuptranslations.json and returned value_cy
    • Compatible CRM formatted/display value branches:
      • case type
      • EIA required
      • procedure
      • type of event
      • status
      • decision
    • Fallback behaviour preserved exactly:
      • case type -> t("case:summary-no-date-entered-label")
      • EIA required -> t("case:summary-to-be-confirmed-label")
      • procedure -> ""
      • type of event -> ""
      • status -> t("case:summary-no-date-entered-label")
      • decision -> t("case:summary-no-information-to-date-label")
    • Empty/null handling preserved via existing helper contract and unchanged surrounding summary logic
    • Consumer-specific behaviour left unchanged:
      • LPA display via getCaseLpaDisplayLines(...)
      • linked case logic
      • key dates/lifecycle interpretation
      • commented legacy blocks
  • All four files were structurally equivalent and safe, so none needed to be skipped.

Validation:

  • node tests/phase22/crm-display-resolver.test.cjs
  • npx eslint components/case/summaryTypes/pinswg_maintenanceoflands217id.js components/case/summaryTypes/pinswg_lawfuldevelopmentcertificatappid.js components/case/summaryTypes/pinswg_communityinfrastructurelevyid.js components/case/summaryTypes/pinswg_planningobligationappeals106id.js lib/i18n/crmDisplay/resolveCrmDisplayValue.js

Follow-ups:

  • Remaining adoption backlog should continue only in bounded summary-type slices, excluding already migrated files and any files with unrelated working-tree changes.

CL-2026-06-30-CRM-DISPLAY-RESOLVER-CLOSEOUT: close out CRM Display Infrastructure resolver adoption stream

CL-2026-06-30-CRM-DISPLAY-RESOLVER-SUMMARY-BATCH-3: continue CRM display resolver summary adoption

date: 2026-06-30 author: Cline scope: components/case/summaryTypes/pinswg_commonlandid.js, components/case/summaryTypes/pinswg_miscellaneouscasewordid.js, components/case/summaryTypes/pinswg_wayleaveid.js, components/case/summaryTypes/pinswg_hedgeshedgerowstreepreservatioid.js, memory-bank/change-log.md type: change rationale: Continue the summary-only CRM display resolver adoption stream in one bounded batch by replacing only structurally safe CRM formatted/display-value translation branches in the next likely-safe summaries, while preserving protected files and intentionally retained keyed/mixed translation concerns. impact: Behaviour-preserving maintainability slice. Four additional summary types now delegate compatible single-value CRM display translation branches to resolveCrmDisplayValue(...). No helper contract change, no relevant-authority keyed lookup change, no specialist-process policy change, and no route/lifecycle/linked-case/business-policy change. status: completed

Summary:

  • Continued on existing branch:
    • feature/crm-display-resolver-slice1
  • Previous commit before this slice:
    • 519a9fda (Continue CRM display resolver summary adoption)
  • Protected/unrelated working-tree items left untouched:
    • .env.local
    • untracked summary analysis files
    • components/case/summaryTypes/pinswg_planningappeals78id.js
  • Summary types adopted in this slice:
    • components/case/summaryTypes/pinswg_commonlandid.js
    • components/case/summaryTypes/pinswg_miscellaneouscasewordid.js
    • components/case/summaryTypes/pinswg_wayleaveid.js
    • components/case/summaryTypes/pinswg_hedgeshedgerowstreepreservatioid.js
  • Summary types skipped:
    • none of the requested four were skipped
  • Characterization findings:
    • Shared compatible CRM formatted/display-value branches confirmed in all four files:
      • case type
      • EIA required
      • procedure
      • type of event
      • status
      • decision
    • English behaviour:
      • these branches rendered direct CRM formatted/display values
    • Welsh behaviour:
      • these branches used direct JSONPath lookup into lookuptranslations.json and returned value_cy
    • Fallback behaviour preserved exactly:
      • case type -> t("case:summary-no-date-entered-label")
      • EIA required -> t("case:summary-to-be-confirmed-label")
      • procedure -> ""
      • type of event -> ""
      • status -> t("case:summary-no-date-entered-label")
      • decision -> t("case:summary-no-information-to-date-label")
    • Empty/null handling preserved through the verified helper contract plus unchanged surrounding summary logic
    • Consumer-specific behaviour left unchanged:
      • pinswg_commonlandid.js / pinswg_miscellaneouscasewordid.js / pinswg_wayleaveid.js
        • relevant-authority lookup via pinswg_lpa keyed translation branch
      • pinswg_hedgeshedgerowstreepreservatioid.js
        • specialist case process translation branch
        • relevant-authority lookup via pinswg_lpa keyed translation branch
      • LPA rendering via getCaseLpaDisplayLines(...)
      • linked-case rendering and status
      • date sections and field-specific source variations
  • Safe-adoption exclusions observed:
    • no keyed lookup translation branches changed
    • no pinswg_lpa lookup logic changed
    • no specialist-case-process mixed branch changed
    • no linked-case, lifecycle, or business-policy logic changed

Validation:

  • node tests/phase22/crm-display-resolver.test.cjs
  • npx eslint components/case/summaryTypes/pinswg_commonlandid.js components/case/summaryTypes/pinswg_miscellaneouscasewordid.js components/case/summaryTypes/pinswg_wayleaveid.js components/case/summaryTypes/pinswg_hedgeshedgerowstreepreservatioid.js

Follow-ups:

  • Remaining summary backlog should continue only in bounded batches where the standard CRM display-value translation pattern can be isolated without touching keyed/mixed branches.

CL-2026-06-30-CRM-DISPLAY-RESOLVER-SUMMARY-BATCH-2: continue CRM display resolver summary adoption

date: 2026-06-30 author: Cline scope: components/case/summaryTypes/pinswg_enforcementnoticeappeals174id.js, components/case/summaryTypes/pinswg_enforcementlistedbuildingconseid.js, components/case/summaryTypes/pinswg_listedbuildingandconservationrid.js, components/case/summaryTypes/pinswg_planningconditionss73s79id.js, memory-bank/change-log.md type: change rationale: Continue the summary-only CRM display resolver adoption stream in one bounded batch by replacing only the remaining structurally identical CRM formatted/display-value translation branches, while preserving protected files, helper boundaries, and any non-matching translation patterns. impact: Behaviour-preserving maintainability slice. Four additional summary types now delegate the standard CRM display-value translation path to resolveCrmDisplayValue(...) for compatible rows only. No helper API or helper behaviour change, no translation-data change, no keyed/list/hyperlink/LPA-specific translation change, and no route, lifecycle, post-decision, linked-case, or business-policy change. status: completed

Summary:

  • Continued on existing branch:
    • feature/crm-display-resolver-slice1
  • Previous commit before this slice:
    • b56c8d46 (Adopt CRM display resolver in summary batch)
  • Protected/unrelated working-tree items left untouched:
    • .env.local
    • untracked analysis files
    • components/case/summaryTypes/pinswg_planningappeals78id.js
  • Summary types adopted in this slice:
    • components/case/summaryTypes/pinswg_enforcementnoticeappeals174id.js
    • components/case/summaryTypes/pinswg_enforcementlistedbuildingconseid.js
    • components/case/summaryTypes/pinswg_listedbuildingandconservationrid.js
    • components/case/summaryTypes/pinswg_planningconditionss73s79id.js
  • Characterization findings across the selected files:
    • Compatible CRM formatted/display-value branches matched exactly:
      • case type
      • EIA required
      • procedure
      • type of event
      • status
      • decision
    • English behaviour before adoption:
      • each compatible row rendered the direct CRM formatted/display value
    • Welsh behaviour before adoption:
      • each compatible row used direct jsonpath lookup into lookuptranslations.json and returned value_cy
    • Fallback behaviour preserved exactly:
      • case type -> t("case:summary-no-date-entered-label")
      • EIA required -> t("case:summary-to-be-confirmed-label")
      • procedure -> ""
      • type of event -> ""
      • status -> t("case:summary-no-date-entered-label")
      • decision -> t("case:summary-no-information-to-date-label")
    • Empty/null handling preserved via the verified helper contract and unchanged surrounding row logic
    • Consumer-specific behaviour left unchanged:
      • LPA rendering via getCaseLpaDisplayLines(...)
      • linked-case rendering and case-link status
      • date sections and field-specific date-source differences
      • lifecycle/status interpretation beyond direct CRM display rows
      • any non-selected translation concerns in other files
  • Safe-adoption exclusions observed:
    • no keyed lookup translations changed
    • no pinswg_lpa lookup logic changed
    • no project-type list translation changed
    • no hyperlink/website rendering changed
    • no map/project-description/linked-case/post-decision/business-policy logic changed

Validation:

  • node tests/phase22/crm-display-resolver.test.cjs
  • npx eslint components/case/summaryTypes/pinswg_enforcementnoticeappeals174id.js components/case/summaryTypes/pinswg_enforcementlistedbuildingconseid.js components/case/summaryTypes/pinswg_listedbuildingandconservationrid.js components/case/summaryTypes/pinswg_planningconditionss73s79id.js

Follow-ups:

  • Remaining summary backlog should continue in bounded batches only for files still using direct JSONPath CRM display translation for the standard single-value pattern.

date: 2026-06-30 author: Cline scope: memory-bank/change-log.md type: milestone rationale: Close out the completed CRM Display Infrastructure resolver adoption stream on feature/crm-display-resolver-slice1 with one documentation-only checkpoint before any follow-on stream begins. impact: Documentation/governance only; records completed adoption scope, helper boundary, preserved-behaviour guarantees, explicit non-goals, known unrelated baseline drift, and next-stream recommendation. No runtime, API, route, auth/session, CRM, relay, storage, Notify, PDF, or workflow behaviour change. status: completed

Summary:

  • Workstream:
    • CRM Display Infrastructure — resolver adoption stream
  • Branch:
    • feature/crm-display-resolver-slice1
  • Shared helper introduced and retained unchanged across the stream:
    • lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Focused helper characterization coverage retained:
    • tests/phase22/crm-display-resolver.test.cjs
  • Helper responsibility recorded as:
    • standard CRM formatted/display value -> Welsh lookup (lookuptranslations.json -> value_cy) -> existing consumer fallback
  • Adopted consumers completed in this stream:
    • components/admin/tabs/appeals.js
    • components/search/searchresults.js
    • components/search/addresssearchresults.js
    • components/search/dnssearchresults.js
    • components/myportal/viewall.js
    • components/case/summaryTypes/pinswg_nonvalidationid.js
    • components/case/summaryTypes/pinswg_ldpid.js
    • components/case/summaryTypes/pinswg_householderappealhasid.js
    • components/case/summaryTypes/pinswg_callinss77id.js
    • components/case/summaryTypes/pinswg_environmentalpermittingid.js
    • components/case/summaryTypes/pinswg_dnsid.js
    • components/case/summaryTypes/pinswg_sipscase.js
  • Preserved behaviour guarantees across the stream:
    • helper API remained unchanged
    • consumer-specific fallback behaviour was preserved
    • empty-string/null behaviour was preserved per consumer
    • incompatible or mixed concerns were intentionally left unchanged
    • no API, route, auth/session, CRM query, relay, storage, Notify, PDF, or workflow behaviour was changed
  • Explicit helper boundary / non-goals recorded:
    • helper owns only standard CRM formatted/display value -> Welsh lookup -> fallback
    • helper does not own:
      • keyed lookup translation
      • list item translation
      • hyperlink-mixed rendering
      • relevant-authority lookup
      • project-type translation
      • lifecycle/status interpretation
      • post-decision interpretation
  • Known unrelated baseline drift retained and not fixed in this stream:
    • tests/phase22/case-service-behaviour.test.cjs
    • legacy expected route: /api/endpoint/getportalmoduledetails_api
    • current route: /api/myportal/get-portal-module-details
  • Working tree caution retained:
    • .env.local remains modified and uncommitted outside this stream

Validation:

  • Focused documentation validation intended for closeout:
    • git diff --check -- memory-bank/change-log.md

Follow-ups:

  • Recommended next stream:
    • begin a new clearly scoped maintainability stream outside this closeout, likely one of:
      • non-summary CRM display adoption only if a remaining consumer still matches the same helper boundary
      • a separate explicitly approved abstraction stream for non-matching translation shapes (keyed lookups, list translation, hyperlink-mixed rendering)
    • do not broaden resolveCrmDisplayValue() by default.

CL-2026-06-30-CRM-DISPLAY-RESOLVER-SLICE12: adopt shared CRM display resolver in SIPS summary

date: 2026-06-30 author: Cline scope: components/case/summaryTypes/pinswg_sipscase.js, memory-bank/change-log.md type: change rationale: Execute a dedicated high-risk, characterization-first SIPS summary slice by adopting the shared CRM display resolver only for the safe subset of direct CRM formatted/display-value translation branches. impact: Behaviour-preserving maintainability slice in the CRM Compatibility Model / Presentation Infrastructure area. components/case/summaryTypes/pinswg_sipscase.js now delegates a bounded set of repeated bilingual CRM display-value resolution branches to the shared resolver for case type, procedure, ES Submitted, Deemed Marine License Applied for, and decision while preserving project-type list translation, website handling, project description rendering, linked-case behaviour, lifecycle/date/reporting sections, and post-decision/redetermination sections. No route, lifecycle, business policy, or linked-case behaviour change intended. status: completed

Summary:

  • Continued work on existing branch:
    • feature/crm-display-resolver-slice1
  • Previous commit on branch:
    • a7ce84c1
  • Selected summary type:
    • components/case/summaryTypes/pinswg_sipscase.js
  • Characterization findings:
    • English behaviour:
      • case type renders casesObj["pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue"]
      • project type renders a list from detailsObj.pinswg_sips_pinswg_sipsprojecttype
      • website renders a link using detailsObj.pinswg_webaddress
      • procedure renders detailsObj["pinswg_choiceofprocedure@OData.Community.Display.V1.FormattedValue"]
      • ES Submitted renders detailsObj["pinswg_essubmitted@OData.Community.Display.V1.FormattedValue"]
      • Deemed Marine License Applied for renders detailsObj["pinswg_deemedmarinelicenseappliedfor@OData.Community.Display.V1.FormattedValue"]
      • decision renders detailsObj["pinswg_decision@OData.Community.Display.V1.FormattedValue"]
      • redetermination renders detailsObj["pinswg_redetermination@OData.Community.Display.V1.FormattedValue"] directly in the post-decision section
    • Welsh behaviour:
      • safe adopted CRM branches used JSONPath against lookuptranslations.json and returned value_cy
      • project-type list translation uses per-item Welsh lookup and remains distinct from the shared helper abstraction
      • website row also uses Welsh lookup but is mixed with hyperlink rendering and URL formatting
    • JSONPath lookup pattern:
      • safe subset matched:
        • CRM formatted/display value -> lookuptranslations.json -> value_cy
      • project-type list translation is list-based rather than a single summary-row CRM branch
    • fallback behaviour preserved:
      • case type -> original display value or t("case:summary-no-date-entered-label")
      • procedure -> original display value or t("case:summary-to-be-confirmed-label") when source branch is null
      • ES Submitted -> original display value or t("case:summary-to-be-confirmed-label") when source branch is null
      • Deemed Marine License Applied for -> original display value or t("case:summary-to-be-confirmed-label") when source branch is null
      • decision -> original display value or t("case:summary-no-information-to-date-label") when source branch is null
      • project type / website / post-decision sections retain existing bespoke behaviour
    • empty/null handling preserved:
      • helper fallback preserves existing field-specific fallback values for null / undefined / empty-string inputs in adopted branches
      • procedure, ES Submitted, Deemed Marine License Applied for, and decision retain their original outer null-guard branches
      • all SIPS lifecycle/date/reporting sections remain unchanged and continue to rely on existing hasOwn(...), hasValidKey(...), and _.isEmpty(...) checks
    • CRM source fields:
      • adopted branches:
        • pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue
        • pinswg_choiceofprocedure@OData.Community.Display.V1.FormattedValue
        • pinswg_essubmitted@OData.Community.Display.V1.FormattedValue
        • pinswg_deemedmarinelicenseappliedfor@OData.Community.Display.V1.FormattedValue
        • pinswg_decision@OData.Community.Display.V1.FormattedValue
      • unadopted branches / mixed concerns:
        • projectType.pinswg_name list translation
        • pinswg_webaddress
        • pinswg_redetermination@OData.Community.Display.V1.FormattedValue
    • project-type list translation behaviour:
      • per-project-type item translation through projectType.pinswg_name
      • intentionally left unchanged because it is a list-based concern, not a simple single-value CRM display branch
    • website behaviour:
      • English renders hyperlink markup
      • Welsh uses lookup + link-related logic in same branch
      • intentionally left unchanged
    • SIPS-specific lifecycle/date/reporting sections:
      • extensive pre-submission, submission, reporting/decision, and post-decision sections remain unchanged
      • case deadline uses latest extension selection logic and remains unchanged
    • post-decision/redetermination sections:
      • redetermination and redetermination consultation deadline remain unchanged
      • although redetermination uses a formatted value, it sits inside a post-decision interpretation area and was left unchanged for scope safety
    • linked-case behaviour:
      • existing numberofchildincidents / linkedCasesList(...) rendering remains unchanged
    • map/project-specific rendering:
      • project description rendering remains unchanged
      • project type list rendering remains unchanged
      • imported map-related concerns remain untouched in this slice
    • option-set / yes-no translations:
      • ES Submitted is a direct option-set style formatted-value branch and was safely adoptable
      • Deemed Marine License Applied for is likewise safely adoptable as a direct formatted-value branch
    • branches compatible with resolveCrmDisplayValue(...):
      • case type
      • procedure
      • ES Submitted
      • Deemed Marine License Applied for
      • decision
    • branches intentionally left unchanged:
      • project-type list translation
      • website / hyperlink rendering
      • project description rendering
      • linked-case rendering
      • all SIPS lifecycle/date/reporting sections
      • post-decision/redetermination section
      • commented-out status/type-of-event/recommendation branches
  • Safe branches adopted:
    • case type
    • procedure
    • ES Submitted
    • Deemed Marine License Applied for
    • decision
  • Branches left unchanged:
    • project-type list translation
      • left unchanged because it is a per-item list translation concern rather than a simple single CRM display-value branch
    • website row
      • left unchanged because it mixes Welsh lookup with hyperlink rendering and URL formatting
    • project description rendering
    • linked-case rendering
    • all SIPS lifecycle/date/reporting sections
    • post-decision/redetermination section
      • left unchanged because it sits inside a post-decision interpretation area and changing it would broaden scope beyond straightforward display-value resolution
  • Consumer-specific nuances preserved:
    • procedure retains to-be-confirmed fallback when source branch is null
    • ES Submitted retains to-be-confirmed fallback when source branch is null
    • Deemed Marine License Applied for retains to-be-confirmed fallback when source branch is null
    • decision retains no-information-to-date fallback when source branch is null
    • project-type list translation remains per-item and unchanged
    • website link behaviour remains unchanged
    • post-decision/redetermination behaviour remains unchanged
  • Helper assessment:
    • no helper API changes were required
    • no new reusable abstraction emerged
    • resolveCrmDisplayValue() remains the correct abstraction for the standard CRM formatted/display-value translation concern only

Validation:

  • Passed:
    • node tests/phase22/crm-display-resolver.test.cjs
    • npx eslint components/case/summaryTypes/pinswg_sipscase.js lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Optional broader suite run (informational baseline):
    • node tests/phase22/index.test.cjs
    • existing unrelated baseline drift remains in tests/phase22/case-service-behaviour.test.cjs
    • observed mismatch remains between expected legacy route /api/endpoint/getportalmoduledetails_api and current route /api/myportal/get-portal-module-details
    • no change made to that unrelated baseline in this slice

Follow-ups:

  • CRM Display Infrastructure adoption backlog status:
    • the previously targeted high-risk summary surfaces in this sequence have now been adopted where safe
    • remaining future work, if any, should focus on non-summary consumers or on separate, explicitly approved abstraction work for translation shapes that do not match the current helper (for example keyed lookup/list translation concerns)

CL-2026-06-30-CRM-DISPLAY-RESOLVER-SLICE11A: adopt shared CRM display resolver in DNS summary

date: 2026-06-30 author: Cline scope: components/case/summaryTypes/pinswg_dnsid.js, memory-bank/change-log.md type: change rationale: Start the DNS/SIPS summary adoption batch with the preferred DNS candidate, adopting the shared CRM display resolver only for the safe subset of repeated CRM display-value translation branches. impact: Behaviour-preserving maintainability slice in the CRM Compatibility Model / Presentation Infrastructure area. components/case/summaryTypes/pinswg_dnsid.js now delegates the standard repeated bilingual CRM display-value resolution branches to the shared resolver for case type, EIA required, procedure, type of event, and decision while preserving DNS-specific project/map behaviour, website rendering, fallback behaviour, linked-case rendering, and date/lifecycle sections. No route, lifecycle, business policy, or linked-case behaviour change intended. status: completed

Summary:

  • Continued work on existing branch:
    • feature/crm-display-resolver-slice1
  • Previous commit on branch:
    • 365fce2e
  • Summary types reviewed in this batch stage:
    • components/case/summaryTypes/pinswg_dnsid.js
  • Summary types adopted in this stage:
    • components/case/summaryTypes/pinswg_dnsid.js
  • Characterization findings for DNS summary:
    • English behaviour:
      • case type renders casesObj["pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue"]
      • website renders a link using detailsObj.pinswg_webaddress
      • EIA required renders detailsObj["pinswg_eiarequired@OData.Community.Display.V1.FormattedValue"]
      • procedure renders detailsObj["pinswg_choiceofprocedure@OData.Community.Display.V1.FormattedValue"]
      • type of event renders detailsObj["pinswg_typeofevent@OData.Community.Display.V1.FormattedValue"]
      • decision renders detailsObj["pinswg_decision@OData.Community.Display.V1.FormattedValue"]
    • Welsh behaviour:
      • the adopted CRM display branches used JSONPath against lookuptranslations.json and returned value_cy
      • website row also used JSONPath in Welsh, but it is not a standard CRM formatted/display-value branch and remains unchanged
    • duplicated JSONPath lookup pattern:
      • safe subset matched the established pattern:
        • $..[?(@ && @.value=="<CRM formatted value>")].value_cy
      • one branch (EIA required) used the shorter historical jsonpath(path, transLookup) form but still matched the same translation concern
    • fallback behaviour preserved:
      • case type -> original display value or t("case:summary-no-date-entered-label")
      • EIA required -> original display value or t("case:summary-to-be-confirmed-label")
      • procedure -> original display value or t("case:summary-to-be-confirmed-label") when source branch is null
      • type of event -> original display value or empty string
      • decision -> original display value or t("case:summary-no-information-to-date-label") when source branch is null
      • website -> unchanged existing behaviour
    • empty/null handling preserved:
      • helper fallback preserves existing field-specific fallback values for null / undefined / empty-string inputs in adopted branches
      • procedure/decision retain their original outer null-guard branches
      • DNS-specific date sections remain unchanged and continue to use existing hasOwn(...) / _.isEmpty(...) checks
    • CRM source fields:
      • adopted branches:
        • pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue
        • pinswg_eiarequired@OData.Community.Display.V1.FormattedValue
        • pinswg_choiceofprocedure@OData.Community.Display.V1.FormattedValue
        • pinswg_typeofevent@OData.Community.Display.V1.FormattedValue
        • pinswg_decision@OData.Community.Display.V1.FormattedValue
      • unadopted mixed concerns:
        • pinswg_webaddress
        • project description/map inputs
    • DNS-specific derived values:
      • project description section uses getBilingualText(...)
      • map section derives coordinates through OSPoint and MapComponent
      • these were intentionally left unchanged
    • lifecycle/status interpretation:
      • active status row is commented out in this summary and was not changed
      • no lifecycle derivation was altered
    • linked-case behaviour:
      • existing numberofchildincidents / linkedCasesList(...) rendering remains unchanged
    • map/project-specific sections:
      • project description section unchanged
      • website row unchanged
      • map rendering unchanged
    • option-set / yes-no translations:
      • EIA required remains safely adoptable
    • branches compatible with resolveCrmDisplayValue(...):
      • case type
      • EIA required
      • procedure
      • type of event
      • decision
    • branches intentionally left unchanged:
      • website row
      • project description section
      • map rendering
      • linked-case rendering
      • all DNS-specific date/lifecycle sections
      • commented-out status/event branches
  • Safe branches adopted:
    • case type
    • EIA required
    • procedure
    • type of event
    • decision
  • Branches left unchanged:
    • website row
      • left unchanged because it mixes Welsh lookup behaviour with hyperlink rendering and URL formatting
    • project description / map sections
      • left unchanged because they are DNS-specific derived display concerns
    • linked-case rendering
    • all DNS date/lifecycle/reporting sections
    • commented-out status/event/recommendation branches
  • Consumer-specific nuances preserved:
    • procedure keeps to-be-confirmed fallback when source branch is null
    • type of event keeps empty-string fallback
    • decision keeps no-information-to-date fallback when source branch is null
    • website link behaviour remains unchanged
    • project/map rendering remains unchanged
  • Helper assessment:
    • no helper API changes were required
    • no new reusable abstraction emerged from the DNS summary
    • resolveCrmDisplayValue() remains the correct abstraction for the standard CRM formatted/display-value translation concern only

Validation:

  • Passed:
    • node tests/phase22/crm-display-resolver.test.cjs
    • npx eslint components/case/summaryTypes/pinswg_dnsid.js lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Optional broader suite run (informational baseline):
    • node tests/phase22/index.test.cjs
    • existing unrelated baseline drift remains in tests/phase22/case-service-behaviour.test.cjs
    • observed mismatch remains between expected legacy route /api/endpoint/getportalmoduledetails_api and current route /api/myportal/get-portal-module-details
    • no change made to that unrelated baseline in this stage

Follow-ups:

  • Batch status after DNS stage:
    • DNS safely adopted and validated
    • SIPS characterization may proceed separately in the same batch only if its safe subset remains equally bounded

CL-2026-06-30-CRM-DISPLAY-RESOLVER-SLICE10: adopt shared CRM display resolver in environmental permitting summary

date: 2026-06-30 author: Cline scope: components/case/summaryTypes/pinswg_environmentalpermittingid.js, memory-bank/change-log.md type: change rationale: Execute the first high-risk, characterization-first summary adoption slice by adopting the shared CRM display resolver only for the safe subset of repeated CRM display-value translation branches in the environmental permitting summary. impact: Behaviour-preserving maintainability slice in the CRM Compatibility Model / Presentation Infrastructure area. components/case/summaryTypes/pinswg_environmentalpermittingid.js now delegates the standard repeated bilingual CRM display-value resolution branches to the shared resolver for case type, EIA required, procedure, type of event, status, and decision while preserving fallback behaviour, linked-case rendering, date handling, summary structure, and the distinct relevant-authority lookup branch. No route, lifecycle, business policy, or linked-case behaviour change intended. status: completed

Summary:

  • Continued work on existing branch:
    • feature/crm-display-resolver-slice1
  • Previous commit on branch:
    • f0deda58
  • Selected summary type:
    • components/case/summaryTypes/pinswg_environmentalpermittingid.js
  • Characterization findings:
    • English behaviour:
      • case type renders casesObj["pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue"]
      • EIA required renders detailsObj["pinswg_eiarequired@OData.Community.Display.V1.FormattedValue"]
      • procedure renders detailsObj["pinswg_procedure@OData.Community.Display.V1.FormattedValue"]
      • type of event renders detailsObj["pinswg_typeofevent@OData.Community.Display.V1.FormattedValue"]
      • status renders casesObj["statuscode@OData.Community.Display.V1.FormattedValue"]
      • decision renders detailsObj["pinswg_decision@OData.Community.Display.V1.FormattedValue"]
      • relevant authority renders an authority-name lookup through pinswg_lpa -> value
    • Welsh behaviour:
      • the six standard CRM display fields used JSONPath against lookuptranslations.json and returned value_cy
      • relevant authority used a different lookup shape through pinswg_lpa -> value_cy
    • duplicated JSONPath lookup pattern:
      • safe subset matched the standard pattern:
        • $..[?(@ && @.value=="<CRM formatted value>")].value_cy
      • one branch (EIA required) used the shorter historical jsonpath(path, transLookup) form but still matched the same translation concern
      • one branch (relevant authority) did not match the helper abstraction because it resolves by pinswg_lpa key rather than by CRM formatted/display value
    • fallback behaviour preserved:
      • case type -> original display value or t("case:summary-no-date-entered-label")
      • EIA required -> original display value or t("case:summary-to-be-confirmed-label")
      • procedure -> original display value or empty string
      • type of event -> original display value or empty string
      • status -> original display value or t("case:summary-no-date-entered-label")
      • decision -> original display value or t("case:summary-no-information-to-date-label")
      • relevant authority -> existing pinswg_lpa JSONPath lookup result or t("case:summary-no-date-entered-label")
    • empty/null handling preserved:
      • helper fallback preserves existing field-specific fallback values for null / undefined / empty-string inputs in the adopted branches
      • relevant authority remains on its existing bespoke lookup path
      • key date rows remain unchanged and still rely on existing hasOwn(...) / _.isEmpty(...) checks
    • CRM source fields:
      • adopted branches:
        • pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue
        • pinswg_eiarequired@OData.Community.Display.V1.FormattedValue
        • pinswg_procedure@OData.Community.Display.V1.FormattedValue
        • pinswg_typeofevent@OData.Community.Display.V1.FormattedValue
        • statuscode@OData.Community.Display.V1.FormattedValue
        • pinswg_decision@OData.Community.Display.V1.FormattedValue
      • unadopted branch:
        • detailsObj.pinswg_relevantauthorityname matched against translation entries using pinswg_lpa
    • linked-case behaviour:
      • existing numberofchildincidents / linkedCasesList(...) rendering remains unchanged
    • lifecycle/status interpretation:
      • direct formatted-value display only; no derived lifecycle logic changed
    • project/map/environmental-permitting-specific sections:
      • no map/project rendering logic was changed in this slice
      • the environmental-permitting-specific relevant authority lookup branch was intentionally preserved unchanged
    • derived display values:
      • none for the adopted CRM display branches
    • option-set/yes-no translations:
      • EIA required remains an option-set/yes-no style translation branch and was safely adoptable
    • branches compatible with resolveCrmDisplayValue(...):
      • case type
      • EIA required
      • procedure
      • type of event
      • status
      • decision
    • branches not compatible without changing behaviour:
      • relevant authority
      • reason: it uses a different translation lookup shape (pinswg_lpa -> value/value_cy) rather than the established CRM formatted/display value -> value_cy helper abstraction
  • Safe branches adopted:
    • case type
    • EIA required
    • procedure
    • type of event
    • status
    • decision
  • Branches left unchanged:
    • relevant authority
      • left unchanged because it is not a standard CRM formatted/display-value translation branch and would require broader helper responsibilities or a separate abstraction to preserve behaviour safely
    • linked-case rendering
    • LPA display rendering
    • key-dates rendering
  • Consumer-specific nuances preserved:
    • EIA required retains to-be-confirmed fallback
    • procedure retains empty-string fallback
    • type of event retains empty-string fallback
    • relevant authority retains bespoke pinswg_lpa lookup behaviour in EN/CY
    • section ordering and summary structure remain unchanged
  • Helper assessment:
    • no helper API changes were required
    • no new reusable abstraction emerged
    • resolveCrmDisplayValue() remains the correct abstraction for the standard CRM formatted/display value translation concern only

Validation:

  • Passed:
    • node tests/phase22/crm-display-resolver.test.cjs
    • npx eslint components/case/summaryTypes/pinswg_environmentalpermittingid.js lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Optional broader suite run (informational baseline):
    • node tests/phase22/index.test.cjs
    • existing unrelated baseline drift remains in tests/phase22/case-service-behaviour.test.cjs
    • observed mismatch remains between expected legacy route /api/endpoint/getportalmoduledetails_api and current route /api/myportal/get-portal-module-details
    • no change made to that unrelated baseline in this slice

Follow-ups:

  • Recommended next high-risk slice:
    • components/case/summaryTypes/pinswg_dnsid.js
    • reason: it is the next remaining high-risk candidate and should be approached before pinswg_sipscase.js because DNS-specific behaviour is likely narrower than the broader SIPS case summary concerns.

CL-2026-06-30-CRM-DISPLAY-RESOLVER-SLICE9: adopt shared CRM display resolver in call-in summary

date: 2026-06-30 author: Cline scope: components/case/summaryTypes/pinswg_callinss77id.js, memory-bank/change-log.md type: change rationale: Continue incremental CRM display resolver adoption with one bounded call-in summary slice, replacing only duplicated CRM bilingual display-resolution logic compatible with the established helper abstraction. impact: Behaviour-preserving maintainability slice in the CRM Compatibility Model / Presentation Infrastructure area. components/case/summaryTypes/pinswg_callinss77id.js now delegates repeated bilingual CRM display-value resolution to the shared resolver for case type, EIA required, procedure, type of event, status, and decision while preserving existing fallback behaviour, linked-case rendering, date handling, and summary structure. No route, lifecycle, business policy, or linked-case behaviour change intended. status: completed

Summary:

  • Continued work on existing branch:
    • feature/crm-display-resolver-slice1
  • Previous commit on branch:
    • f08a6ce1
  • Selected summary type:
    • components/case/summaryTypes/pinswg_callinss77id.js
  • Characterization findings:
    • English behaviour:
      • case type renders casesObj["pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue"]
      • EIA required renders detailsObj["pinswg_eiarequired@OData.Community.Display.V1.FormattedValue"]
      • procedure renders detailsObj["pinswg_procedure@OData.Community.Display.V1.FormattedValue"]
      • type of event renders detailsObj["pinswg_typeofevent@OData.Community.Display.V1.FormattedValue"]
      • status renders casesObj["statuscode@OData.Community.Display.V1.FormattedValue"]
      • decision renders detailsObj["pinswg_decision@OData.Community.Display.V1.FormattedValue"]
    • Welsh behaviour:
      • each of the above fields used JSONPath against lookuptranslations.json and returned value_cy
    • duplicated JSONPath lookup pattern:
      • $..[?(@ && @.value=="<CRM formatted value>")].value_cy
      • one branch (EIA required) used the shorter historical jsonpath(path, transLookup) form but still followed the same value -> value_cy translation pattern
    • fallback behaviour preserved:
      • case type -> original display value or t("case:summary-no-date-entered-label")
      • EIA required -> original display value or t("case:summary-to-be-confirmed-label")
      • procedure -> original display value or empty string
      • type of event -> original display value or empty string
      • status -> original display value or t("case:summary-no-date-entered-label")
      • decision -> original display value or t("case:summary-no-information-to-date-label")
    • empty/null handling preserved:
      • helper fallback now preserves existing field-specific fallback values for null / undefined / empty-string inputs
      • key date rows remain unchanged and still rely on existing hasOwn(...) / _.isEmpty(...) checks
    • CRM source fields:
      • pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue
      • pinswg_eiarequired@OData.Community.Display.V1.FormattedValue
      • pinswg_procedure@OData.Community.Display.V1.FormattedValue
      • pinswg_typeofevent@OData.Community.Display.V1.FormattedValue
      • statuscode@OData.Community.Display.V1.FormattedValue
      • pinswg_decision@OData.Community.Display.V1.FormattedValue
    • lifecycle/status interpretation:
      • direct formatted-value display only; no derived lifecycle logic changed
    • linked-case behaviour:
      • existing numberofchildincidents / linkedCasesList(...) rendering remains unchanged
    • additional CRM lookup branches:
      • EIA-required option-set translation branch was present and compatible with the helper abstraction, so it was adopted
    • any derived display values:
      • none for the adopted CRM display branches
  • Adopted CRM lookup branches:
    • case type
    • EIA required
    • procedure
    • type of event
    • status
    • decision
  • Intentionally unadopted branches:
    • none of the duplicated CRM bilingual display-resolution branches in this summary needed to remain unchanged
    • non-CRM summary concerns were intentionally left unchanged:
      • LPA rendering via getCaseLpaDisplayLines(...)
      • linked-case rows
      • all key-date rows
  • Consumer-specific nuances preserved:
    • EIA required retains to-be-confirmed fallback
    • procedure retains empty-string fallback
    • type of event retains empty-string fallback
    • linked-case rows and counts remain untouched
    • date handling and translated labels remain untouched
  • Helper assessment:
    • no helper API changes were required
    • no new reusable abstraction emerged
    • resolveCrmDisplayValue() remains the correct abstraction for this repeated presentation concern

Validation:

  • Passed:
    • node tests/phase22/crm-display-resolver.test.cjs
    • npx eslint components/case/summaryTypes/pinswg_callinss77id.js lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Optional broader suite run (informational baseline):
    • node tests/phase22/index.test.cjs
    • existing unrelated baseline drift remains in tests/phase22/case-service-behaviour.test.cjs
    • observed mismatch remains between expected legacy route /api/endpoint/getportalmoduledetails_api and current route /api/myportal/get-portal-module-details
    • no change made to that unrelated baseline in this slice

Follow-ups:

  • Recommended first high-risk adoption candidate:
    • components/case/summaryTypes/pinswg_environmentalpermittingid.js
    • reason: it is structurally closer to the now-proven summary pattern than the DNS/SIPS-heavy summaries and introduces one extra relevant-authority CRM lookup branch without the added map/project complexity present in higher-risk DNS/SIPS variants.

CL-2026-06-30-CRM-DISPLAY-RESOLVER-SLICE8: adopt shared CRM display resolver in householder summary

date: 2026-06-30 author: Cline scope: components/case/summaryTypes/pinswg_householderappealhasid.js, memory-bank/change-log.md type: change rationale: Continue incremental CRM display resolver adoption with one bounded householder summary slice, replacing only duplicated CRM bilingual display-resolution logic that matches the established helper abstraction. impact: Behaviour-preserving maintainability slice in the CRM Compatibility Model / Presentation Infrastructure area. components/case/summaryTypes/pinswg_householderappealhasid.js now delegates repeated bilingual CRM display-value resolution to the shared resolver for case type, EIA required, procedure, status, and decision while preserving existing fallback behaviour, linked-case rendering, key date handling, and summary structure. No route, lifecycle, business policy, or linked-case behaviour change intended. status: completed

Summary:

  • Continued work on existing branch:
    • feature/crm-display-resolver-slice1
  • Previous commit on branch:
    • 40c87fa4
  • Selected summary type:
    • components/case/summaryTypes/pinswg_householderappealhasid.js
  • Characterization findings:
    • English behaviour:
      • case type renders casesObj["pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue"]
      • EIA required renders detailsObj["pinswg_eiarequired@OData.Community.Display.V1.FormattedValue"]
      • procedure renders detailsObj["pinswg_procedure@OData.Community.Display.V1.FormattedValue"]
      • status renders casesObj["statuscode@OData.Community.Display.V1.FormattedValue"]
      • decision renders detailsObj["pinswg_decision@OData.Community.Display.V1.FormattedValue"]
    • Welsh behaviour:
      • each of the above fields used JSONPath against lookuptranslations.json and returned value_cy
    • duplicated JSONPath lookup pattern:
      • $..[?(@ && @.value=="<CRM formatted value>")].value_cy
      • one branch (EIA required) used the shorter historical jsonpath(path, transLookup) form but still followed the same value -> value_cy translation pattern
    • fallback behaviour preserved:
      • case type -> original display value or t("case:summary-no-date-entered-label")
      • EIA required -> original display value or t("case:summary-to-be-confirmed-label")
      • procedure -> original display value or empty string
      • status -> original display value or t("case:summary-no-date-entered-label")
      • decision -> original display value or t("case:summary-no-information-to-date-label")
    • empty/null handling preserved:
      • helper fallback now preserves existing field-specific fallback values for null / undefined / empty-string inputs
      • key date rows remain unchanged and still rely on existing hasOwn(...) / _.isEmpty(...) checks
    • CRM source fields:
      • pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue
      • pinswg_eiarequired@OData.Community.Display.V1.FormattedValue
      • pinswg_procedure@OData.Community.Display.V1.FormattedValue
      • statuscode@OData.Community.Display.V1.FormattedValue
      • pinswg_decision@OData.Community.Display.V1.FormattedValue
    • summary-specific derived values:
      • none for the adopted CRM display branches
    • lifecycle/status interpretation:
      • direct formatted-value display only; no extra derived lifecycle logic changed
    • linked-case behaviour:
      • existing numberofchildincidents / linkedCasesList(...) rendering remains unchanged
    • additional CRM lookup branches:
      • EIA-required option-set translation branch was present and was compatible with the existing helper abstraction, so it was adopted
  • CRM lookup branches adopted:
    • case type
    • EIA required
    • procedure
    • status
    • decision
  • Branches intentionally left unchanged:
    • none of the duplicated CRM bilingual display-resolution branches in this summary needed to remain unchanged
    • non-CRM summary concerns were intentionally left unchanged:
      • LPA rendering via getCaseLpaDisplayLines(...)
      • linked-case rows
      • all key-date rows
  • Consumer-specific nuances preserved:
    • EIA required retains to-be-confirmed fallback rather than no-date-entered
    • procedure retains empty-string fallback
    • linked-case rows and counts remain untouched
    • date handling and translated labels remain untouched
  • Helper assessment:
    • no helper API changes were required
    • no new reusable abstraction emerged
    • resolveCrmDisplayValue() remains the correct abstraction for this repeated presentation concern

Validation:

  • Passed:
    • node tests/phase22/crm-display-resolver.test.cjs
    • npx eslint components/case/summaryTypes/pinswg_householderappealhasid.js lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Optional broader suite run (informational baseline):
    • node tests/phase22/index.test.cjs
    • existing unrelated baseline drift remains in tests/phase22/case-service-behaviour.test.cjs
    • observed mismatch remains between expected legacy route /api/endpoint/getportalmoduledetails_api and current route /api/myportal/get-portal-module-details
    • no change made to that unrelated baseline in this slice

Follow-ups:

  • Recommended Slice 9:
    • components/case/summaryTypes/pinswg_callinss77id.js
    • reason: it is the remaining reviewed medium-risk candidate and should be assessed next using the same bounded characterization-first approach.

CL-2026-06-30-CRM-DISPLAY-RESOLVER-SLICE7: adopt shared CRM display resolver in LDP summary type

date: 2026-06-30 author: Cline scope: components/case/summaryTypes/pinswg_ldpid.js, memory-bank/change-log.md type: change rationale: Continue proven incremental CRM display resolver adoption with one carefully selected medium-risk summary type while preserving all summary-specific behaviour. impact: Behaviour-preserving maintainability slice in the CRM Compatibility Model / Presentation Infrastructure area. components/case/summaryTypes/pinswg_ldpid.js now delegates repeated bilingual CRM display-value resolution to the shared resolver for case type, procedure, type of event, status, and decision while preserving existing linked-case behaviour, date rendering, fallback labels, and summary structure. No route, lifecycle interpretation, business policy, or rendering-sequence change intended. status: completed

Summary:

  • Continued work on existing branch:
    • feature/crm-display-resolver-slice1
  • Previous commit on branch:
    • d05c1911
  • Summary types reviewed (medium-risk candidates only):
    • components/case/summaryTypes/pinswg_householderappealhasid.js
    • components/case/summaryTypes/pinswg_callinss77id.js
    • components/case/summaryTypes/pinswg_ldpid.js
  • Selected summary type for Slice 7:
    • components/case/summaryTypes/pinswg_ldpid.js
  • Selection rationale:
    • all three candidates shared the dominant repeated CRM bilingual lookup pattern
    • pinswg_ldpid.js was the safest medium-risk candidate because it has fewer duplicated CRM lookup branches than the other two reviewed files
    • unlike pinswg_householderappealhasid.js and pinswg_callinss77id.js, it does not add an extra EIA-required CRM translation branch
    • it remains read-only, retains straightforward linked-case behaviour, and avoids additional summary-specific CRM display branches beyond the selected repeated pattern
  • Characterization findings for the selected summary type:
    • English behaviour:
      • case type renders casesObj["pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue"]
      • procedure renders detailsObj["pinswg_procedure@OData.Community.Display.V1.FormattedValue"]
      • type of event renders detailsObj["pinswg_typeofevent@OData.Community.Display.V1.FormattedValue"]
      • status renders casesObj["statuscode@OData.Community.Display.V1.FormattedValue"]
      • decision renders detailsObj["pinswg_decision@OData.Community.Display.V1.FormattedValue"]
    • Welsh behaviour:
      • each of the above fields used JSONPath against lookuptranslations.json and returned value_cy
    • duplicated JSONPath lookup pattern:
      • $..[?(@ && @.value=="<CRM formatted value>")].value_cy
    • fallback behaviour preserved:
      • case type -> original display value or t("case:summary-no-date-entered-label")
      • procedure -> original display value or empty string
      • type of event -> original display value or empty string
      • status -> original display value or t("case:summary-no-date-entered-label")
      • decision -> original display value or t("case:summary-no-information-to-date-label")
    • empty/null handling preserved:
      • helper fallback now preserves the existing field-specific fallback values for null/undefined/empty inputs
      • all date rows remain unchanged and still rely on existing hasOwn(...) / _.isEmpty(...) checks
    • CRM source fields preserved:
      • pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue
      • pinswg_procedure@OData.Community.Display.V1.FormattedValue
      • pinswg_typeofevent@OData.Community.Display.V1.FormattedValue
      • statuscode@OData.Community.Display.V1.FormattedValue
      • pinswg_decision@OData.Community.Display.V1.FormattedValue
    • summary-specific derived values:
      • none for the adopted CRM display fields
    • lifecycle/status interpretation:
      • direct formatted-value display only; no extra derived lifecycle logic added or changed
    • linked-case behaviour:
      • existing numberofchildincidents / linkedCasesList(...) behaviour remains unchanged
  • Consumer-specific nuances preserved:
    • LPA rendering remains via getCaseLpaDisplayLines(...)
    • linked-case rows remain unchanged
    • all key-dates rendering remains unchanged
    • no attempt made to standardize empty-string vs label fallback behaviour across fields
  • Helper assessment:
    • no helper API changes were required
    • no new reusable abstraction emerged
    • resolveCrmDisplayValue() remains the correct abstraction for this repeated presentation concern

Validation:

  • Passed:
    • node tests/phase22/crm-display-resolver.test.cjs
    • npx eslint components/case/summaryTypes/pinswg_ldpid.js lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Optional broader suite run (informational baseline):
    • node tests/phase22/index.test.cjs
    • existing unrelated baseline drift remains in tests/phase22/case-service-behaviour.test.cjs
    • observed mismatch remains between expected legacy route /api/endpoint/getportalmoduledetails_api and current route /api/myportal/get-portal-module-details
    • no change made to that unrelated baseline in this slice

Follow-ups:

  • Remaining work is now mostly incremental adoption.
  • Recommended next adoption slice:
    • components/case/summaryTypes/pinswg_householderappealhasid.js
    • reason: it is the next closest match to the proven summary pattern and should provide another medium-risk proof before considering pinswg_callinss77id.js.

CL-2026-06-30-CRM-DISPLAY-RESOLVER-SLICE6: adopt shared CRM display resolver in non-validation summary type

date: 2026-06-30 author: Cline scope: components/case/summaryTypes/pinswg_nonvalidationid.js, memory-bank/change-log.md type: change rationale: Prove the CRM display resolver can be adopted safely in one low-risk summary type without broadening summary refactor scope. impact: Behaviour-preserving maintainability slice in the CRM Compatibility Model / Presentation Infrastructure area. components/case/summaryTypes/pinswg_nonvalidationid.js now delegates repeated bilingual CRM display-value resolution to the shared resolver for case type, status, and decision while preserving the existing summary rendering structure, fallback labels, and date handling. No route, payload, lifecycle interpretation, linked-case behaviour, or workflow logic change intended. status: completed

Summary:

  • Continued work on existing branch:
    • feature/crm-display-resolver-slice1
  • Previous commit on branch:
    • 0774783cd54c652947694de7812b5a2cc0f3a5c9
  • Selected summary type for Slice 6:
    • components/case/summaryTypes/pinswg_nonvalidationid.js
  • Summary types reviewed for selection:
    • Low Risk:
      • components/case/summaryTypes/pinswg_nonvalidationid.js
    • Medium Risk:
      • components/case/summaryTypes/pinswg_householderappealhasid.js
      • components/case/summaryTypes/pinswg_callinss77id.js
      • components/case/summaryTypes/pinswg_ldpid.js
    • High Risk:
      • components/case/summaryTypes/pinswg_dnsid.js
      • components/case/summaryTypes/pinswg_sipscase.js
      • components/case/summaryTypes/pinswg_environmentalpermittingid.js
  • Selection rationale:
    • pinswg_nonvalidationid.js is the safest proof candidate because it is small, read-only, and uses the dominant CRM formatted-value -> Welsh lookup pattern in only a few places.
    • It has minimal conditional business logic, no lifecycle/status derivation beyond direct formatted-value display, no linked-case rendering branch, and no map/project/derived summary sections.
  • Characterization findings for the selected summary type:
    • English behaviour:
      • case type renders casesObj["pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue"]
      • status renders casesObj["statuscode@OData.Community.Display.V1.FormattedValue"]
      • decision renders detailsObj["pinswg_decisionissued@OData.Community.Display.V1.FormattedValue"]
    • Welsh behaviour:
      • each of the above fields used JSONPath against lookuptranslations.json and rendered value_cy
    • dominant JSONPath lookup pattern:
      • $..[?(@ && @.value=="<CRM formatted value>")].value_cy
    • fallback behaviour preserved:
      • case type -> original display value or t("case:summary-no-date-entered-label")
      • status -> original display value or t("case:summary-no-date-entered-label")
      • decision -> original display value or t("case:summary-no-information-to-date-label")
    • empty/null handling preserved:
      • helper falls back safely when value is null / undefined / empty string
      • date rows remain unchanged and still use existing hasOwn(...) / _.isEmpty(...) checks
    • CRM source fields preserved:
      • pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue
      • statuscode@OData.Community.Display.V1.FormattedValue
      • pinswg_decisionissued@OData.Community.Display.V1.FormattedValue
    • summary-specific derived values:
      • none for the adopted CRM display fields
  • Consumer-specific nuances preserved:
    • LPA rendering remains delegated to getCaseLpaDisplayLines(...) and was intentionally not changed
    • key dates card logic remains unchanged
    • no attempt made to standardize or expand summary behaviour beyond the duplicated CRM display-resolution logic
  • Helper assessment from this proof:
    • no helper API changes were required
    • no new reusable abstraction emerged from this slice
    • resolveCrmDisplayValue() remains the correct abstraction for this repeated presentation concern

Validation:

  • Passed:
    • node tests/phase22/crm-display-resolver.test.cjs
    • npx eslint components/case/summaryTypes/pinswg_nonvalidationid.js lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Optional broader suite run (informational baseline):
    • node tests/phase22/index.test.cjs
    • existing unrelated baseline drift remains in tests/phase22/case-service-behaviour.test.cjs
    • observed mismatch remains between expected legacy route /api/endpoint/getportalmoduledetails_api and current route /api/myportal/get-portal-module-details
    • no change made to that unrelated baseline in this slice

Follow-ups:

  • CRM Display Infrastructure is now sufficiently proven for continued adoption, but future work should remain incremental and summary-by-summary rather than broad migration.
  • Prefer further low-risk adoptions before considering any wider summary sweep.

CL-2026-06-30-CRM-DISPLAY-RESOLVER-SLICE5: adopt shared CRM display resolver in My Portal view all

date: 2026-06-30 author: Cline scope: components/myportal/viewall.js, memory-bank/change-log.md type: change rationale: Continue CRM Display Infrastructure adoption in one mixed read-only My Portal list/worklist consumer while preserving per-worklist display differences. impact: Behaviour-preserving maintainability slice in the CRM Compatibility Model / Presentation Infrastructure area. components/myportal/viewall.js now delegates repeated Welsh CRM display-value lookup logic to the shared resolver for authority/LPA and case type in several worklist contexts, and for standard formatted-value status display where that pattern already existed. No dashboard policy, route, query parameter, auth/session, Redux shape, or rendering-structure change intended. status: completed

Summary:

  • Continued work on existing branch:
    • feature/crm-display-resolver-slice1
  • Previous commit on branch:
    • d0d971180134347d9e993161f01d9871d25337f6
  • Selected consumer for Slice 5:
    • components/myportal/viewall.js
  • My Portal worklist contexts affected:
    • awaitingSubmissionDetails
    • watchedCases
    • myCases
    • myRepresentations
  • Characterization findings for this consumer:
    • English behaviour: renders original CRM display/formatted values directly in the standard worklist cases
    • Welsh behaviour: uses JSONPath against lookuptranslations.json and renders value_cy for repeated CRM display fields
    • repeated dominant pattern present for:
      • authority/LPA display
      • case type display
      • standard formatted-value status display
    • fallback behaviour differs slightly by worklist and source field:
      • many worklists use original display value or N/A
      • myRepresentations preserves several blank-string fallbacks instead of global N/A
    • empty/null handling remains safe through existing hasOwn(...) checks and branch-specific fallbacks
    • this consumer is more mixed than the search-family slices because source fields vary by worklist and some values are derived from form collection metadata rather than direct CRM formatted values
  • Comparison against prior slices:
    • matches prior slices for repeated formatted-value -> locale -> translation lookup pattern
    • unlike prior search-family slices, this consumer contains multiple worklist-specific branches with different source fields and different fallback semantics
    • awaitingSubmissionDetails status uses statuscode lookup by status-code key rather than the common formatted-value lookup pattern, so that branch was intentionally left unchanged
    • myRepresentations status is a derived created-date display (repRaisedDate(...)), not CRM bilingual display-resolution logic, and was intentionally left unchanged
  • Reused existing shared helper:
    • lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Adopted helper only in the selected consumer and did not modify dashboard/worklist structure or navigation.

Validation:

  • Passed:
    • node tests/phase22/crm-display-resolver.test.cjs
    • npx eslint components/myportal/viewall.js lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Broader suite run (informational baseline):
    • node tests/phase22/index.test.cjs
    • existing unrelated baseline drift remains in tests/phase22/case-service-behaviour.test.cjs, which still expects legacy route /api/endpoint/getportalmoduledetails_api while current repo behaviour uses grouped route /api/myportal/get-portal-module-details
    • no change made to that unrelated test in this slice

Follow-ups:

  • Recommended next slice: move to a low-risk summary proof rather than continue immediately into broader mixed consumers; if CRM display adoption continues, prefer one narrowly scoped summary-type or list-only proof where worklist/state branching is minimal.

CL-2026-06-30-CRM-DISPLAY-RESOLVER-SLICE4: adopt shared CRM display resolver in DNS search results

date: 2026-06-30 author: Cline scope: components/search/dnssearchresults.js, memory-bank/change-log.md type: change rationale: Continue CRM Display Infrastructure adoption in one additional read-only search consumer while preserving DNS-specific derived display behavior. impact: Behaviour-preserving maintainability slice in the CRM Compatibility Model / Presentation Infrastructure area. components/search/dnssearchresults.js now delegates repeated Welsh CRM display-value lookup logic to the shared resolver for authority and status, and reuses it as the first step in DNS/SIP derived type display, without changing the existing DNS-specific derived label behaviour. No CRM query, route, payload, auth/session, or rendering-structure change intended. status: completed

Summary:

  • Continued work on existing branch:
    • feature/crm-display-resolver-slice1
  • Previous commit on branch:
    • 236c707ec2955df9f9a9ad7c93ee751bfc75e89f
  • Selected consumer for Slice 4:
    • components/search/dnssearchresults.js
  • Characterization findings for this consumer:
    • English behaviour: authority and status render original CRM formatted/display values directly
    • Welsh behaviour: authority and status use JSONPath against lookuptranslations.json and render value_cy
    • repeated dominant pattern present for:
      • authority
      • status
    • the DNS/SIP type column has DNS-specific derived display behaviour:
      • first resolves the underlying appeal case type label in EN/CY
      • then collapses the resulting label to either DNS or SIP
    • fallback behaviour:
      • where source field exists, English uses original display value or N/A
      • Welsh falls back to original display value / existing N/A path when translation missing
      • where source field is absent, rendered value remains empty string
    • empty/null handling remains safe through existing hasOwn(...) guards and unchanged fallbacks
  • Comparison against prior slices:
    • matches prior slices for dominant authority/status CRM bilingual lookup pattern
    • unlike Slice 2, no status .trim() behaviour was present and none was introduced
    • unlike Slice 3, there is only one authority source field in the active duplicated resolver usage here
    • unlike all prior slices, this consumer contains a DNS-specific derived type reduction (translated case type -> DNS/SIP)
  • Reused existing shared helper:
    • lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Adopted helper only in the selected consumer and did not modify surrounding rendering structure.

Validation:

  • Passed:
    • node tests/phase22/crm-display-resolver.test.cjs
    • npx eslint components/search/dnssearchresults.js lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Broader suite run (informational baseline):
    • node tests/phase22/index.test.cjs
    • existing unrelated baseline drift remains in tests/phase22/case-service-behaviour.test.cjs, which still expects legacy route /api/endpoint/getportalmoduledetails_api while current repo behaviour uses grouped route /api/myportal/get-portal-module-details
    • no change made to that unrelated test in this slice

Follow-ups:

  • Recommended Slice 5: adopt the shared resolver in one further read-only consumer only, likely components/myportal/viewall.js, with particular care around its mixed source fields and per-view fallback differences.

CL-2026-06-29-CRM-DISPLAY-RESOLVER-SLICE3: adopt shared CRM display resolver in address search results

date: 2026-06-29 author: Cline scope: components/search/addresssearchresults.js, memory-bank/change-log.md type: change rationale: Continue CRM Display Infrastructure adoption with one additional behavior-preserving read-only search consumer using the existing shared resolver. impact: Behaviour-preserving maintainability slice in the CRM Compatibility Model / Presentation Infrastructure area. components/search/addresssearchresults.js now delegates repeated Welsh CRM display-value lookup logic to the shared resolver for authority, case type, and status labels while preserving local field-source and fallback differences. No CRM query, route, payload, auth/session, or rendering-structure change intended. status: completed

Summary:

  • Continued work on existing branch:
    • feature/crm-display-resolver-slice1
  • Previous commit on branch:
    • 77ae9a6d99d4df1b839aaebdbb1798ce07d1a0d0
  • Selected consumer for Slice 3:
    • components/search/addresssearchresults.js
  • Characterization findings for this consumer:
    • English behaviour: renders original CRM formatted/display values directly
    • Welsh behaviour: uses JSONPath against lookuptranslations.json and renders value_cy
    • repeated dominant pattern present for:
      • authority
      • case type
      • status
    • fallback behaviour:
      • where source field exists, English uses original display value or N/A
      • Welsh falls back to original display value / existing N/A path when translation missing
      • where source field is absent, rendered value remains empty string
    • empty/null handling remains safe through existing hasOwn(...) guards and unchanged fallbacks
  • Comparison against prior slices:
    • matches Slice 1 / Slice 2 dominant resolver pattern overall
    • unlike Slice 2, no .trim() behaviour was present on status lookup and none was introduced
    • unlike Slice 1 and Slice 2 authority lookup, this consumer can render authority from either:
      • _pinswg_localplanningauthority_value@OData.Community.Display.V1.FormattedValue
      • _pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue
    • case type in this consumer comes from item.pinswg_appealType rather than formatted pinswg_appealcasetype@...
  • Reused existing shared helper:
    • lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Adopted helper only in the selected consumer and did not modify surrounding rendering structure.

Validation:

  • Passed:
    • node tests/phase22/crm-display-resolver.test.cjs
    • npx eslint components/search/addresssearchresults.js lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Broader suite run (informational baseline):
    • node tests/phase22/index.test.cjs
    • existing unrelated baseline drift remains in tests/phase22/case-service-behaviour.test.cjs, which still expects legacy route /api/endpoint/getportalmoduledetails_api while current repo behaviour uses grouped route /api/myportal/get-portal-module-details
    • no change made to that unrelated test in this slice

Follow-ups:

  • Recommended Slice 4: adopt the shared resolver in one further read-only consumer only, likely components/search/dnssearchresults.js, while preserving any consumer-specific nuances such as derived labels or alternate display-field sources.

CL-2026-06-29-CRM-DISPLAY-RESOLVER-SLICE2: adopt shared CRM display resolver in search results

date: 2026-06-29 author: Cline scope: components/search/searchresults.js, memory-bank/change-log.md type: change rationale: Continue CRM Display Infrastructure adoption by reusing the existing shared resolver in one additional read-only search consumer with behavior-preserving scope only. impact: Behaviour-preserving maintainability slice in the CRM Compatibility Model / Presentation Infrastructure area. components/search/searchresults.js now delegates repeated Welsh CRM display-value lookup logic to the shared resolver for authority, case type, specialist process, and status labels. No CRM query, route, payload, auth/session, or rendering-structure change intended. status: completed

Summary:

  • Continued work on existing branch:
    • feature/crm-display-resolver-slice1
  • Previous commit on branch:
    • 3f651c25d4c31972b2c84b872e3ba5f98f34e3ea
  • Selected consumer for Slice 2:
    • components/search/searchresults.js
  • Characterization findings for this consumer:
    • English behaviour: renders original CRM formatted/display values directly
    • Welsh behaviour: uses JSONPath against lookuptranslations.json and renders value_cy
    • repeated dominant pattern present for:
      • authority
      • case type
      • specialist process
      • status
    • status lookup trims the source formatted value before Welsh lookup
    • fallback behaviour:
      • when source field exists, English uses original display value or N/A
      • Welsh falls back to the original display value / existing N/A path when translation missing
      • when source field is absent, rendered value remains empty string
  • Reused existing shared helper:
    • lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Adopted helper only in the selected consumer and did not modify surrounding rendering structure.

Validation:

  • Passed:
    • node tests/phase22/crm-display-resolver.test.cjs
    • npx eslint components/search/searchresults.js lib/i18n/crmDisplay/resolveCrmDisplayValue.js
  • Broader suite run (informational baseline):
    • node tests/phase22/index.test.cjs
    • existing unrelated baseline drift remains in tests/phase22/case-service-behaviour.test.cjs, which still expects legacy route /api/endpoint/getportalmoduledetails_api while current repo behaviour uses grouped route /api/myportal/get-portal-module-details
    • no change made to that unrelated test in this slice

Follow-ups:

  • Recommended Slice 3: adopt the shared resolver in one further read-only consumer only, likely components/search/addresssearchresults.js, while preserving any consumer-specific fallback nuances and avoiding bulk migration.

CL-2026-06-29-CRM-DISPLAY-RESOLVER-SLICE1: add shared CRM bilingual display resolver and adopt in admin appeals tab

date: 2026-06-29 author: Cline scope: lib/i18n/crmDisplay/resolveCrmDisplayValue.js, components/admin/tabs/appeals.js, tests/phase22/crm-display-resolver.test.cjs, memory-bank/change-log.md type: change rationale: Start CRM Display Infrastructure Slice 1 by extracting the dominant CRM formatted-value to Welsh lookup pattern into one small pure helper and adopting it in one low-risk read-only consumer without changing runtime behaviour. impact: Behaviour-preserving maintainability slice in the CRM Compatibility Model / Presentation Infrastructure area. components/admin/tabs/appeals.js now resolves authority, case type, and status labels through one shared helper while preserving English output, Welsh lookuptranslation usage, and per-field fallback behaviour. No CRM query, payload, auth/session, relay hash, route, or accessibility semantics change intended. status: completed

Summary:

  • Required context read before implementation:
    • context/business-domain-overview.md
    • context/architecture.md
    • context/maintainability-adoption-programme.md
    • context/implementation-governance.md
    • GUARDRAILS.md
    • memory-bank/change-log.md
  • Branch created from latest origin/SIPS-Development:
    • feature/crm-display-resolver-slice1
  • Characterized duplicated dominant CRM bilingual display-resolution pattern across the requested candidate consumers:
    • components/search/searchresults.js
    • components/search/addresssearchresults.js
    • components/search/dnssearchresults.js
    • components/myportal/viewall.js
    • components/admin/tabs/appeals.js
  • Chosen first adoption surface:
    • components/admin/tabs/appeals.js
    • reasons: read-only, smaller blast radius than public search/myportal consumers, clear repeated CRM formatted-value -> locale check -> lookuptranslations.json -> value_cy pattern, and easy EN/CY comparison for authority / case type / status columns.
  • Added shared pure helper:
    • lib/i18n/crmDisplay/resolveCrmDisplayValue.js
    • API: resolveCrmDisplayValue({ value, locale, translations, fallbackValue })
    • behaviour:
      • English returns fallback/original display value unchanged
      • Welsh resolves value_cy via encapsulated JSONPath lookup
      • missing/null/empty values preserve provided fallback behaviour
  • Adopted helper only in components/admin/tabs/appeals.js for:
    • authority label
    • case type label
    • status label
  • Left all other candidate consumers unchanged for later slices.
  • Added focused characterization coverage:
    • tests/phase22/crm-display-resolver.test.cjs
    • covers English return, Welsh translated return, Welsh missing-translation fallback, and null/empty/undefined safe handling.
  • Audit evidence note:
    • the standalone audit file was not found by title during repository search, but the duplicated dominant pattern was directly evidenced in the in-scope candidate consumers listed above and matches the maintainability programmes cited completed Bilingual CRM Display Resolution Audit.

Validation:

  • Focused validation intended for this slice:
    • npm run lint
    • node tests/phase22/crm-display-resolver.test.cjs
    • node tests/phase22/index.test.cjs
  • Manual validation still required/expected for the adopted consumer:
    • English admin appeals tab labels
    • Welsh admin appeals tab labels
    • lookup hit and lookup miss comparison
    • no row/list rendering regression

Follow-ups:

  • Next recommended slice: adopt the same helper in one additional read-only search-family consumer, likely components/search/searchresults.js or components/search/addresssearchresults.js, while preserving each consumers existing fallback behaviour.

CL-2026-06-29-BUSINESS-DOMAIN-OVERVIEW: clarify PEDW planning-domain terminology and portal business responsibilities

date: 2026-06-29 author: Cline scope: context/business-domain-overview.md, context/architecture.md, context/maintainability-adoption-programme.md, context/implementation-governance.md, memory-bank/change-log.md type: change rationale: Add a concise business-domain overview so future architectural and implementation work uses correct PEDW planning terminology and clearly distinguishes between digital appeal submission responsibilities and broader statutory public information/public participation responsibilities. impact: Documentation/context only; clarifies business-domain terminology, portal business capabilities, and the relationship between statutory process administration in CRM/PEDW and the portal's public-facing role. No runtime, API, auth/session, CRM, storage, queue, notification, routing, i18n, or behaviour change. status: completed

Summary:

  • Confirmed the required business-domain clarification context was read before drafting:
    • context/architecture.md
    • context/maintainability-adoption-programme.md
    • context/implementation-governance.md
    • GUARDRAILS.md
    • memory-bank/change-log.md
  • Created new business-domain clarification document:
    • context/business-domain-overview.md
  • Recorded the portal's two principal business capabilities as:
    • Digital Appeal Submission
    • Statutory Public Information & Public Participation
  • Clarified that:
    • digital appeal submission currently applies to supported appeal processes only (currently S78 MVP)
    • the portal also fulfils statutory public-facing information and participation responsibilities across multiple PEDW-administered case types
    • CRM / PEDW administers the statutory process
    • the portal provides the public-facing statutory digital service layer
  • Added a concise business glossary covering key planning-domain terms including:
    • Planning Appeal
    • Planning Application
    • Planning Case
    • Appellant
    • Applicant
    • Representation
    • Local Planning Authority (LPA)
    • DNS
    • SIP
    • CRM
    • PEDW
  • Updated context/architecture.md with a short business-capability context section without altering established architectural classifications.
  • Updated context/maintainability-adoption-programme.md so the programme purpose clearly reflects support for both statutory public information capabilities and current/future supported digital appeal submission capabilities.
  • Updated context/implementation-governance.md with a short clarification that implementation slices should recognise the distinction between digital appeal submission capabilities and statutory public information capabilities.
  • Left GUARDRAILS.md unchanged because no contradiction was identified and no guardrail meaning needed alteration.

Validation:

  • Documentation-only clarification slice.
  • No application code or tests changed.
  • Intended diff-format validation for this slice:
    • git diff --check -- context/business-domain-overview.md context/architecture.md context/maintainability-adoption-programme.md context/implementation-governance.md memory-bank/change-log.md

Follow-ups:

  • Use context/business-domain-overview.md as the business terminology baseline for future architecture and implementation prompts where planning-domain terminology may otherwise be ambiguous.
  • Keep future workstream and implementation language aligned with the distinction between appeal submission and broader statutory public information responsibilities.

CL-2026-06-29-IMPLEMENTATION-GOVERNANCE: create standard execution governance for future maintainability slices

date: 2026-06-29 author: Cline scope: context/implementation-governance.md, context/maintainability-adoption-programme.md, GUARDRAILS.md, memory-bank/change-log.md type: change rationale: Create a dedicated governance document defining how future PEDW implementation slices should be planned, branched, executed, validated, documented, and reported, and add minimal linked references so the current programme stage and execution model are clear without reopening discovery. impact: Documentation/context only; defines implementation-slice governance for future maintainability/adoption work, clarifies the relationship between guardrails, architecture phase, and maintainability roadmap, and adds linked references in existing docs. No runtime, API, auth/session, CRM, storage, queue, notification, routing, i18n, or behaviour change. status: completed

Summary:

  • Confirmed the required governance context was read before drafting:
    • GUARDRAILS.md
    • context/maintainability-adoption-programme.md
    • context/architecture.md
    • context/api-grouping-adoption-roadmap.md
    • context/remaining-architecture-candidates.md
    • memory-bank/change-log.md
    • memory-bank/open-questions.md
  • Created new governance document:
    • context/implementation-governance.md
  • Recorded the current programme stage as:
    • broad architecture discovery complete
    • business-policy discovery complete
    • API grouping rollout at a natural boundary for read-style façade slices
    • maintainability work now implementation-led
    • future work to proceed through small, behaviour-preserving adoption slices
  • Defined the standard implementation-slice model as:
    • one branch
    • one logical change set
    • one maintainability workstream
    • one bounded adoption target
    • behaviour-preserving unless explicitly approved otherwise
  • Recorded the required slice lifecycle:
    • characterize
    • validate
    • extract/add
    • adopt
  • Recorded branching governance, prompt governance, behaviour-preservation defaults, prohibited-by-default changes, scoped validation expectations, documentation expectations, and required final reporting format.
  • Updated context/maintainability-adoption-programme.md with a short section clarifying that the maintainability programme defines what to advance, while implementation governance defines how future slices should be executed.
  • Updated GUARDRAILS.md Related Docs with minimal references to:
    • context/implementation-governance.md
    • context/maintainability-adoption-programme.md
  • Did not update memory-bank/open-questions.md because no new unresolved governance question was created by this slice.
  • Did not create memory-bank/maintainability-tracker.md because it does not already exist and was not clearly established as current project convention for this slice.

Validation:

  • Documentation-only governance slice.
  • No application code or tests changed.
  • Intended diff-format validation for this slice:
    • git diff --check -- context/implementation-governance.md context/maintainability-adoption-programme.md GUARDRAILS.md memory-bank/change-log.md

Follow-ups:

  • Use context/implementation-governance.md as the default execution model for future maintainability/adoption implementation prompts.
  • Keep future slice prompts explicit about workstream, bounded scope, preservation rules, and validation expectations.

CL-2026-06-29-MAINTAINABILITY-ADOPTION-PROGRAMME: create architecture roadmap for incremental maintainability implementation workstreams

date: 2026-06-29 author: Cline scope: context/maintainability-adoption-programme.md, memory-bank/change-log.md type: change rationale: Consolidate the maintainability implementation workstreams that naturally emerged from the completed architecture discovery programme into one planning document so future work can proceed through bounded, behaviour-preserving adoption slices rather than reopening discovery. impact: Documentation/context only; records programme status, maintainability principles, workstream classification, relative priorities, and non-goals for future implementation planning. No runtime, routing, auth/session, CRM, storage, queue, notification, i18n, or behaviour change. status: completed

Summary:

  • Confirmed the required context was read before drafting the programme document:
    • context/architecture.md
    • context/portal-api-platform-assessment.md
    • context/api-grouping-adoption-roadmap.md
    • context/remaining-architecture-candidates.md
    • memory-bank/change-log.md
  • Used the completed Bilingual CRM Display Resolution Audit and Bilingual Navigation & Link Resolution Audit as evidence inputs rather than reopening those discovery slices.
  • Created new planning document:
    • context/maintainability-adoption-programme.md
  • Recorded the programme position that:
    • architecture discovery is complete
    • business-policy discovery is complete
    • remaining work is implementation-led
    • future improvements should be incremental and behaviour-preserving
  • Recorded maintainability principles:
    • characterize
    • validate
    • extract/add
    • adopt
    • and explicit prohibitions against rewrite/replace/redesign/modernize without approval
  • Classified the main maintainability workstreams:
    • domain layer adoption
    • CRM display infrastructure
    • navigation infrastructure
    • API journey grouping
  • Recorded for each workstream:
    • objective
    • architectural classification
    • current maturity
    • implementation strategy
    • expected maintenance benefit
  • Recommended relative implementation priority order:
    1. domain layer adoption
    2. CRM display infrastructure
    3. navigation infrastructure
    4. API journey grouping
  • Recorded programme-wide guiding principles, explicit non-goals, and the expected adoption model for future bounded slices.

Validation:

  • Documentation-only planning slice.
  • Checked consistency against the current architecture phase (Adoption Planning), established policy boundaries, API grouping boundary guidance, and completed characterization outputs.
  • No code, route, contract, or runtime changes performed.
  • Intended lightweight validation for this slice:
    • git diff --check

Follow-ups:

  • Use this document as the top-level roadmap when proposing future maintainability implementation slices.
  • Keep future workstream proposals bounded to one characterized seam and one adoption surface where possible.

CL-2026-06-25-WORKFLOW-ORCHESTRATION-DOC-VALIDATION: validate whether existing architecture docs already cover PEDW workflow/orchestration

date: 2026-06-25 author: Cline scope: context/workflow-orchestration-documentation-validation.md, memory-bank/change-log.md type: change rationale: Validate, using existing architecture documentation first, whether PEDW's workflow/orchestration model is already sufficiently documented for appeals and representations without reopening broad workflow discovery or performing route-by-route API review. impact: Documentation/context only; confirms current workflow/orchestration coverage across draft lifecycle, submission/finalisation, storage ownership, queue handoff, CRM transition, PDF/document generation, Notify/email side effects, completion-state handling, integration sequencing, and ownership transitions. No runtime, API, auth/session, CRM, storage, queue, notification, i18n, or behaviour change. status: completed

Summary:

  • Confirmed the required context was read before validation:
    • context/architecture.md
    • context/journey-architecture-map.md
    • context/api-route-map.md
    • context/api-grouping-workflow-boundary-assessment.md
    • context/api-grouping-adoption-roadmap.md
    • context/portal-api-platform-assessment.md
    • context/integration-map.md
    • memory-bank/change-log.md
    • memory-bank/open-questions.md
  • Created new validation document:
    • context/workflow-orchestration-documentation-validation.md
  • Reused existing documentation as the evidence base and did not perform a new workflow discovery stream.
  • Validated the following workflow areas:
    • draft appeal lifecycle
    • appeal submission / finalisation
    • draft representation lifecycle
    • representation submission / finalisation
    • Azure Storage ownership
    • queue handoff
    • CRM transition boundary
    • PDF/document generation where relevant
    • Notify/email side effects where relevant
    • completion-state handling
    • integration sequencing
    • ownership transitions
  • Main conclusion:
    • existing PEDW architecture documents are already sufficient to describe the workflow/orchestration model at architecture level
    • strongest evidence is already present in context/journey-architecture-map.md, supported by context/api-route-map.md, context/api-grouping-workflow-boundary-assessment.md, context/architecture.md, and context/integration-map.md
    • the only notable gap is limited downstream detail beyond the visible queue handoff / CRM transition boundary, which does not justify another broad workflow discovery stream
  • Final recommendation recorded in the validation document:
    • small documentation consolidation only
    • no new workflow discovery needed at this time

Validation:

  • Documentation-only validation.
  • Existing documentation used as primary evidence.
  • No route inventory, broad code discovery, implementation, migration, or refactor performed.
  • Intended lightweight validation for this slice:
    • git diff --check

Follow-ups:

  • Prefer small documentation consolidation/cross-linking only if follow-up work is requested.
  • Do not open a new broad workflow/orchestration discovery stream unless a specific contradiction or missing boundary emerges.

CL-2026-06-25-API-GROUPING-FACADE-SEARCH-VERTICAL-SLICE: active public search results grouping and service adoption

CL-2026-06-25-API-GROUPING-WORKFLOW-BOUNDARY-ASSESSMENT: classify where the additive façade pattern should stop

date: 2026-06-25 author: Cline scope: context/api-grouping-workflow-boundary-assessment.md, context/api-grouping-adoption-roadmap.md, memory-bank/change-log.md type: change rationale: After completing the main additive façade read slices, perform a bounded architecture assessment to determine which remaining API families are still suitable for the current façade pattern, which require extra caution, which are workflow/orchestration boundaries, and which should be left unchanged for now. impact: Documentation/context only; clarifies the natural stop point of the current façade rollout pattern and guides future planning without any runtime, routing, auth/session, CRM, storage, queue, notification, or behaviour change. status: completed

Summary:

  • Confirmed the required context was read before the assessment:
    • context/api-grouping-plan.md
    • context/api-grouping-adoption-roadmap.md
    • context/api-route-map.md
    • context/journey-architecture-map.md
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • memory-bank/change-log.md
  • Created new assessment document:
    • context/api-grouping-workflow-boundary-assessment.md
  • Used representative family inspection only, not a full inventory.
  • Reviewed representative service/routes across the remaining major families, including:
    • account / registration / personal details
    • notifications / email
    • appeals / drafts / finalisation
    • representations / drafts / finalisation
    • storage / blob / file operations
    • auth / session
    • admin / reporting
  • Recorded the A/B/C/D classification model:
    • A — safe façade candidate
    • B — possible façade candidate with caution
    • C — workflow / orchestration boundary
    • D — leave unchanged for now
  • Main findings:
    • the completed façade slices prove the current pattern is strongest for bounded read-only journey families with clear service ownership and trivial delegation
    • the strongest remaining cautious candidate is account read-support only, because the family has a clear service layer but is tightly coupled to identity/bootstrap and includes mutations
    • notifications/email, appeals/drafts/finalisation, and representations/drafts/finalisation all cross workflow/orchestration boundaries and should not use the same façade pattern without a different design
    • storage/blob/file operations, auth/session, and admin/reporting should currently be left unchanged because they are either integration-owned, platform-critical, already coherent enough, or low-value for another façade layer
  • Updated the adoption roadmap with a workflow-boundary checkpoint stating that the current additive façade pattern now has a natural stop point and should not simply continue into orchestration-heavy families.

Validation:

  • Documentation-only validation.
  • Representative inspection only; no full route inventory, no implementation work, and no service/route changes performed.
  • Intended lightweight validation for this slice:
    • git diff --check

Follow-ups:

  • Primary next planning recommendation:
    • workflow/orchestration design assessment
  • Secondary fallback only if another implementation slice is explicitly desired:
    • one narrow account read-support cautious façade slice

CL-2026-06-25-API-GROUPING-FACADE-MYPORTAL-READS-VERTICAL-SLICE: bounded myportal dashboard CRM read/enrichment grouping and service adoption

date: 2026-06-25 author: Cline scope: pages/api/myportal/get-my-cases.js, pages/api/myportal/get-my-lpa-cases.js, pages/api/myportal/get-portal-module-details.js, actions/services/portalDirectService.js, actions/services/caseDirectService.js, tests/phase22/api-grouping-facade-myportal-reads.test.cjs, context/api-grouping-adoption-roadmap.md type: change rationale: Implement the next additive API grouping façade vertical slice for the bounded My Portal dashboard read / enrichment journey by grouping only the proven active CRM read/enrichment routes under pages/api/myportal/ and adopting those grouped routes in the existing owning service layers without changing dashboard storage, auth, watched-case, submission, or account behaviour. impact: Runtime behaviour is intended to remain unchanged; grouped myportal façade routes now cover the proven active dashboard CRM read/enrichment family, legacy endpoint handlers remain canonical, watched-case behaviour remains owned by subscriptions, and no route migration, deletion, contract change, CRM query change, auth/session change, storage/draft change, or submission/orchestration change has occurred. status: completed

Summary:

  • Confirmed the required context was read before implementation:
    • context/api-grouping-plan.md
    • context/api-grouping-adoption-roadmap.md
    • context/api-route-map.md
    • context/journey-architecture-map.md
    • context/architecture.md
    • memory-bank/change-log.md
  • Performed a bounded audit of the active My Portal dashboard read / enrichment journey across the dashboard loader, service layers, and endpoint usage.
  • Audit findings established that:
    • the active entry page is pages/myportal/index.js
    • the active owning service layers for the proven CRM read/enrichment path are:
      • actions/services/portalDirectService.js for getMyCases / getMyLPACases
      • actions/services/caseDirectService.js for getPortalModuleDetails
    • the active CRM-backed dashboard path is:
      • getMyCases(loggedInUser) or getMyLPACases(lpaId)
      • detail-card enrichment fan-out through getPortalModuleDetails(collectionName, caseID)
    • watched cases are already handled by the earlier subscriptions façade slice and were left alone
    • representation and awaiting-submission data on the active dashboard loader are blob-backed in the current path via:
      • getRepsFromBlob(thisSession.user.id)
      • getAwaitingSubmissionFromBlob(thisSession.user.id)
    • getmyrepresentations_api.js and getawaitingsubmission_api.js were investigated but were not proven active in the bounded dashboard loader path for this slice
    • account/auth/bootstrap routes (getPortalLogin, getPersonalAccount, getSession) remain upstream support concerns and out of scope
  • Added grouped façade routes under pages/api/myportal/ for the proven active CRM dashboard family only:
    • get-my-cases.js
    • get-my-lpa-cases.js
    • get-portal-module-details.js
  • Implemented each façade as the smallest safe compatibility wrapper:
    • import the existing legacy endpoint handler
    • delegate req and res directly to that existing handler
  • Updated only the existing owning service call sites needed for the bounded slice:
    • actions/services/portalDirectService.js
      • getMyCases
      • getMyLPACases
    • actions/services/caseDirectService.js
      • getPortalModuleDetails
  • Left the following unchanged:
    • legacy endpoint handlers
    • watched-case grouped subscriptions routes
    • representation routes
    • awaiting-submission routes
    • storage/blob routes
    • account/profile routes
    • auth/session behaviour
    • CRM queries and transforms
    • query parameter names and response contracts
  • Added focused characterization coverage to prove:
    • grouped myportal façade routes delegate correctly
    • legacy endpoint handlers remain present
    • adopted service methods now target grouped myportal routes
    • adopted methods no longer target the legacy endpoint URLs directly
    • request-contract cues remain unchanged
    • excluded route families remain outside this slice
  • Updated the adoption roadmap to record the audited scope boundary, included routes, excluded routes, service adoption status, and behaviour-preservation notes for the My Portal dashboard read / enrichment slice.

Validation:

  • Focused validation only intended for this slice:
    • npx eslint actions/services/portalDirectService.js actions/services/caseDirectService.js pages/api/myportal/*.js tests/phase22/api-grouping-facade-myportal-reads.test.cjs
    • node tests/phase22/api-grouping-facade-myportal-reads.test.cjs
  • No repository-wide validation run.

Follow-ups:

  • The bounded My Portal dashboard read / enrichment family is now a complete façade grouping slice only for the proven active CRM dashboard read/enrichment routes adopted here.
  • Adjacent subscriptions, storage/draft, representation, awaiting-submission, and account/auth support routes remain intentionally outside this slice and should only be considered in separate bounded audits if needed.

CL-2026-06-25-API-GROUPING-FACADE-CASE-DETAILS-VERTICAL-SLICE: bounded case-details read grouping and service adoption

date: 2026-06-25 author: Cline scope: pages/api/cases/get-incident-by-id.js, pages/api/cases/get-case-message.js, pages/api/cases/get-linked-cases.js, pages/api/cases/get-sips-events.js, pages/api/cases/get-sips-media.js, actions/services/caseDirectService.js, actions/services/searchDirectService.js, tests/phase22/api-grouping-facade-case-details.test.cjs, context/api-grouping-adoption-roadmap.md type: change rationale: Implement the next additive API grouping façade vertical slice for the bounded Case Details read journey by grouping only the proven case-specific read family under pages/api/cases/ and adopting those grouped routes in the existing owning service layers without changing bootstrap/search/documents behavior. impact: Runtime behaviour is intended to remain unchanged; grouped cases façade routes now cover the proven incident-id, case-message, linked-cases, and SIPS enrichment reads, legacy endpoint handlers remain canonical, and no route migration, deletion, contract change, document behaviour change, search behaviour change, auth change, storage change, or submission/orchestration change has occurred. status: completed

Summary:

  • Confirmed the required context was read before implementation:
    • context/api-grouping-plan.md
    • context/api-grouping-adoption-roadmap.md
    • context/api-route-map.md
    • context/journey-architecture-map.md
    • context/architecture.md
    • memory-bank/change-log.md
  • Performed a bounded audit of the active Case Details read journey across pages, components, services, and endpoint usage.
  • Audit findings established that:
    • pages/case/[ticketnumber].js and pages/myportal/case/[ticketnumber].js still bootstrap primarily through the Public Search family using getBasicSearch(...) and getSearchDetails(...)
    • pages/case/id/[incident].js and pages/myportal/case/id/[incident].js actively use the case-specific incident-id read route through getIncidentbyID(...)
    • getCaseMessage(...) is actively used in case-page loading for banner/message content
    • getLinkedCases(...) is actively used inside components/case/summary.js as a case-details read concern
    • getSIPSEvents(...) and getSIPSMedia(...) are active case-page enrichments for relevant SIPS cases
    • components/case/documents.js and document metadata routes remain part of the documents slice, not this case-details slice
    • getcase_api.js, getcasebyid_api.js, and getportalmoduledetails_api.js are adjacent/support reads but were not proven to belong to the bounded active case-page read path for this slice
  • Added grouped façade routes under pages/api/cases/ for the proven case-specific read family only:
    • get-incident-by-id.js
    • get-case-message.js
    • get-linked-cases.js
    • get-sips-events.js
    • get-sips-media.js
  • Implemented each façade as the smallest safe compatibility wrapper:
    • import the existing legacy endpoint handler
    • delegate req and res directly to that existing handler
  • Updated only the existing owning service call sites needed for the bounded slice:
    • actions/services/caseDirectService.js
      • getIncidentbyID
      • getCaseMessage
      • getSIPSEvents
      • getSIPSMedia
    • actions/services/searchDirectService.js
      • getLinkedCases
  • Left the following unchanged:
    • legacy endpoint handlers
    • search bootstrap routes and search detail expansion routes
    • document metadata/download routes
    • CRM queries
    • transforms
    • query parameter names
    • response contracts
    • status codes
    • auth/session behavior
    • storage/queue/submission behavior
  • Added focused characterization coverage to prove:
    • grouped cases façade routes delegate correctly
    • legacy endpoint handlers remain present
    • adopted service methods now target grouped cases routes
    • request-contract cues remain unchanged
    • adjacent search/documents/support routes remain outside grouped adoption scope
  • Updated the adoption roadmap to record the audited scope boundary, included routes, excluded routes, service adoption status, and behaviour-preservation notes for the Case Details read slice.

Validation:

  • Focused validation only intended for this slice:
    • npx eslint pages/api/cases/*.js actions/services/caseDirectService.js actions/services/searchDirectService.js tests/phase22/api-grouping-facade-case-details.test.cjs context/api-grouping-adoption-roadmap.md memory-bank/change-log.md
    • node tests/phase22/api-grouping-facade-case-details.test.cjs
  • No repository-wide validation run.

Follow-ups:

  • The bounded Case Details read family is now a complete façade grouping slice only for the proven case-specific read routes adopted here.
  • Adjacent case bootstrap, document, and myportal aggregation/detail-enrichment routes should remain separate future slices if needed.

CL-2026-06-25-API-GROUPING-FACADE-SEARCH-VERTICAL-SLICE: active public search results grouping and service adoption

CL-2026-06-25-API-GROUPING-FACADE-ROLLOUT-CHECKPOINT: proven rollout pattern and guardrails checkpoint

date: 2026-06-25 author: Cline scope: context/api-grouping-adoption-roadmap.md, tests/phase22/api-grouping-facade-rollout.test.cjs, memory-bank/change-log.md type: milestone rationale: Record a short checkpoint after the first successful additive API grouping façade slices so the proven rollout pattern, completion criteria, and delay/guardrail criteria are documented before considering more complex API groups. impact: Documentation and characterization only; no runtime behaviour, handler implementation, service URL, route location, payload, contract, auth/session behaviour, or integration behaviour change. status: completed

Summary:

  • Confirmed the required checkpoint context was read before implementation:
    • context/api-grouping-plan.md
    • context/api-grouping-adoption-roadmap.md
    • context/api-route-map.md
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • memory-bank/change-log.md
    • existing façade tests under tests/phase22
  • Confirmed the proven slice pattern now established by the completed façade areas:
    • pages/api/subscriptions/*
    • pages/api/documents/*
    • pages/api/search/*
  • Recorded the rollout checkpoint in the adoption roadmap with the proven shape:
    • UI journey
    • existing service layer
    • grouped façade route
    • legacy canonical handler
    • unchanged integration behaviour
  • Documented completion criteria for a vertical façade slice, including:
    • bounded active-journey audit
    • grouped façade coverage for in-scope active routes only
    • safe service-layer adoption
    • retained canonical legacy handlers
    • explicit out-of-scope treatment for inactive/support routes
    • characterization proof and scope documentation
  • Documented “do not group yet” criteria for cases such as:
    • unclear ownership
    • missing clear service layer
    • orchestration-heavy routes
    • storage + queue + CRM crossing routes
    • auth/session-critical routes
    • unproven activity
    • required frontend refactor
    • required contract change
  • Added a lightweight meta-test to characterize the checkpoint structure without introducing a larger framework.

Validation:

  • Focused validation only for this checkpoint:
    • npx eslint tests/phase22/api-grouping-facade-rollout.test.cjs
    • node tests/phase22/api-grouping-facade-rollout.test.cjs
    • node tests/phase22/api-grouping-facade-subscriptions.test.cjs
    • node tests/phase22/api-grouping-facade-documents.test.cjs
    • node tests/phase22/api-grouping-facade-search.test.cjs
  • No heavy repo-wide tooling run.

Follow-ups:

  • Use this checkpoint as the baseline before considering any further façade candidates.
  • Prefer the next candidate only where the same additive pattern can be proven without route movement, contract change, or orchestration-risk expansion.

CL-2026-06-25-API-GROUPING-FACADE-SEARCH-VERTICAL-SLICE: active public search results grouping and service adoption

date: 2026-06-25 author: Cline scope: pages/api/search/basic-paged.js, pages/api/search/advanced.js, pages/api/search/advanced-paged.js, pages/api/search/basic-details-paged.js, actions/services/searchDirectService.js, tests/phase22/api-grouping-facade-search.test.cjs, context/api-grouping-adoption-roadmap.md type: change rationale: Implement the next additive API grouping façade vertical slice by grouping the active Public Search results journey under pages/api/search/ and adopting the grouped routes only in the existing common public-search service layer, while preserving legacy endpoint handlers unchanged and leaving adjacent/excluded search journeys alone. impact: Runtime behaviour is intended to remain unchanged; grouped search façade routes now cover the proven active public-results path, actions/services/searchDirectService.js now targets those grouped routes for the bounded in-scope calls, legacy endpoint handlers remain canonical, and no migration, route deletion, query-parameter change, paging change, sorting change, payload change, or response-contract change has occurred. status: completed

Summary:

  • Confirmed the required context was read before implementation:
    • context/api-grouping-plan.md
    • context/api-grouping-adoption-roadmap.md
    • context/api-route-map.md
    • context/journey-architecture-map.md
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • memory-bank/change-log.md
  • Performed a bounded audit of the active Public Search journey across pages, components, services, and endpoint usage.
  • Audit findings established that:
    • the active public results UI is driven through pages/searchresults.js and components/search/searchresults.js
    • the advanced results page pages/advancedsearchresults.js is part of the active public-search scope
    • the common owning service layer is actions/services/searchDirectService.js
    • components/search/searchresults.js actively uses:
      • getBasicSearchPaged
      • getAdvancedSearchPaged
      • getSearchDetailsPaged(...), which in turn uses getBasicSearchDetailsPaged
    • pages/advancedsearchresults.js actively uses getAdvancedSearch(...) for bootstrap
    • getbasicsearch_api.js and getbasicsearchdetails_api.js remain active in adjacent case-detail/bootstrap paths and therefore remained out of scope for this bounded public-results slice
    • address-search routes, DNS routes, and myportal search pages were intentionally excluded as separate journeys
  • Added grouped façade routes under pages/api/search/ for the proven active public-results path only:
    • basic-paged.js
    • advanced.js
    • advanced-paged.js
    • basic-details-paged.js
  • Implemented each façade as the smallest safe compatibility wrapper:
    • import the existing legacy endpoint handler
    • delegate req and res directly to that existing handler
  • Updated only the suitable common service layer in actions/services/searchDirectService.js to target grouped façade routes for:
    • getBasicSearchPaged
    • getAdvancedSearch
    • getAdvancedSearchPaged
    • getBasicSearchDetailsPaged
  • Left the following unchanged:
    • existing endpoint handlers
    • query parameter names
    • CRM queries
    • paging behaviour
    • sorting behaviour
    • search semantics
    • case-detail behaviour
    • document behaviour
    • excluded search journeys
  • Added focused characterization coverage to prove:
    • grouped search façade routes delegate correctly
    • active service methods now target grouped search routes
    • legacy endpoint handlers remain present
    • adopted service methods no longer target the legacy endpoint URLs directly
    • request-contract strings/parameter names remain unchanged
    • excluded routes remain outside grouped adoption scope
  • Updated the adoption roadmap to record the active Public Search grouping slice and its exact scope boundary.

Validation:

  • Focused validation only intended for this slice:
    • npx eslint actions/services/searchDirectService.js pages/api/search/*.js tests/phase22/api-grouping-facade-search.test.cjs
    • node tests/phase22/api-grouping-facade-search.test.cjs
  • No repository-wide validation run.

Follow-ups:

  • The active Public Search journey is now a complete vertical grouping slice for the bounded public-results scope:
    • grouped façade routes
    • active service-layer adoption
    • legacy canonical handlers retained
    • unchanged behaviour
  • Adjacent or excluded search journeys should remain separate future slices if needed:
    • case-detail bootstrap search
    • address search
    • DNS search
    • myportal search

CL-2026-06-25-API-GROUPING-FACADE-DOCUMENTS-VERTICAL-SLICE: active published-document metadata grouping and service adoption

date: 2026-06-25 author: Cline scope: pages/api/documents/get-search-document-details.js, pages/api/documents/get-search-document-details-paged.js, pages/api/documents/get-search-document-types.js, actions/services/searchDirectService.js, tests/phase22/api-grouping-facade-documents.test.cjs, context/api-grouping-adoption-roadmap.md type: change rationale: Implement the second additive API grouping façade vertical slice by grouping the active published-document metadata journey under pages/api/documents/ and adopting the grouped routes only in the existing common document metadata service layer, while preserving the existing download route and legacy endpoint handlers unchanged. impact: Runtime behaviour is intended to remain unchanged; grouped document façade routes now cover the active metadata/type journey calls, actions/services/searchDirectService.js now targets those grouped routes, legacy endpoint handlers remain canonical, the existing grouped download route remains unchanged, and no migration, route deletion, auth change, CRM query change, hash-link generation change, payload change, or response-contract change has occurred. status: completed

Summary:

  • Confirmed the required context was read before implementation:
    • context/api-grouping-plan.md
    • context/api-grouping-adoption-roadmap.md
    • context/journey-architecture-map.md
    • context/api-route-map.md
    • context/architecture.md
    • memory-bank/change-log.md
  • Performed a bounded audit of the active published-documents journey across services, API handlers, and frontend components.
  • Audit findings established that:
    • active document metadata retrieval is surfaced through components/case/documents.js
    • active document metadata/type calls already route through the common service layer actions/services/searchDirectService.js
    • published-document downloads already use the grouped runtime route pages/api/documents/download/[id].js
    • legacy metadata handlers generate pinswg_hashlink values that continue to point at the unchanged grouped download route
    • getsearchdocumenthistory_api.js and getsearchdocumenthistorypaged_api.js remain present but were not surfaced in the currently active UI journey and therefore remained outside grouped adoption scope
  • Added grouped façade routes under pages/api/documents/ for the active metadata journey only:
    • get-search-document-details.js
    • get-search-document-details-paged.js
    • get-search-document-types.js
  • Implemented each façade as the smallest safe compatibility wrapper:
    • import the existing legacy endpoint handler
    • delegate req and res directly to that existing handler
  • Updated only the suitable common service layer in actions/services/searchDirectService.js to target grouped façade routes for:
    • getSearchDocumentDetails
    • getSearchDocumentDetailsPaged
    • getSearchDocumentTypes
  • Left the following unchanged:
    • CRM queries
    • download behaviour
    • hash-link generation
    • authentication
    • response contracts
    • payloads
    • file locations of legacy handlers
  • Added focused characterization coverage to prove:
    • grouped document façade routes delegate correctly
    • active document service methods now target grouped document routes
    • legacy document endpoint handlers remain present
    • active service-layer query-contract cues remain unchanged
    • the runtime download path remains /api/documents/download/[id]
  • Updated the adoption roadmap to record the active documents grouping slice and its bounded scope.

Validation:

  • Focused validation only intended for this slice:
    • npx eslint actions/services/searchDirectService.js pages/api/documents/get-search-document-details.js pages/api/documents/get-search-document-details-paged.js pages/api/documents/get-search-document-types.js tests/phase22/api-grouping-facade-documents.test.cjs context/api-grouping-adoption-roadmap.md memory-bank/change-log.md
    • node tests/phase22/api-grouping-facade-documents.test.cjs
  • No repository-wide validation run.

Follow-ups:

  • The active Documents journey is now a complete vertical grouping slice for the bounded published-document metadata/type path:
    • grouped façade routes
    • active service-layer adoption
    • legacy canonical handlers retained
    • unchanged grouped download path
    • unchanged behaviour
  • Any future expansion beyond this bounded scope should be a separate slice, for example if history/support routes are later proven to be part of an actively adopted user journey.

CL-2026-06-25-API-GROUPING-FACADE-SUBSCRIPTIONS-VERTICAL-SLICE-COMPLETION: watched-case proxy read adoption and scope closure

date: 2026-06-25 author: Cline scope: pages/api/subscriptions/get-watched-cases-proxy.js, actions/services/portalDirectService.js, tests/phase22/api-grouping-facade-subscriptions.test.cjs, context/api-grouping-adoption-roadmap.md type: change rationale: Complete the intended subscriptions/watched-cases vertical slice by covering the active proxy read path still used by the normal watched-case UI journey, while keeping legacy endpoint and proxy handlers in place as canonical/support implementation routes. impact: Runtime behaviour is intended to remain unchanged; the grouped subscriptions façade now covers the active watched-case journey service methods including proxy reads, while legacy handlers remain canonical and no route deletion, migration, auth/session change, CRM query change, payload change, or status-code change has occurred. status: completed

Summary:

  • Performed a bounded watched-case usage audit across services, pages, and components.
  • Audit findings established that:
    • no frontend components directly call legacy watched-case endpoint URLs
    • normal watched-case journey calls are routed through actions/services/portalDirectService.js
    • getWatchedCasesProxy() is actively used in the current watched-case UI journey
    • deletewatchedcasesproxy_api remains present but is not part of the currently adopted normal watched-case service path
  • Added grouped façade route:
    • pages/api/subscriptions/get-watched-cases-proxy.js
    • delegates directly to pages/api/endpoint/getwatchedcasesproxy_api.js
  • Updated actions/services/portalDirectService.js so getWatchedCasesProxy now targets:
    • /api/subscriptions/get-watched-cases-proxy
  • Extended the existing subscriptions façade characterization test to prove:
    • grouped proxy façade delegation works
    • legacy proxy handler remains present
    • watched-case service methods now target grouped subscriptions routes for both normal and proxy watched-case reads
    • adopted methods no longer target the legacy watched-case endpoint URLs directly
    • delete proxy remains outside grouped pilot scope and legacy support route remains present
  • Updated the adoption roadmap to define what “complete vertical slice” means for this pilot and to record the proxy-scope boundary.

Validation:

  • Focused validation only:
    • npx eslint actions/services/portalDirectService.js tests/phase22/api-grouping-facade-subscriptions.test.cjs pages/api/subscriptions/get-watched-cases.js pages/api/subscriptions/get-watched-cases-proxy.js pages/api/subscriptions/create-watched-case.js pages/api/subscriptions/delete-watched-case.js
    • node tests/phase22/api-grouping-facade-subscriptions.test.cjs
  • No repository-wide test suite run.

Follow-ups:

  • For the intended watched-case/subscriptions scope, the pilot now forms a complete vertical slice:
    • subscriptions journey
    • service layer
    • grouped façade routes
    • legacy canonical endpoint handlers
    • unchanged behaviour
  • Any future expansion beyond this scope should be a separate bounded slice (for example, deciding whether legacy/support proxy delete routes also need grouped façade coverage).

CL-2026-06-25-API-GROUPING-FACADE-SUBSCRIPTIONS-INTERNAL-ADOPTION: first internal consumer targets grouped watched-case routes

date: 2026-06-25 author: Cline scope: actions/services/portalDirectService.js, tests/phase22/api-grouping-facade-subscriptions.test.cjs, context/api-grouping-adoption-roadmap.md type: change rationale: Validate the subscriptions façade pilot through one carefully selected internal consumer by updating watched-case service methods in portalDirectService to call the new grouped subscriptions routes while leaving the legacy endpoint handlers unchanged and canonical behind the façade. impact: Runtime behaviour is intended to remain unchanged because the grouped routes still delegate to the existing watched-case handlers; validates incremental internal adoption without route migration, deprecation, auth/session change, CRM query change, payload change, or signed-request behaviour change. status: completed

Summary:

  • Re-read required continuation context before implementation:
    • context/api-grouping-plan.md
    • context/api-grouping-adoption-roadmap.md
    • context/api-route-map.md
    • memory-bank/change-log.md
  • Updated only the watched-case methods in actions/services/portalDirectService.js to target grouped façade routes:
    • getWatchedCases -> /api/subscriptions/get-watched-cases
    • createWatchedCases -> /api/subscriptions/create-watched-case
    • deleteWatchedCases -> /api/subscriptions/delete-watched-case
  • Did not modify any other service methods in portalDirectService.js.
  • Did not modify underlying watched-case endpoint handlers or façade handler delegation.
  • Extended the existing façade characterization test to prove:
    • grouped façade delegation still works
    • legacy endpoint handler files remain present
    • the internal service layer now targets /api/subscriptions/...
    • the adopted watched-case methods no longer point at legacy /api/endpoint/... routes
    • key service-layer request-contract cues remain present (loggedInUserId, watchedCaseID, POST method)
  • Updated the adoption roadmap to record:
    • first internal consumer adoption has occurred
    • legacy routes remain canonical implementations
    • no migration or deprecation has occurred
    • this validates incremental adoption of grouped journey-owned routes

Validation:

  • Focused validation only:
    • npx eslint actions/services/portalDirectService.js tests/phase22/api-grouping-facade-subscriptions.test.cjs context/api-grouping-adoption-roadmap.md memory-bank/change-log.md
    • node tests/phase22/api-grouping-facade-subscriptions.test.cjs
  • No repository-wide test suite run.

Follow-ups:

  • The subscriptions façade pattern has now been validated through both additive route introduction and one successful internal service adoption.
  • Any further adoption should continue incrementally and only with the same behaviour-preserving characterization discipline.

CL-2026-06-25-API-GROUPING-FACADE-PILOT-SUBSCRIPTIONS: additive watched-case subscriptions façade routes

date: 2026-06-25 author: Cline scope: pages/api/subscriptions/*, watched-case API façade pilot, focused characterization tests, and grouping-plan/adoption-roadmap documentation type: change rationale: Implement the first additive API grouping façade pilot by adding journey-owned subscriptions wrapper routes for watched-case read/create/delete operations that delegate to the existing canonical endpoint handlers without changing current frontend call sites, contracts, or behaviour. impact: Runtime impact is intentionally additive only; existing watched-case endpoint routes remain canonical/stable; no auth/session, CRM query, hash/signing, payload, status-code, i18n, or frontend-callsite behaviour change intended; documentation now records the pilot and its non-migration status. status: completed

Summary:

  • Confirmed required implementation context was read before changes:
    • context/api-grouping-plan.md
    • context/api-grouping-adoption-roadmap.md
    • context/api-route-map.md
    • context/journey-architecture-map.md
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • memory-bank/change-log.md
  • Added the first grouped façade routes under pages/api/subscriptions/:
    • get-watched-cases.js
    • create-watched-case.js
    • delete-watched-case.js
  • Implemented each façade as the smallest safe compatibility wrapper:
    • import the existing watched-case endpoint handler
    • delegate req and res directly to that existing handler
  • Preserved the existing canonical watched-case routes unchanged:
    • pages/api/endpoint/getwatchedcases_api.js
    • pages/api/endpoint/createwatchedcases_api.js
    • pages/api/endpoint/deletewatchedcases_api.js
  • Did not change frontend call sites; actions/services/portalDirectService.js continues to call the legacy endpoint routes.
  • Added a focused characterization test file to prove:
    • grouped façade route files exist and delegate to the expected legacy handlers
    • legacy endpoint files still exist
    • frontend watched-case service call sites were not migrated to /api/subscriptions/
  • Updated the API grouping plan and adoption roadmap to record:
    • this is the first additive grouping façade pilot
    • existing watched-case endpoint routes remain canonical/stable
    • new subscriptions routes are grouped façade routes
    • no old route removal or migration has occurred

Validation:

  • Focused implementation only; no broad refactor performed.
  • Characterization coverage added for façade delegation and unchanged frontend call sites.
  • Intended validation set for this slice:
    • npx eslint pages/api/subscriptions/*.js tests/phase22/api-grouping-facade-subscriptions.test.cjs
    • node tests/phase22/api-grouping-facade-subscriptions.test.cjs
  • Existing watched-case endpoint files were preserved in place.

Follow-ups:

  • If the pilot is accepted, use the same additive façade pattern for future journey-owned grouping candidates only where behaviour preservation can be equally well characterized.

CL-2026-06-25-API-GROUPING-ADOPTION-ROADMAP: gradual adoption roadmap for future journey-owned API grouping

date: 2026-06-25 author: Cline scope: context/api-grouping-adoption-roadmap.md, architecture roadmap for gradual adoption of future API grouping concepts type: milestone rationale: Create a planning-only roadmap document describing how PEDW could gradually adopt the future journey-owned API grouping model over time through documentation guidance, additive consistency, and future-development discipline rather than migration or restructuring. impact: Documentation/context only; improves architecture planning clarity for gradual API grouping adoption, maintainability direction, and future decision support; no runtime, auth, CRM, storage, queue, notification, i18n, contract, or behaviour change. status: completed

Summary:

  • Confirmed the required roadmap context was read before drafting:
    • context/api-grouping-plan.md
    • context/api-route-map.md
    • context/journey-architecture-map.md
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • context/integration-map.md
    • memory-bank/change-log.md
  • Added new document: context/api-grouping-adoption-roadmap.md.
  • Kept the roadmap explicitly scoped to architecture planning only.
  • Recorded the core adoption philosophy that PEDW should favour:
    • gradual evolution
    • behaviour preservation
    • contract preservation
    • incremental consistency
    • reuse of shared helpers over large restructures, wholesale moves, rewrites, or mass renaming.
  • Added a staged roadmap covering:
    • Stage 1 — Current state
    • Stage 2 — Future development guidance
    • Stage 3 — Documentation maturity
    • Stage 4 — Future opportunities
  • Added concise adoption principles emphasising:
    • preserve stable APIs
    • preserve contracts and integrations
    • prefer additive improvements
    • improve findability first
    • avoid unnecessary churn
    • never move APIs solely for cosmetic reasons
  • Added a future-development decision matrix covering:
    • new APIs
    • existing stable APIs
    • existing APIs being enhanced
    • shared/platform concerns
    • cross-cutting concerns
    • orchestration-heavy routes
  • Added qualitative success measures focused on maintainability outcomes rather than implementation metrics.
  • Added explicit non-goals including:
    • no migrations
    • no implementation
    • no route movement
    • no route renaming
    • no API deletion
    • no consolidation
    • no contract changes
    • no behaviour changes
  • Recommended the next architecture planning document only:
    • context/api-maintainer-guidance-v2.md

Validation:

  • Documentation-only planning work; no runtime code changed.
  • Non-destructive synthesis only:
    • reused the completed architecture programme as the evidence base
    • reused the newly completed API grouping plan rather than reopening discovery
    • remained at architecture-roadmap level and did not widen into implementation or migration guidance
  • Did not perform another API inventory.
  • Did not recommend implementation, migration, refactor, or route moves.
  • No repo-wide automated analysis performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next architecture planning document only:
    • context/api-maintainer-guidance-v2.md

CL-2026-06-25-API-GROUPING-PLAN-PHASE-1: future journey-owned API grouping model

date: 2026-06-25 author: Cline scope: context/api-grouping-plan.md, architecture planning for future journey-owned API grouping concepts type: milestone rationale: Create a planning-only architecture document that synthesises the completed API platform, route map, journey map, integration map, and architecture findings into a future journey-owned API grouping model focused on maintainability, findability, consistency, and extensibility without reopening discovery or recommending implementation. impact: Documentation/context only; improves future architecture planning clarity around journey-owned API grouping, shared platform concerns, and API-family classification; no runtime, auth, CRM, storage, queue, notification, i18n, contract, or behaviour change. status: completed

Summary:

  • Confirmed the required planning context was read before drafting:
    • context/api-route-map.md
    • context/journey-architecture-map.md
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • context/integration-map.md
    • memory-bank/change-log.md
  • Added new document: context/api-grouping-plan.md.
  • Kept the document explicitly scoped to architecture planning only.
  • Recorded the key evidence-base finding that the main API maintenance problem is:
    • findability
    • ownership clarity
    • consistency
    • reuse discipline
  • Assessed the proposed future grouping concepts only as documentation guidance:
    • account/
    • admin/
    • appeals/
    • auth/
    • cases/
    • documents/
    • notifications/
    • platform/
    • representations/
    • search/
    • subscriptions/
    • shared/
  • For each proposed group, documented:
    • purpose
    • primary journeys supported
    • typical integrations
    • typical route types
    • representative existing routes
    • shared components used
    • relationship to other groups
    • migration cautions
  • Added a Shared Platform Components section classifying reusable helpers such as:
    • relay helpers
    • response helpers
    • hash helpers
    • signed request helpers
    • Azure Storage helpers
    • auth/session helpers
    • Notify helpers
    • logging helpers
    • validation helpers
  • Added a Journey-to-Group Mapping section working at journey/API-family level only and classifying areas as:
    • Natural fit
    • Likely fit
    • Shared / Platform concern
    • Cross-cutting concern
    • Leave unchanged for now
  • Added future guidance principles and explicit non-goals, including:
    • no implementation
    • no migrations
    • no folder restructuring
    • no route movement
    • no route renaming
    • no API deletions
    • no API consolidation
    • no behaviour change
    • no contract change
  • Recommended the next architecture planning document only:
    • context/api-rationalisation-planning.md

Validation:

  • Documentation-only planning work; no runtime code changed.
  • Non-destructive evidence gathering and synthesis only:
    • read required context files
    • targeted extraction from existing route/journey/platform architecture documents
    • top-level pages/api listing only to compare current shape with future grouping concepts without producing a new API inventory
  • Did not reopen architectural discovery.
  • Did not perform a new route inventory.
  • Did not recommend implementation, refactor, migration, or route moves.
  • No repo-wide automated analysis performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next architecture planning document only:
    • context/api-rationalisation-planning.md

date: 2026-06-22 author: Cline scope: context/journey-architecture-map.md, case messages/notices and related published case communications architecture documentation type: milestone rationale: Extend the Journey Architecture Map with the next bounded maintainability slice by documenting how public case-level notices are loaded and rendered on case pages, how adjacent published communication content such as SIPS events/media participates in the case-detail experience, and how the relevant routes classify under a future grouping view without proposing implementation changes. impact: Documentation/context only; improves maintainer understanding of the case-message retrieval path, the case-page notice rendering model, the split ownership between message props and event/media Redux state, and the boundary between on-page case communications, document delivery, and outbound notifications; no runtime, CRM, relay, auth, storage, notification, or i18n behaviour change. status: completed

Summary:

  • Confirmed the required Slice 8 context was read before investigation:
    • context/journey-architecture-map.md
    • context/api-route-map.md
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • context/integration-map.md
    • memory-bank/change-log.md
  • Extended context/journey-architecture-map.md with Slice 8 covering:
    • Case Messages / Notices
    • Related Published Case Communications
    • Case Communication Architecture
    • Future API Grouping Assessment
    • Messages vs Documents vs Notifications Comparison
  • Documented, for each communication journey:
    • purpose
    • primary entry points
    • loaders / initialisation
    • state ownership
    • service layer
    • API layer
    • integration boundaries
    • ownership model
    • architectural flow
    • change entry set
    • risk classification
  • Recorded the clearest visible notice/message model as:
    • case loader retrieves getCaseMessage(incidentid)
    • messagesObj is passed as page props
    • CaseNoticeBanner renders active banner-style notices on the case-details tab
  • Recorded the clearest visible adjacent published communication model as:
    • SIPS-only conditional event/media fetches
    • Redux eventDetailsObj / mediaDetailsObj
    • live-event banner plus events/media tab presentation in the case summary layer
  • Added the requested future grouping classification for relevant case communication routes using:
    • current location
    • journey owner
    • integration touched
    • future grouping candidate
    • migration caution
  • Added the requested comparison showing:
    • where case messages/notices overlap with published documents and notifications
    • where they differ
    • which one is case-page presentation
    • which one is document delivery
    • which one is outbound communication

Validation:

  • Documentation-only work; no runtime code changed.
  • Non-destructive evidence gathering only:
    • targeted searches across pages, components/case, actions/services, store, and pages/api/endpoint
    • direct review of public/DNS/myportal case loaders, components/case/summary.js, components/case/caseNoticeBanner.js, actions/services/caseDirectService.js, getcasemessage_api.js, and directly relevant SIPS event/media routes
  • Did not widen into notification email delivery or document download behaviour beyond the requested comparison.
  • No repo-wide automated analysis performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next bounded documentation slice only:
    • Case Status / Lifecycle Presentation and Related Published Timeline Signals

CL-2026-06-22-JOURNEY-ARCHITECTURE-MAP-SLICE-7: published-document-discovery and download lifecycle map

date: 2026-06-22 author: Cline scope: context/journey-architecture-map.md, published document discovery and published document retrieval/download architecture documentation type: milestone rationale: Extend the Journey Architecture Map with the next bounded maintainability slice by documenting how published documents are surfaced on case pages, how published-document metadata is retrieved and shaped, how download links are generated, how the dedicated download proxy delivers files, and how the relevant routes classify under a future grouping view without proposing implementation changes or reopening authorization assessment. impact: Documentation/context only; improves maintainer understanding of the case-document discovery path, the metadata-to-hashlink-to-download-proxy handoff, document-history route visibility boundaries, and journey ownership across the document route family; no runtime, CRM, relay, auth, storage, notification, or i18n behaviour change. status: completed

Summary:

  • Confirmed the required Slice 7 context was read before investigation:
    • context/journey-architecture-map.md
    • context/api-route-map.md
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • context/integration-map.md
    • memory-bank/change-log.md
  • Extended context/journey-architecture-map.md with Slice 7 covering:
    • Published Document Discovery
    • Published Document Retrieval / Download
    • Document Delivery Architecture
    • Future API Grouping Assessment
    • Discovery vs Download Comparison
  • Documented, for each document journey:
    • purpose
    • primary entry points
    • loaders / initialisation
    • state ownership
    • service layer
    • API layer
    • integration boundaries
    • ownership model
    • architectural flow
    • change entry set
    • risk classification
  • Recorded the clearest visible discovery model as:
    • search-to-case navigation
    • case documents component bootstrap
    • relay-backed CRM metadata retrieval
    • Redux documentDetailsObj
    • filtered/sorted/paged document presentation
  • Recorded the clearest visible delivery model as:
    • metadata-generated pinswg_hashlink
    • browser fetch of /api/documents/download/[id]?hash=...
    • dedicated download proxy stream
    • browser blob download
  • Recorded that document history routes are present in the document route family but were not visibly surfaced by the reviewed case-document UI path.
  • Added the requested future grouping classification for relevant document routes using:
    • current location
    • journey owner
    • integration touched
    • future grouping candidate
    • migration caution
  • Added the requested comparison showing:
    • shared APIs
    • shared integrations
    • shared ownership assumptions
    • where discovery and download diverge

Validation:

  • Documentation-only work; no runtime code changed.
  • Non-destructive evidence gathering only:
    • targeted searches across pages, components, actions/services, store, and pages/api
    • direct review of case detail entry, case documents UI, search navigation handoff, document metadata routes, document type route, document history routes, and the dedicated download proxy route
  • Did not reopen authorization or security posture assessment.
  • No repo-wide automated analysis performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next bounded documentation slice only:
    • Case Messages / Notices and Related Published Case Communications

CL-2026-06-21-JOURNEY-ARCHITECTURE-MAP-SLICE-6: watchlist-subscriptions and unsubscribe-watchlist-removal lifecycle map

date: 2026-06-21 author: Cline scope: context/journey-architecture-map.md, watchlist/subscriptions and unsubscribe/watchlist-removal architecture documentation type: milestone rationale: Extend the Journey Architecture Map with the next bounded maintainability slice by documenting watched-case creation, viewing, removal, unsubscribe paths, the visible CRM contact↔watched-case relationship model, and future grouping candidates for watched-case APIs without proposing implementation changes or reopening the completed authorization assessment. impact: Documentation/context only; improves maintainer understanding of the CRM watched-case relationship lifecycle, dashboard/search/case watch-state reuse, email-notification participation on watched-case records, and watched-case API ownership boundaries; no runtime, auth, CRM, Notify, storage, or i18n behaviour change. status: completed

Summary:

  • Confirmed the required Slice 6 context was read before investigation:
    • context/journey-architecture-map.md
    • context/api-route-map.md
    • context/portal-api-security-boundary-assessment.md
    • context/architecture.md
    • context/integration-map.md
    • memory-bank/change-log.md
  • Extended context/journey-architecture-map.md with Slice 6 covering:
    • Watchlist Creation
    • Watchlist Viewing
    • Watchlist Removal
    • CRM Relationship Ownership Model
    • Future API Grouping Assessment
    • Watchlist vs Dashboard Comparison
  • Documented, for each watched-case journey:
    • purpose
    • primary entry points
    • loaders / initialisation
    • state ownership
    • service layer
    • API layer
    • integration boundaries
    • ownership model
    • architectural flow
    • change entry set
    • risk classification
  • Recorded the strongest visible CRM relationship model as:
    • CRM Contact ↔ Watched Case
    • represented through pinswg_watchlists
    • with visible fields including watched-case id, contact id, case id, appeal type, and pinswg_emailnotifications
  • Recorded that email-notification participation is visibly expressed on the watched-case relationship record itself rather than through a separate subscription entity in the reviewed frontend code.
  • Added the requested future grouping classification for watched-case routes using:
    • journey ownership
    • integration touched
    • future grouping candidate
    • migration caution
  • Added the requested comparison with My Portal Dashboard, documenting shared dependencies, shared state, shared APIs, and ownership relationship without proposing changes.

Validation:

  • Documentation-only work; no runtime code changed.
  • Non-destructive evidence gathering only:
    • targeted searches across pages, components, actions/services, store, and pages/api
    • direct review of watched-case portal bootstrap pages, search/case watch-action components, dashboard/view-all watched-case components, watched-case routes, unsubscribe pages, and watched-case Redux ownership files
  • Did not reopen exploitability or security posture assessment.
  • No repo-wide automated analysis performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next bounded documentation slice only:
    • Documents / Published Document Retrieval and Download

CL-2026-06-21-JOURNEY-ARCHITECTURE-MAP-SLICE-5: authentication-sign-in and notifications-email lifecycle map

date: 2026-06-21 author: Cline scope: context/journey-architecture-map.md, authentication/sign-in and notifications/email architecture documentation type: milestone rationale: Extend the Journey Architecture Map with the next bounded maintainability slice by documenting authentication/sign-in and notifications/email as cross-cutting journeys, including future grouping classification for auth/email-related APIs and an explicit auth-vs-notification comparison, without reopening the completed authorization assessment or proposing implementation changes. impact: Documentation/context only; improves maintainer understanding of NextAuth-to-CRM bootstrap continuity, locale-aware sign-in behaviour, Notify usage across auth and business notifications, and journey ownership/grouping boundaries for auth/email routes; no runtime, auth, CRM, Notify, storage, or i18n behaviour change. status: completed

Summary:

  • Confirmed the required Slice 5 context was read before investigation:
    • context/journey-architecture-map.md
    • context/api-route-map.md
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • context/integration-map.md
    • memory-bank/change-log.md
  • Extended context/journey-architecture-map.md with Slice 5 covering:
    • Authentication / Sign-In
    • Notifications / Email
    • Ownership / Identity Model
    • Future API Grouping Assessment
    • Auth vs Notification Comparison
  • Documented, for each auth/email journey:
    • purpose
    • primary entry points
    • loaders / initialisation
    • state ownership
    • service layer
    • API layer
    • integration boundaries
    • ownership / identity model
    • architectural flow
    • change entry set
    • risk classification
  • Recorded the strongest visible sign-in continuity model as:
    • sign-in page locale resolution
    • NextAuth verification + callback
    • session creation
    • homepage bootstrap
    • getPortalLogin(session.user.email)
    • /myportal or /account/register
  • Recorded the strongest visible notifications model as split between:
    • thin direct Notify sends via pages/api/email/notify.js
    • orchestration-heavy aggregation/batch sends via pages/api/email/getall.js and supporting data routes
  • Added the requested future grouping classification for auth/email-related routes using:
    • current location
    • journey ownership
    • integration touched
    • future grouping candidate
    • migration caution
  • Added the requested comparison showing:
    • where auth and notifications are independent
    • where they overlap
    • where Notify acts as auth-support integration
    • where Notify acts as business-notification integration
  • Kept the grouping and comparison sections explicitly framed as documentation/discovery only and not implementation recommendations.

Validation:

  • Documentation-only work; no runtime code changed.
  • Non-destructive evidence gathering only:
    • targeted searches across pages, components, actions/services, lib, and pages/api
    • direct review of sign-in pages, verify-request page, homepage bootstrap, logout helpers, NextAuth handler, locale resolution route, email routes, Notify service helpers, and completion/watchlist notification callers
  • Did not reopen the completed authorization risk/security assessment.
  • No repo-wide automated analysis performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next bounded documentation slice only:
    • Watchlist / Subscriptions and Unsubscribe Flows

CL-2026-06-21-JOURNEY-ARCHITECTURE-MAP-SLICE-4: account registration and personal-details/account-management lifecycle map

date: 2026-06-21 author: Cline scope: context/journey-architecture-map.md, account registration and personal-details/account-management architecture documentation type: milestone rationale: Extend the Journey Architecture Map with the next bounded maintainability slice by documenting account registration and personal-details/account-management journeys, and by classifying relevant APIs against a future journey-based grouping model without proposing implementation changes. impact: Documentation/context only; improves maintainer understanding of the NextAuth-session-to-CRM-contact bootstrap model, account journey ownership, and future grouping candidates for account/auth/shared/platform-level APIs; no runtime, auth, storage, CRM, or i18n behaviour change. status: completed

Summary:

  • Re-read required Slice 4 context before investigation:
    • context/journey-architecture-map.md
    • context/api-route-map.md
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • context/integration-map.md
    • memory-bank/change-log.md
  • Extended context/journey-architecture-map.md with Slice 4 covering:
    • Account Registration
    • Personal Details / Account Management
    • Ownership Model
    • Future API Grouping Assessment
  • Documented, for each account journey:
    • purpose
    • primary entry points
    • loaders / initialisation
    • state ownership
    • service layer
    • API layer
    • integration boundaries
    • ownership model
    • architectural flows
    • change entry sets
    • risk classification
  • Recorded how the journeys strengthen the identity model:
    • registration bridges NextAuth session.user.email -> CRM Contact
    • account management depends on that bridge and reuses CRM contact identity for account reads/updates
  • Added a future grouping classification for relevant account/auth routes using:
    • journey owner
    • integration touched
    • future grouping candidate
    • migration caution
  • Kept the grouping section explicitly framed as a future grouping assessment only and not an implementation recommendation.

Validation:

  • Documentation-only work; no runtime code changed.
  • Non-destructive evidence gathering only:
    • targeted searches across pages, components/account, actions/services, pages/api, and store
    • direct review of homepage bootstrap, registration page/components, personal-details page/components, account service helpers, account endpoint handlers, and relevant auth support routes
  • No repo-wide automated analysis performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next bounded documentation slice only:
    • Authentication / Sign-In and Notifications / Email

CL-2026-06-21-JOURNEY-ARCHITECTURE-MAP-SLICE-3: draft representation and representation submission/finalisation lifecycle map

date: 2026-06-21 author: Cline scope: context/journey-architecture-map.md, representation draft creation and representation submission/finalisation architecture documentation type: milestone rationale: Extend the Journey Architecture Map with the next bounded maintainability slice by documenting the representation draft lifecycle and the representation submission/finalisation lifecycle, including an explicit comparison against the already documented appeal lifecycle, without proposing implementation changes. impact: Documentation/context only; improves maintainer understanding of representation storage ownership, queue handoff, CRM representation boundaries, and appeal-vs-representation lifecycle differences; no runtime, auth, storage, queue, CRM, or i18n behaviour change. status: completed

Summary:

  • Re-read required Slice 3 context before investigation:
    • context/journey-architecture-map.md
    • context/api-route-map.md
    • context/architecture.md
    • context/integration-map.md
    • memory-bank/change-log.md
  • Extended context/journey-architecture-map.md with Slice 3 covering:
    • Draft Representation Creation
    • Representation Submission / Finalisation
    • Appeal vs Representation Lifecycle Comparison
  • Documented, for each representation journey:
    • purpose
    • primary entry points
    • loaders / initialisation
    • state ownership
    • service layer
    • API layer
    • integration boundaries
    • ownership model
    • architectural flows
    • change entry sets
    • risk classification
  • Added the requested comparison across:
    • ownership
    • storage
    • queue
    • CRM
    • maintainability
  • Recorded the main visible distinction from appeals as:
    • representation drafts are more tightly case-linked and repfile_name-keyed
    • representation completion concentrates more client-side side effects around involvement, queue handoff, email, and watched/submitted status updates

Validation:

  • Documentation-only work; no runtime code changed.
  • Non-destructive evidence gathering only:
    • targeted searches across pages, lib, actions/services, pages/api, and store
    • direct review of representation loaders, representation completion component, storage routes, queue/completion routes, and representation-specific Redux state
  • No repo-wide automated analysis performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next bounded documentation slice only:
    • Account Registration and Personal Details / Account Management

CL-2026-06-21-JOURNEY-ARCHITECTURE-MAP-SLICE-2: draft appeal and appeal submission/finalisation lifecycle map

date: 2026-06-21 author: Cline scope: context/journey-architecture-map.md, draft appeal creation and appeal submission/finalisation architecture documentation type: milestone rationale: Extend the Journey Architecture Map with the next bounded maintainability slice by documenting the draft appeal lifecycle and the appeal submission/finalisation lifecycle from page entry through state, services, APIs, storage, queue, and CRM transition without proposing implementation changes. impact: Documentation/context only; improves maintainer understanding of draft ownership, storage container boundaries, queue handoff, and submitted-appeal transition; no runtime, auth, storage, queue, CRM, or i18n behaviour change. status: completed

Summary:

  • Re-read required Slice 2 context before investigation:
    • context/journey-architecture-map.md
    • context/api-route-map.md
    • context/architecture.md
    • context/integration-map.md
    • memory-bank/change-log.md
  • Extended context/journey-architecture-map.md with Slice 2 covering:
    • Draft Appeal Creation
    • Appeal Submission / Finalisation
  • Documented, for each journey:
    • purpose
    • primary entry points
    • loaders / initialisation
    • state ownership
    • service layer
    • API layer
    • integration boundaries
    • ownership model
    • architectural flows
    • change entry sets
    • risk classification
  • Recorded the strongest visible draft ownership model as:
    • NextAuth session.user.id -> containerID -> Azure Storage container -> casefolder / draft blob state
  • Recorded the clearest visible submitted-appeal transition as:
    • draft blob state
    • finalisation route
    • Azure Queue message
    • adjacent CRM mutation boundary
  • Added Slice 2-specific investigation method, risks/cautions, validation summary, and a documentation-only recommendation for the next journey slice.

Validation:

  • Documentation-only work; no runtime code changed.
  • Non-destructive evidence gathering only:
    • targeted searches across pages, lib, components/newappeal, actions/services, actions, pages/api, and store
    • direct review of draft/resume loaders, Redux hydration helpers, new appeal flow components, storage helpers, finalisation handlers, and adjacent CRM mutation routes
  • No repo-wide automated analysis performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next bounded documentation slice only:
    • Representation Draft Creation and Representation Submission / Finalisation

CL-2026-06-20-PORTAL-API-PLATFORM-ASSESSMENT-SLICE-A: API family classification sample

date: 2026-06-20 author: Cline scope: context/portal-api-platform-assessment.md, sampled API families under pages/api/{endpoint,file,email,documents,auth} type: milestone rationale: Record the first bounded slice of the Portal Integration Contract & API Platform Assessment by classifying representative API families, integration responsibilities, repeated patterns, duplication candidates, and contract-critical areas without widening into a full inventory. impact: Documentation/context only; improves visibility of the portal API platform shape and integration-family boundaries; no runtime, contract, auth, storage, email, or i18n behaviour change. status: completed

Summary:

  • Added context/portal-api-platform-assessment.md.
  • Confirmed the required context files were read before starting the slice:
    • context/architecture.md
    • context/portal-api-security-boundary-assessment.md
    • context/integration-map.md
    • memory-bank/debt-list.md
    • memory-bank/change-log.md
  • Assessed only the requested first slice folders:
    • pages/api/endpoint
    • pages/api/file
    • pages/api/email
    • pages/api/documents
    • pages/api/auth
  • Used a representative bounded sample to classify the visible API platform families into:
    • CRM relay read
    • CRM relay write
    • storage/blob read
    • storage/blob write
    • queue/finalisation
    • email/notification
    • document download
    • auth/session
    • local utility/meta
    • middleware/helper
  • Recorded the integration responsibility map from the sample, showing a mix of:
    • CRM relay-backed
    • Azure Storage SDK-backed
    • GOV.UK Notify-backed
    • NextAuth-backed
    • local-only
    • mixed/orchestration routes
  • Identified repeated sample patterns including:
    • hash validation
    • helper-based relay forwarding
    • required query/body validation
    • respondSuccess / respondError response envelopes
    • raw CRM URL construction in older handlers
    • Azure blob path construction
    • mixed logging styles
  • Marked only high-level duplication candidates from the sample, distinguishing likely intentional vs likely historical vs unclear families without recommending consolidation yet.
  • Identified contract-critical sampled families including public search, myportal/dashboard, submission/finalisation, file upload/download, document download, auth/session, and email/notification.

Validation:

  • Documentation-only assessment; no runtime code changed.
  • Non-destructive evidence gathering only:
    • small directory listings of sampled API folders and pages/api/middleware
    • direct file inspection of representative sample handlers
    • targeted grep/search limited to sampled folders for relay helper usage, hash usage, axios/direct integration usage, Notify usage, and auth/locale/redirect patterns
  • No repo-wide automated analysis performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next bounded slice: Slice B — Endpoint CRM Relay Shape Sample, restricted to a representative subset of pages/api/endpoint to distinguish newer helper-based relay routes from older direct relay wrappers and to classify main endpoint contract shapes without proposing implementation.

CL-2026-06-20-PORTAL-API-PLATFORM-ASSESSMENT-SLICE-B: endpoint CRM relay contract shape sample

date: 2026-06-20 author: Cline scope: context/portal-api-platform-assessment.md, representative pages/api/endpoint route sample type: milestone rationale: Record the second bounded slice of the Portal Integration Contract & API Platform Assessment by classifying endpoint contract shapes inside pages/api/endpoint, comparing helper-oriented read contracts with older direct-wrapper mutation contracts, and assessing contract consistency without widening into a full endpoint inventory. impact: Documentation/context only; improves visibility of endpoint contract structure, implementation-style split, and maintainability shape in the CRM relay layer; no runtime, auth, storage, i18n, or API behaviour change. status: completed

Summary:

  • Re-read required Slice B context before inspection:
    • context/portal-api-platform-assessment.md
    • context/portal-api-security-boundary-assessment.md
    • context/architecture.md
    • memory-bank/change-log.md
  • Assessed only a bounded representative subset inside pages/api/endpoint across the requested route groups:
    • public read/search
    • authenticated/myportal read
    • CRM create
    • CRM update/patch/delete
    • proxy/pass-through
  • Classified the sampled endpoint layer into a small number of recurring contract shapes:
    • public CRM read
    • user-owned CRM read
    • CRM create
    • CRM patch/update
    • CRM delete
    • proxy/pass-through
    • lookup/config/support
    • hybrid upsert/orchestration
  • Recorded the strongest visible implementation split in pages/api/endpoint:
    • newer helper-oriented read routes using relayGet(...), shared response helpers, transforms, and relay policy presets
    • older direct-wrapper mutation routes using getToken(), axios(config), WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), and manual CRM method configuration
  • Recorded that response envelope style is more consistent than integration implementation style:
    • respondError(...) / respondSuccess(...) are broadly used across both newer and older route styles
  • Recorded that paged/search/document read routes repeatedly use transform/pagination sub-patterns:
    • transformData
    • @odata.nextLink normalization
    • custom request headers for paged reads
  • Concluded that the endpoint/ route count likely overstates uniqueness, because the sampled evidence suggests relatively few repeated contract shapes rather than many fully bespoke contracts.

Validation:

  • Documentation-only assessment; no runtime code changed.
  • Non-destructive evidence gathering only:
    • direct inspection of representative endpoint routes including:
      • getbasicsearch_api.js
      • getbasicsearchpaged_api.js
      • getadvancedsearch_api.js
      • getsearchdocumentdetails_api.js
      • getsearchdocumenthistory_api.js
      • getmycases_api.js
      • getmyrepresentations_api.js
      • getwatchedcases_api.js
      • getawaitingsubmission_api.js
      • getpersonalaccount_api.js
      • createcase_api.js
      • createaccount_api.js
      • createwatchedcases_api.js
      • updateaccount_api.js
      • patchcase_api.js
      • deletewatchedcases_api.js
      • deletemyrepresentations_api.js
      • getwatchedcasesproxy_api.js
    • targeted endpoint-only searches for helper/direct-wrapper indicators:
      • relayGet(...)
      • relayGetData(...)
      • axios(...)
      • hashAPIPath(...)
      • respondSuccess(...) / respondError(...)
      • transformData
      • @odata.nextLink
      • common status-code patterns
  • No repo-wide automated analysis performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next bounded slice: Slice C — Endpoint Transform and Pagination Pattern Sample, restricted to representative pages/api/endpoint routes that use transforms, @odata.nextLink normalization, paged/unpaged variants, and supplementary relayGetData(...) enrichment so the transform/pagination sub-patterns can be classified without proposing implementation.

CL-2026-06-20-PORTAL-API-PLATFORM-ASSESSMENT-SLICE-C: API maintenance map and reuse baseline

date: 2026-06-20 author: Cline scope: context/portal-api-platform-assessment.md, top-level pages/api maintenance structure and existing reuse primitives type: milestone rationale: Record the third bounded slice of the Portal Integration Contract & API Platform Assessment by mapping current folder responsibility, feature-to-API maintenance entry points, folder drift, reusable building blocks, and a future-facing consistency baseline without proposing implementation or file movement. impact: Documentation/context only; improves API findability and maintainability guidance for future work planning; no runtime, auth, storage, email, i18n, or API behaviour change. status: completed

Summary:

  • Re-read required Slice C context before inspection:
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • context/integration-map.md
    • memory-bank/debt-list.md
    • memory-bank/change-log.md
  • Built a bounded maintenance map for the top-level API areas:
    • endpoint
    • file
    • email
    • documents
    • auth
    • admin
    • middleware
    • top-level utility/meta files
  • Recorded that current API maintenance difficulty is driven primarily by:
    • folder responsibility overlap
    • historical naming drift
    • split document/account/auth-support behaviors across multiple areas
    • coexistence of older direct-wrapper and newer shared-helper implementation styles
  • Added a feature-to-API maintenance map covering major portal areas including:
    • search
    • case details
    • document download
    • my portal/dashboard
    • watched cases
    • representations
    • drafts
    • submission/finalisation
    • account/auth
    • email/notifications
    • admin/internal reporting
  • Identified reusable building blocks already present, including:
    • relayGet(...)
    • relayGetData(...)
    • respondSuccess(...) / respondError(...)
    • relay policy presets
    • signed request client helpers
    • hash/path validation helpers
    • Azure storage helpers
    • logging helpers
  • Added a future-facing consistency baseline as guidance only, emphasizing reuse of existing shared helpers for new work where suitable and avoiding blind copying of stable but older direct axios + hashAPIPath patterns.

Validation:

  • Documentation-only assessment; no runtime code changed.
  • Non-destructive evidence gathering only:
    • small top-level listings of pages/api and pages/api/admin
    • direct representative inspection of:
      • pages/api/admin/getnewappeals_api.js
      • pages/api/admin/getlatestdocuments_api.js
      • pages/api/health.js
      • pages/api/doc.ts
      • pages/api/notices/index.js
      • pages/api/middleware/{apiResponse,relayForwarding,relayPolicyPresets,middleware}.js
      • actions/azurestorage.js
      • actions/clients/signedRequestClient.js
    • targeted searches across pages/api for existing helper and integration-building-block usage
    • reuse of already completed bounded evidence from Slices A and B for folder/feature classification
  • No repo-wide automated analysis performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next bounded step: Slice D — API Route Ownership & Change-Entry Sample, focused on a small set of high-value journeys (watched cases, account/personal details, public document retrieval, appeal submission/finalisation) to identify the minimum safe API entry set a maintainer would need to inspect for future changes.

CL-2026-06-20-PORTAL-API-PLATFORM-ASSESSMENT-SLICE-D: route ownership and change-entry sample

date: 2026-06-20 author: Cline scope: context/portal-api-platform-assessment.md, bounded journey change-entry mapping across watched cases, account/personal details, public document retrieval, and appeal submission/finalisation type: milestone rationale: Record the fourth bounded slice of the Portal Integration Contract & API Platform Assessment by identifying maintainer-first change-entry sets, route ownership, change risk, first-look checklists, and reuse points for four high-value journeys without widening into a full dependency inventory. impact: Documentation/context only; improves maintainability and change-entry clarity for future API work; no runtime, auth, storage, queue, i18n, or API behaviour change. status: completed

Summary:

  • Re-read required Slice D context before inspection:
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • context/integration-map.md
    • memory-bank/change-log.md
  • Assessed only the requested four journeys:
    • watched cases
    • account / personal details
    • public document retrieval
    • appeal submission / finalisation
  • Built maintainer-first change-entry sets for each journey, covering:
    • primary UI/component entry points
    • likely service helpers
    • API routes
    • shared clients/helpers
    • relevant state/store modules
    • integrations touched
  • Classified route ownership for sampled journey routes using:
    • feature-owned
    • integration-owned
    • orchestration-owned
    • helper/support
    • ambiguous/historical
  • Recorded journey-level change risk:
    • watched cases -> high
    • account/personal details -> high
    • public document retrieval -> medium-high
    • appeal submission/finalisation -> very high
  • Added short first-look checklists so a maintainer can identify the minimum safe inspection set before changing behavior in each sampled journey.
  • Identified reuse points already present for each journey, including relay helpers, response helpers, signing/hash helpers, Azure storage helpers, and logging helpers.

Validation:

  • Documentation-only assessment; no runtime code changed.
  • Non-destructive evidence gathering only:
    • targeted bounded searches across components, actions/services, pages, lib, and store for the four sampled journeys
    • direct representative inspection of:
      • components/myportal/viewall.js
      • pages/account/personaldetails.js
      • components/account/personaldetails.js
      • components/search/searchresults.js
      • lib/myportal/loadMyPortalAppealPage.js
      • components/case/representation/representationComplete.js
    • reuse of already reviewed route/service evidence from prior slices for linked API ownership and helper classification
  • No repo-wide automated analysis performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next bounded step: Slice E — API Maintainer Decision Guide Sample, focused on a small sample of maintenance intents (for example: adding a CRM read route, extending a watched-case mutation, adding a document-related route, or extending a finalisation orchestration path) so preferred reusable patterns can be distinguished from older compatibility-driven ones without proposing implementation.

CL-2026-06-20-PORTAL-API-PLATFORM-ASSESSMENT-SLICE-E: wider API surface pattern validation

date: 2026-06-20 author: Cline scope: context/portal-api-platform-assessment.md, top-level API surface validation across wider route families and implementation styles type: milestone rationale: Record the fifth bounded slice of the Portal Integration Contract & API Platform Assessment by validating whether the route-family, contract-shape, implementation-style, and folder-drift conclusions from earlier representative slices generalise across the wider API surface without generating a full endpoint inventory. impact: Documentation/context only; increases confidence that the architecture model is representative of the wider API platform; no runtime, auth, storage, queue, i18n, or API behaviour change. status: completed

Summary:

  • Re-read required Slice E context before inspection:
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • context/integration-map.md
    • memory-bank/change-log.md
  • Performed bounded top-level API folder sizing and validation:
    • endpoint -> 74 files
    • file -> 26 files
    • email -> 6 files
    • documents -> 1 file
    • auth -> 2 files
    • admin -> 5 files
    • middleware -> 4 files
    • top-level files -> 4
  • Confirmed that the earlier API platform model still appears representative:
    • endpoint remains the dominant historical CRM relay catch-all
    • file remains the main secondary mixed storage/orchestration folder
    • smaller folders (email, documents, auth, admin, middleware) did not reveal a materially new route family or implementation style
  • Validated that the existing route-family model still covers the wider surface, including:
    • search/read
    • myportal reads
    • account/profile
    • create/update/delete
    • document retrieval
    • storage/blob
    • queue/finalisation
    • notifications
    • admin/reporting
    • auth/session
    • lookup/config/support
  • Confirmed that no genuinely new major contract shape was needed beyond the existing set:
    • Public CRM read
    • User-owned CRM read
    • CRM create
    • CRM update/patch
    • CRM delete
    • Proxy/pass-through
    • Lookup/config/support
    • Hybrid upsert/orchestration
  • Confirmed that the implementation-style model still holds across the wider surface:
    • helper-oriented relay routes
    • direct-wrapper routes
    • orchestration routes
    • with local/static support routes treated as small support variants, not a separate major style
  • Classified folder drift:
    • low drift: documents, admin, middleware, top-level utility routes
    • low-to-moderate drift: auth
    • moderate drift: email
    • high drift: endpoint, file
  • Identified main maintenance hotspots:
    • high: endpoint, file
    • medium-high: auth, middleware
    • medium: email
    • low/medium-low: documents, admin
  • Recorded a high-confidence conclusion that Slices AD are representative at the platform pattern level, even though route-local nuance still exists.

Validation:

  • Documentation-only assessment; no runtime code changed.
  • Non-destructive evidence gathering only:
    • bounded top-level API folder-size count pass
    • targeted route-name searches across pages/api, pages/api/endpoint, and pages/api/file
    • direct representative inspection of selected wider-surface outlier routes:
      • pages/api/endpoint/getmandatoryfields_api.js
      • pages/api/endpoint/createcrmtask_api.js
      • pages/api/file/createcaseinvolvement_api.js
      • pages/api/file/generateappealpdf.js
      • pages/api/email/getdocuments.js
      • pages/api/admin/getStatusCountsByAppealAndLPA_api.js
    • comparison against the documented conclusions from Slices AD
  • No full route inventory generated.
  • No repo-wide automated analysis performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next bounded step: Slice F — API Platform Final Synthesis, consolidating Slices AE into one stable architecture-level API platform view covering route families, contract shapes, implementation styles, folder drift, maintainer guidance, and final assessment conclusions without proposing implementation.

CL-2026-06-20-PORTAL-API-PLATFORM-ASSESSMENT-SLICE-F: final synthesis and stream closure

date: 2026-06-20 author: Cline scope: context/portal-api-platform-assessment.md, context/architecture.md, API platform assessment stream closure type: milestone rationale: Consolidate Slices AE into one concise architecture-level API platform view, record the stable route-family / contract-shape / implementation-style model, and formally close the Portal Integration Contract & API Platform Assessment stream. impact: Documentation/context only; improves long-term maintainability guidance and architectural clarity for future API planning work; no runtime, auth, storage, queue, i18n, or API behaviour change. status: completed

Summary:

  • Re-read required Slice F context before synthesis:
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • memory-bank/change-log.md
  • Added a final synthesis section to context/portal-api-platform-assessment.md and marked the stream complete.
  • Recorded the stable architecture-level API platform model as:
    • large route surface
    • small route-family vocabulary
    • small contract-shape vocabulary
    • small implementation-style vocabulary
  • Consolidated the stable route-family model:
    • CRM relay routes
    • storage/blob routes
    • finalisation/orchestration routes
    • email/notification routes
    • document download routes
    • auth/session routes
    • admin/internal routes
    • middleware/helper routes
    • local utility/meta routes
  • Consolidated the stable contract-shape model:
    • Public CRM read
    • User-owned CRM read
    • CRM create
    • CRM update/patch
    • CRM delete
    • Proxy/pass-through
    • Lookup/config/support
    • Hybrid upsert/orchestration
    • Storage read/write/delete
    • Queue/finalisation
    • Notify send / notification orchestration
  • Consolidated the stable implementation-style model:
    • newer helper-oriented
    • older direct-wrapper
    • orchestration-heavy
  • Recorded the stable folder-drift and maintenance-hotspot model:
    • highest drift/hotspots remain endpoint and file
    • smaller folders are more coherent but still require sensitivity in auth and middleware
  • Added a maintainer guidance baseline emphasizing:
    • identify owning feature/journey first
    • identify integration touched
    • identify contract-criticality
    • prefer existing shared helpers where suitable
    • avoid blindly copying older direct-wrapper patterns into new work
    • avoid refactoring stable legacy routes without explicit approval and characterization
  • Updated context/architecture.md so the core architecture reference now includes the completed Portal API Platform Assessment and its stable final model.
  • Formally recorded that the Portal Integration Contract & API Platform Assessment should now be considered complete.

Validation:

  • Documentation-only synthesis and closure; no runtime code changed.
  • Non-destructive evidence base reused from completed Slices AE.
  • No new route inventory or automated analysis performed for closure.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Recommended future planning stream only:
    • API Route Map / Maintainer Guide
    • or API Rationalisation Planning
  • No implementation work is recommended from this assessment alone.

CL-2026-06-20-API-ROUTE-MAP-MAINTAINER-GUIDE: first-generation journey-owned route map

date: 2026-06-20 author: Cline scope: context/api-route-map.md, maintainability-focused journey-owned API documentation type: milestone rationale: Create a first-generation maintainer guide that converts the completed API platform assessment into a journey-owned route map so future contributors can identify where to start, which APIs/helpers matter, what integrations are touched, and where change risk is highest without reopening discovery. impact: Documentation/context only; improves API maintainability, findability, and ownership clarity for future planning and change review; no runtime, auth, storage, queue, i18n, or API behaviour change. status: completed

Summary:

  • Re-read required context before drafting the route map:
    • context/portal-api-platform-assessment.md
    • context/architecture.md
    • context/integration-map.md
    • memory-bank/change-log.md
  • Added new document: context/api-route-map.md.
  • Structured the guide around journey ownership, not folder ownership.
  • Documented the requested major journey catalogue, including:
    • Public Search
    • Case Details
    • Documents
    • My Portal Dashboard
    • Watched Cases
    • Representations
    • Draft Appeals
    • Draft Representations
    • Appeal Submission / Finalisation
    • Representation Submission / Finalisation
    • Account Registration
    • Personal Details / Account Management
    • Authentication / Sign-In
    • Notifications / Email
    • Admin / Reporting
  • For each journey, recorded:
    • purpose
    • primary UI entry points
    • service layer helpers
    • primary API routes a maintainer should inspect first
    • integrations touched
    • ownership type
    • change risk
    • first-look checklist
  • Added a shared platform section documenting reusable API building blocks such as:
    • relayGet
    • relayGetData
    • respondSuccess
    • respondError
    • relay policy helpers
    • hash helpers
    • signed request helpers
    • Azure storage helpers
    • Notify helpers
    • auth/session helpers
  • Added maintainer guidance describing the recommended decision sequence when adding a new API:
    • which journey owns it
    • which integration it touches
    • whether an existing route family exists
    • whether existing helpers can be reused
    • whether the route is contract-critical
  • Recorded that the resulting document is sufficient as a first-generation maintainer guide and that any future expansion should be treated as a separate planning/documentation stream rather than renewed platform discovery.

Validation:

  • Documentation-only work; no runtime code changed.
  • Non-destructive consolidation based on completed architecture streams and existing API platform conclusions.
  • No new discovery or route inventory performed.
  • No Python used.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Optional future documentation stream only:
    • deeper API Route Map / Maintainer Guide v2
    • or API Rationalisation Planning
  • No implementation work recommended from this guide alone.

CL-2026-06-19-ARCHITECTURE-PROGRAMME-MILESTONE: discovery close-out and transition to adoption planning

date: 2026-06-19 author: Architecture Review scope: architecture programme type: milestone rationale: Record the conclusion of the architectural discovery programme and the transition into the next architecture phase. impact: Documentation/context only; improves programme clarity, handoff readiness, and next-stream prioritisation; no runtime or implementation behaviour change. status: completed

Summary:

Architectural discovery programme concluded.

Final classifications established:

  • Business Decision Models
  • Presentation Models
  • CRM Compatibility Models
  • Workflow / Integration Models

Recommendation:

Transition architecture effort from discovery to adoption planning and API security assessment.

Validation:

  • Documentation-only update.
  • No implementation files changed.
  • No tests or lint run because this was a documentation-only milestone entry.

Follow-ups:

  • Use Portal API Security & Access Boundary Assessment as the recommended next architecture stream.

CL-2026-06-19-PORTAL-API-SECURITY-FIRST-PASS: endpoint inventory and access-boundary assessment

date: 2026-06-19 author: Cline scope: pages/api/**, context/portal-api-security-boundary-assessment.md type: milestone rationale: Record the first-pass architecture assessment of the portal API security and access boundary, focusing on endpoint inventory, authentication visibility, trust boundaries, and ownership enforcement evidence. impact: Documentation/context only; improves visibility of API security posture and next-step prioritisation; no runtime, contract, auth, or i18n behaviour change. status: completed

Summary:

  • Added context/portal-api-security-boundary-assessment.md as a concise first-pass assessment of pages/api/**.
  • Recorded a route-family inventory across auth, endpoint, file, email, documents, admin, and utility API areas.
  • Identified the main current architectural concern:
    • route-level authentication and ownership enforcement are not consistently visible in sensitive handlers
    • many routes trust caller-supplied identifiers such as loggedInUserId, contactId, incidentId, blob/container/path values, and record IDs
    • file/blob routes rely heavily on signed hash validation, but that is not equivalent to explicit per-route ownership verification
  • Recorded that the only explicit session gate found during the first-pass scan was pages/api/endpoint/gethash_api.js, which protects hash issuance for a narrow allowlist of sensitive downstream paths.
  • Prioritised the next pass as end-to-end tracing of authenticated hash issuance and downstream consumption in sensitive file/blob and user-owned CRM routes.

Validation:

  • Documentation-only assessment; no runtime code changed.
  • Read required architecture and memory-bank context files before assessment.
  • Non-destructive inventory and evidence commands used:
    • recursive listing of pages/api/**
    • route-family count command across pages/api
    • targeted code searches for session/auth usage and trusted identifiers
    • direct review of representative high-risk handlers in auth, file, email, documents, middleware, and endpoint families
  • Lint/tests not run because no implementation files were changed.

Follow-ups:

  • Next assessment pass: trace gethash_api consumers and determine whether sensitive downstream routes are effectively session-bound and ownership-bound, or only path-hash protected.

CL-2026-06-19-PORTAL-API-OWNERSHIP-TRACE: session-to-contact and ownership-enforcement trace

date: 2026-06-19 author: Cline scope: context/portal-api-security-boundary-assessment.md, portal SSR loaders, account/portal/document service flows, selected user-owned API handlers type: milestone rationale: Record the ownership-enforcement trace for the Portal API Security & Access Boundary Assessment by identifying how the authenticated session becomes portal identity, CRM contact identity, blob/container identity, and user-owned API access. impact: Documentation/context only; clarifies current authorization architecture and ownership-enforcement locations; no runtime, contract, auth, or i18n behaviour change. status: completed

Summary:

  • Extended context/portal-api-security-boundary-assessment.md with an ownership-enforcement trace focused on:
    • session -> email -> portal user -> CRM contact flow
    • split identity model between CRM contact ownership and blob/container ownership
    • the role of pinsUser cookie as a CRM-contact shortcut in some SSR flows
    • location mapping for where ownership is established vs merely propagated
  • Documented that ownership enforcement is not primarily route-local in sampled APIs.
  • Recorded the dominant pattern as:
    • SSR/page loaders establish identity from session and/or cookie
    • service helpers propagate that identity into API calls
    • CRM ownership is often expressed as query filtering by contact ID
    • blob ownership is often expressed as container scoping by session.user.id
  • Traced high-risk flows end-to-end:
    • My Cases
    • My Representations
    • Watched Cases
    • Draft Appeals
    • Draft Representations
    • Document Retrieval
  • Concluded that ownership enforcement is partially centralized at session/bootstrap time but operationally distributed across SSR loaders, cookies, query construction, CRM filtering, and blob container naming.

Validation:

  • Documentation-only assessment update; no runtime code changed.
  • Non-destructive evidence gathering performed via targeted searches and direct code review of:
    • lib/auth/resolveMyPortalAuthContext.js
    • lib/representation/pageLoaders.js
    • lib/newappeal/loadNewAppealPage.js
    • lib/myportal/loadMyPortalAppealPage.js
    • pages/myportal/index.js
    • pages/myportal/case/[ticketnumber].js
    • pages/myportal/representation.js
    • actions/services/{accountDirectService,portalDirectService,documentDirectService}.js
    • pages/api/endpoint/createwatchedcases_api.js
    • previously reviewed user-owned API handlers from the first-pass assessment
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next assessment pass: trace the provenance and lifecycle of the pinsUser cookie and compare it against the getPortalLogin(session.user.email) path to determine which identity source is canonical and where divergence risk exists.

date: 2026-06-19 author: Cline scope: context/portal-api-security-boundary-assessment.md, pinsUser cookie lifecycle, SSR loaders, homepage/login routing, account/search/myportal flows type: milestone rationale: Record the architectural role of pinsUser by tracing where it is created, refreshed, cleared, and consumed, and by comparing it to the canonical session-to-CRM-contact identity path. impact: Documentation/context only; clarifies current identity and trust-boundary model; no runtime, contract, auth, or i18n behaviour change. status: completed

Summary:

  • Extended the assessment with a dedicated pinsUser trace covering:
    • cookie contents
    • creation source
    • overwrite/refresh behavior
    • clear/delete paths
    • consumption across SSR and client flows
  • Determined that pinsUser stores a CRM contactid value.
  • Identified the primary creation path in pages/index.js:
    • NextAuth session -> session.user.email -> getPortalLogin(email) -> CRM contactid -> setCookie("pinsUser", contactid)
  • Identified that later flows frequently consume pinsUser directly without re-resolving CRM contact from the current session.
  • Recorded the architectural conclusion that pinsUser behaves as:
    • a cached CRM contact identity
    • a convenience shortcut
    • and likely a compatibility mechanism in cookie-driven flows rather than the canonical business authorization authority.
  • Confirmed that CRM Contact remains the strongest visible business authorization boundary for CRM-owned portal data, while session.user.id remains the visible ownership boundary for blob/draft flows.

Validation:

  • Documentation-only assessment update; no runtime code changed.
  • Non-destructive evidence gathering performed via targeted searches and direct review of:
    • pages/index.js
    • pages/account/personaldetails.js
    • components/search/searchresults.js
    • components/search/addresssearchresults.js
    • lib/auth/sessionClient.js
    • lib/auth/resolveMyPortalAuthContext.js
    • lib/newappeal/loadNewAppealPage.js
    • lib/myportal/loadMyPortalAppealPage.js
    • pages/error.js
    • pages/_error.js
    • pages/components search results for pinsUser, setCookie, destroyCookie, and parseCookies
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next assessment pass: trace registration and post-registration bootstrap to determine exactly how a NextAuth-authenticated user becomes a CRM Contact and where dashboard authorization is granted after contact creation.

CL-2026-06-19-PORTAL-API-ACCOUNT-MUTATION-TRACE: account/profile read and mutation boundary assessment

date: 2026-06-19 author: Cline scope: context/portal-api-security-boundary-assessment.md, account/profile pages, account direct services, account endpoint handlers type: milestone rationale: Record the account/profile mutation assessment by tracing how account pages establish identity, how personal details are read and updated, whether email can diverge from session identity, and whether account/password APIs re-bind caller-supplied contact IDs to the authenticated session. impact: Documentation/context only; clarifies account/profile authorization posture and legacy-route risk; no runtime, contract, auth, or i18n behaviour change. status: completed

Summary:

  • Extended context/portal-api-security-boundary-assessment.md with a dedicated account/profile mutation trace.
  • Documented that normal account entry is session-gated at page level, but account identity is typically established earlier during dashboard SSR bootstrap:
    • getSession(ctx)
    • getPortalLogin(session.user.email)
    • CRM contactid
    • getPersonalAccount(contactid)
    • Redux loggedinUserId / accountDetails
  • Recorded that the live personal-details flow reads and mutates CRM contacts using a Redux-held loggedinUserId, then passes that ID into:
    • /api/endpoint/getpersonalaccount_api?contactid=...
    • /api/endpoint/updateaccount_api?contactId=...
  • Confirmed that sampled account read/write routes do not visibly:
    • resolve current session server-side
    • resolve CRM contact from session.user.email
    • compare caller-supplied contactId/contactid to a session-derived CRM contact
  • Confirmed that the reviewed personal-details UI does not allow direct email editing because emailaddress1 is rendered disabled, but also recorded that updateaccount_api.js forwards request bodies to CRM without a visible field allowlist or email/session reconciliation step.
  • Confirmed that the live change-password UI does not use updatepassword_api.js; it writes pinswg_custom_password through updateaccount_api.js, making the standalone password route appear exposed but likely legacy/inconsistent with passwordless NextAuth.
  • Concluded that account/profile mutation is strongest at session/bootstrap derivation time but weaker at the final API route boundary, where caller-supplied contact IDs are trusted.

Validation:

  • Documentation-only assessment update; no runtime code changed.
  • Non-destructive evidence gathering performed via targeted searches and direct review of:
    • pages/account/personaldetails.js
    • pages/account/changepassword.js
    • components/account/{personaldetails,personaldetailsCheck,personaldetailsComplete,changepassword}.js
    • components/myportal/youraccount.js
    • pages/index.js
    • pages/myportal/index.js
    • actions/services/accountDirectService.js
    • pages/api/endpoint/{getpersonalaccount_api,updateaccount_api,updatepassword_api,getemailaccountcheck_api,getportallogin_api}.js
    • store/accountDetails/{action,reducer}.js
  • Additional targeted searches performed for setLoggedInUserId, setAccountDetails, updatePassword, pinswg_custom_password, and updatepassword_api usage.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next assessment pass: trace other user-owned CRM mutation routes to determine whether the caller-supplied contact-ID trust pattern repeats across watchlists, involvements, representations, and case mutation endpoints.

CL-2026-06-19-PORTAL-API-USER-OWNED-MUTATION-SURFACE-TRACE: systemic caller-ID trust across watchlists, cases, involvements, and representations

date: 2026-06-19 author: Cline scope: context/portal-api-security-boundary-assessment.md, watchlist/case/involvement/representation mutation routes, related service helpers, and completion/finalisation flows type: milestone rationale: Extend the security boundary assessment beyond account/profile routes to determine whether caller-supplied CRM identity and record-ID trust is isolated or systemic across other user-owned CRM mutation families. impact: Documentation/context only; clarifies systemic authorization-boundary posture for user-owned CRM mutations; no runtime, contract, auth, or i18n behaviour change. status: completed

Summary:

  • Extended context/portal-api-security-boundary-assessment.md with a dedicated user-owned CRM mutation route trace covering:
    • watchlist create/update and delete
    • case/appeal create, update, and patch
    • case and representation involvement creation
    • representation delete and completion/finalisation side effects
    • blob-side draft helper variants where relevant
  • Added a mutation-route inventory table recording, for each route:
    • purpose
    • primary callers/helpers
    • accepted identifiers
    • CRM entity affected
    • whether the route reads session
    • whether it resolves CRM contact from session.user.email
    • whether it trusts caller-supplied contact/record identifiers
    • whether it performs visible ownership checks before mutation
    • a classification label
  • Confirmed the account/profile pattern is not isolated:
    • contact-ID-bound caller trust appears in watchlist upsert, case creation, case involvement creation, and representation involvement creation
    • record-ID-bound mutation without visible ownership proof appears in watchlist delete, representation delete, case update, and case patch routes
  • Recorded that some flows perform duplicate/existence checks or CRM filtering using supplied IDs, but these checks do not amount to independent user-ownership verification.
  • Concluded that the current mutation authorization model is distributed and upstream-heavy:
    • session-to-contact derivation commonly happens in SSR/UI/bootstrap layers
    • service helpers propagate those identifiers
    • final mutation routes frequently trust supplied IDs directly rather than re-binding them to the authenticated session

Validation:

  • Documentation-only assessment update; no runtime code changed.
  • Non-destructive evidence gathering performed via targeted searches and direct review of:
    • pages/api/endpoint/{createwatchedcases_api,deletewatchedcases_api,createcase_api,updatecase_api,patchcase_api,deletemyrepresentations_api}.js
    • pages/api/file/{createcaseinvolvement_api,createrepinvolvement_api,createappealcompletemessage_api,createrepcompletemessage_api,createcase_api,updatecase_api}.js
    • actions/services/{portalDirectService,caseDirectService}.js
    • components/{case/summary,myportal/viewall,newappeal/createCase,newappeal/buildsection,case/representation/representationComplete}.js
    • lib/newappeal/journeyEffects.js
  • Additional targeted searches performed across pages/api, actions/services, components, and lib for mutation helpers and identifier propagation (contactid, contactId, loggedInUserId, watchedCaseID, myRepresentationsID, incidentid, mutation helper names).
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next assessment pass: trace gethash_api and signed-route consumption together to determine whether signed hash issuance materially strengthens mutation authorization boundaries or remains an integrity-only control layered on top of caller-supplied identity trust.

CL-2026-06-19-PORTAL-API-HASH-WATCHLIST-DELETE-VERTICAL-SLICE: request integrity vs referential ownership verification

date: 2026-06-19 author: Cline scope: context/portal-api-security-boundary-assessment.md, gethash_api, signing helpers, watchlist delete route, and watched-case delete UI/service flow type: milestone rationale: Perform a focused vertical-slice assessment of signed hash issuance and watchlist deletion to determine whether the hash mechanism materially strengthens the mutation boundary beyond request/identifier integrity. impact: Documentation/context only; clarifies what the signed-hash model does and does not visibly protect; no runtime, contract, auth, or i18n behaviour change. status: completed

Summary:

  • Extended context/portal-api-security-boundary-assessment.md with a dedicated vertical-slice trace for:
    • pages/api/endpoint/gethash_api.js
    • client signing helpers (relayClient, signedRequestClient)
    • watchlist delete service + UI flows
    • pages/api/endpoint/deletewatchedcases_api.js
  • Recorded the visible hash issuance model as:
    • authenticated session required to obtain a hash
    • allowlisted route-prefix check in gethash_api
    • hash generated from the full raw query path (hashAPIPath(rawQueryPath))
    • signed URL built client-side as queryUrl + hash
  • Recorded that in the watchlist delete flow, the signed query path includes watchedCaseID, so the hash visibly protects:
    • route integrity
    • query/parameter integrity
    • identifier integrity for watchedCaseID
  • Recorded that the reviewed code does not visibly show hash issuance encoding CRM ownership or object authorization state.
  • Traced watchlist deletion end-to-end and concluded that, in the reviewed flow:
    • watchedCaseID originates from previously loaded watchlist data in normal portal UI flows
    • delete route performs direct CRM delete by record ID
    • no route-local referential ownership verification was visible before delete
  • Classified the current watchlist delete design as provenance-based rather than relationship-verified.

Validation:

  • Documentation-only assessment update; no runtime code changed.
  • Non-destructive evidence gathering performed via direct review of:
    • pages/api/endpoint/gethash_api.js
    • actions/clients/relayClient.js
    • actions/clients/signedRequestClient.js
    • actions/services/portalDirectService.js
    • pages/api/endpoint/deletewatchedcases_api.js
    • components/myportal/topthree.js
    • components/myportal/viewall.js
    • components/case/summary.js
    • components/search/searchresults.js
  • Additional targeted searches performed for gethash_api, buildHashedQueryUrl, buildSignedUrl, deleteWatchedCases, watchedCaseID, and pinswg_watchlistid across actions, components, and pages/api.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next assessment pass: perform the same integrity-vs-authorization vertical slice for deletemyrepresentations_api and one blob/container mutation route to compare whether the provenance-based signed-request model is consistent across CRM-record and blob/file deletion paths.

CL-2026-06-19-PORTAL-API-DRAFT-STORAGE-OWNERSHIP-ASSESSMENT: session user ID to container boundary

date: 2026-06-19 author: Cline scope: context/portal-api-security-boundary-assessment.md, draft appeal/representation loaders, storage service helpers, Azure storage helpers, and file/blob draft APIs type: milestone rationale: Assess the draft-storage ownership boundary by tracing how session.user.id becomes container identity, how draft data/files are created/read/deleted/submitted, and whether storage operations remain container-scoped through the queue handoff to CRM creation. impact: Documentation/context only; clarifies pre-submission storage ownership architecture and the storage-to-CRM ownership transition; no runtime, contract, auth, or i18n behaviour change. status: completed

Summary:

  • Extended context/portal-api-security-boundary-assessment.md with a dedicated draft storage ownership assessment covering:
    • container identity generation
    • draft appeal and draft representation creation/resume flows
    • file upload/download/list/delete storage routes
    • draft delete flows
    • submission/finalisation queue handoff
  • Confirmed the strongest visible upstream storage ownership root is:
    • NextAuth session.user.id -> container identity
  • Recorded that SSR/page-loader flows consistently use session.user.id as the container identity for draft reads and store hydration.
  • Confirmed many file/blob APIs accept container / containerID as caller-supplied inputs and do not visibly re-derive container identity from session inside the reviewed handlers.
  • Classified the overall draft-storage model as hybrid:
    • upstream session-derived container ownership
    • downstream caller-supplied container/path values
    • hash-protected blob-path integrity
  • Identified the clearest ownership transition point as queue/finalisation:
    • storage-owned draft
    • completion route
    • queue message carrying container/storage paths
    • downstream CRM submitted-record creation

Validation:

  • Documentation-only assessment update; no runtime code changed.
  • Non-destructive evidence gathering performed via targeted searches and direct review of:
    • lib/newappeal/loadNewAppealPage.js
    • lib/myportal/loadMyPortalAppealPage.js
    • lib/representation/pageLoaders.js
    • actions/services/documentDirectService.js
    • actions/azurestorage.js
    • pages/api/file/{getprogressobjblob,getbloblist,getawaitingsubmissionfromblob,upload,uploadsinglefile,deleteblobcase,deleteblobrep,downloadblob,setupcontainer,createappealcompletemessage_api,createrepcompletemessage_api,editRepJson}.js
  • Additional searches performed across lib, pages, actions, and pages/api/file for session.user.id, containerID, getProgressFromBlob, getFilesFromBlob, getRepsFromBlob, getAwaitingSubmissionFromBlob, and queue helper names.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next assessment pass: perform a focused route-local verification pass on the highest-value storage APIs to determine whether any of them derive container ownership from session server-side elsewhere in the stack, or whether they rely entirely on upstream container provenance plus hash-protected path integrity.

CL-2026-06-19-PORTAL-API-AUTHORIZATION-ARCHITECTURE-MODEL: consolidated authorization roots, patterns, integrity controls, and risk framing

date: 2026-06-19 author: Cline scope: context/portal-api-security-boundary-assessment.md, memory-bank/change-log.md type: milestone rationale: Consolidate the completed portal API assessment passes into one PEDW authorization architecture model that clearly distinguishes authorization roots, ownership patterns, integrity controls, proven findings, and non-proven findings without widening scope into a new endpoint review. impact: Documentation/context only; improves architectural clarity, auditability, and future review discipline; no runtime, contract, auth, storage, relay, or i18n behaviour change. status: completed

Summary:

  • Added a dedicated synthesis section to context/portal-api-security-boundary-assessment.md titled PEDW authorization architecture model.
  • Consolidated the completed investigations into four visible authorization roots:
    • anonymous public
    • NextAuth session
    • CRM Contact (session.user.email -> getPortalLogin(email) -> CRM Contact)
    • storage container (session.user.id -> user-specific Azure Storage container)
  • Added a pattern catalogue describing the main visible ownership / authorization models:
    • public anonymous
    • CRM contact scoped
    • CRM relationship scoped
    • record-ID provenance-based
    • storage container scoped
  • Added an integrity controls section clarifying that the signed hash most clearly protects route/query/path integrity and identifier integrity, and should be understood as an integrity control rather than object-authorization control.
  • Recorded the server-mediated execution model for storage and relay paths:
    • PEDW API -> Azure SDK -> Azure Storage
    • PEDW API -> signed path hash -> Azure Relay -> CRM
  • Added explicit synthesis of what has been proven:
    • distributed authorization model
    • route-local authorization not consistently visible
    • ownership usually established upstream and identifiers then propagated
    • signed hash strengthens integrity rather than object ownership proof
    • storage is server-mediated rather than direct browser-to-storage
  • Added explicit synthesis of what has not been proven:
    • no confirmed exploitability
    • no demonstrated User A -> User B mutation
    • no evidence invalidating existing health-check / OWASP / pentest posture
    • no evidence of direct browser-to-CRM or direct browser-to-storage exposure
  • Added high-level risk framing as:
    • architectural integrity / auditability risk
    • maintainability risk
    • future-change risk
    • not currently a confirmed exploitable vulnerability

Validation:

  • Documentation-only synthesis update; no runtime code changed.
  • Consolidation based on previously completed evidence in the assessment document covering:
    • endpoint inventory and risk classification
    • ownership-enforcement trace
    • pinsUser lifecycle and identity comparison
    • registration/bootstrap trace
    • account/profile mutation trace
    • user-owned CRM mutation trace
    • signed-hash watchlist delete vertical slice
    • draft storage ownership assessment
  • No new endpoint review performed for this synthesis step.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Next step: use this authorization architecture model as the baseline for future assessment/change review, and only if implementation work is later explicitly approved, consider narrow helper/guard patterns that re-bind selected sensitive CRM mutation routes to session-derived CRM contact and selected storage mutation routes to session-derived container identity while preserving existing behaviour and successful test posture.

CL-2026-06-19-PORTAL-API-SECURITY-STREAM-CLOSURE: formal close-out of authorization architecture assessment

date: 2026-06-19 author: Cline scope: context/portal-api-security-boundary-assessment.md, context/architecture.md, memory-bank/change-log.md type: milestone rationale: Formally close the Portal API Security & Access Boundary Assessment stream and record the resulting stable architectural conclusions, programme status, and future framing in core project documentation. impact: Documentation/context only; clarifies that the stream is complete and records the final authorization architecture model for future review work; no runtime, contract, auth, relay, storage, or i18n behaviour change. status: completed

Summary:

  • Updated context/portal-api-security-boundary-assessment.md with a dedicated Programme status section stating:
    • Portal API Security & Access Boundary Assessment
    • Status: COMPLETE
  • Recorded stable stream conclusions in the assessment document:
    • PEDW uses a distributed authorization model
    • authorization is generally established upstream and propagated through later flows
    • integrity controls are meaningful but distinct from route-local object-authorization proof
    • no confirmed exploitability has been demonstrated by the assessment
  • Updated context/architecture.md to reflect that the portal authorization assessment stream is complete and to record the consolidated authorization architecture model as part of the architecture reference.
  • Added architecture-level conclusions covering:
    • principal authorization roots (public anonymous, CRM-owned, draft/storage-owned)
    • signed hash as an integrity control rather than object-authorization control
    • server-mediated storage execution via PEDW API and Azure SDK
    • relay path validation model via PEDW API and Azure Relay
    • proven vs not-proven findings
    • risk characterisation as architectural integrity/auditability/maintainability/future-change risk
  • Recorded that no immediate remediation programme is recommended and that any future work should be framed as authorization hardening, consistency improvement, and maintainability improvement rather than emergency remediation.

Validation:

  • Documentation-only closure update; no runtime code changed.
  • Reviewed existing assessment synthesis in context/portal-api-security-boundary-assessment.md before adding stream close-out language.
  • Reviewed context/architecture.md and aligned the architecture reference with the now-complete authorization assessment stream.
  • Reviewed memory-bank/open-questions.md; no update made because no genuinely new unresolved architecture/security question emerged from closure work.
  • Lint/tests not run because this was documentation-only work.

Follow-ups:

  • Portal authorization architecture stream should now be considered closed.
  • Recommended next architecture stream: authorization hardening / consistency planning, using the completed authorization architecture model as the baseline and only proceeding to implementation work by explicit approval.

CL-2026-06-19-PORTAL-API-REGISTRATION-TRACE: session-to-CRM-contact registration and dashboard bootstrap

date: 2026-06-19 author: Cline scope: context/portal-api-security-boundary-assessment.md, registration pages/components, account service helpers, createaccount_api, homepage signed-in bootstrap type: milestone rationale: Record how the portal transitions an authenticated NextAuth user with no CRM contact into a CRM-contact-backed portal user, and identify where dashboard access becomes valid. impact: Documentation/context only; improves architectural clarity around registration, identity binding, and dashboard authorization bootstrap; no runtime, contract, auth, or i18n behaviour change. status: completed

Summary:

  • Extended the assessment with a dedicated registration and post-registration bootstrap trace.
  • Identified the primary registration entry condition in pages/index.js:
    • signed-in session exists
    • getPortalLogin(session.user.email) returns no CRM contact
    • user is redirected to /account/register
  • Traced the registration flow across:
    • pages/account/register.js
    • components/account/registerform.js
    • components/account/registerCheck.js
    • components/account/registerComplete.js
    • actions/services/accountDirectService.createAccount
    • pages/api/endpoint/createaccount_api.js
  • Determined that registration uses the authenticated session email as the intended identity source:
    • loggedInUserEmail comes from getServerSideProps
    • form emailaddress1 is prefilled from session email
    • form email field is disabled in the reviewed UI
  • Determined that CRM contact creation is performed by forwarding the form body directly to CRM contacts via createaccount_api, with pinswg_typeofinvolvement set client-side in the registration completion flow.
  • Determined that dashboard access is not visibly granted immediately by registration completion itself.
  • Instead, dashboard access becomes valid after a subsequent signed-in bootstrap pass on / re-runs getPortalLogin(session.user.email), finds the newly created contact, sets pinsUser, and redirects to /myportal.

Validation:

  • Documentation-only assessment update; no runtime code changed.
  • Non-destructive evidence gathering performed via targeted searches and direct review of:
    • pages/index.js
    • pages/account/register.js
    • components/account/registerform.js
    • components/account/registerCheck.js
    • components/account/registerComplete.js
    • actions/services/accountDirectService.js
    • pages/api/endpoint/createaccount_api.js
    • pages/api/endpoint/getemailaccountcheck_api.js
    • pages/api/endpoint/getportallogin_api.js
  • No lint/tests run because this was documentation-only work.

Follow-ups:

  • Next assessment pass: trace account/profile mutation flows after registration to determine whether CRM-contact-backed users mutate account data through session-derived identity, pinsUser, or caller-supplied contact IDs.

CL-2026-06-18-CRM-CASE-PROGRESS-CONTEXT: record CRM Case Progress Display investigation conclusions

date: 2026-06-18 author: Cline scope: context/current-platform-state.md, memory-bank/{techContext,productContext,change-log}.md type: change rationale: Preserve the completed CRM Case Progress Display investigation conclusions in project context so future contributors do not re-open the same status/lifecycle ambiguity without need. impact: Documentation/context only; improves architectural clarity around case status display ownership and inputs; no runtime, auth, i18n, API, or behaviour change. status: completed

Summary:

  • Updated project context to record that the Case Status tab is a portal-owned journey interpretation layer.
  • Recorded the active input model as:
    • CRM statuscode
    • appeal type
    • specialist process (Rights of Way only)
  • Recorded that pinswg_casestage is not a significant active input to current status-tab lifecycle resolution.
  • Recorded that stage catalogues are best understood as:
    • user-facing journey models
    • appeal-type interpretation models
    • presentation-layer progress visualization
  • Recorded that shared catalogue families (PLANNING_S78, ENFORCEMENT, DNS/SIP, Rights of Way specialist-process variants) reflect a mixture of shared journey structures, presentation reuse, and historical consolidation.
  • Recorded the architectural conclusion that this area is presentation architecture / journey interpretation, not a new business-policy domain boundary candidate.

Validation:

  • Documentation/context update only.
  • No application code changed.
  • No tests changed.
  • Lint/tests skipped because this was documentation-only work.

Follow-ups:

  • Reopen only if a future business-validation or behaviour-change initiative requires deeper lifecycle/status decision work.

CL-2026-06-18-RPA-DOCS: representation participation availability documentation + context update

date: 2026-06-18 author: Cline scope: docs/representation-participation-availability.md, docs/representation-participation-business-review.md, context/current-platform-state.md, memory-bank/{techContext,productContext,open-questions,change-log}.md type: change rationale: Preserve the completed Representation Participation Availability investigation in repository documentation and project context so future contributors do not need to repeat the same discovery work. impact: Documentation/context only; improves architectural clarity and business-validation readiness; no runtime, auth, i18n, or API behaviour change. status: completed

Summary:

  • Added first-class technical documentation for Representation Participation Availability.
  • Added a plain-English business review document for product/business/SME validation.
  • Updated platform and memory-bank context to record:
    • the stream as complete from a technical discovery/documentation perspective
    • the architectural conclusion that representation participation is one coherent decision model with layered exceptions
    • the current state as awaiting business validation before further representation-policy adoption/expansion/change work
    • the next likely discovery candidate: Case Lifecycle / Status Decision Model Discovery
  • Added a concise open-questions entry pointing business stakeholders to the new review/technical documentation.

Validation:

  • Documentation/context update only.
  • No application code changed.
  • No tests changed.
  • Lint/tests skipped because this was documentation-only work.

Follow-ups:

  • Review docs/representation-participation-business-review.md with business stakeholders.
  • Do not broaden representation-policy boundaries further until validation outcomes are agreed.

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-22541-V: breadcrumbs larger slice — callback route-group extraction (/myportal/case/id/[incident], /case)

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 extraction slice by moving callback-bearing breadcrumb branches into a dedicated callback renderer map while preserving router.back() behavior through explicit callback injection. impact: Structural refactor with preserved callback semantics; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added createCallbackRouteRenderers(...) in lib/routing/breadcrumbRendererFactories.js to map:
    • /myportal/case/id/[incident]
    • /case
  • Introduced explicit callback injection (onBack) into grouped factory composition and route renderers, preserving router.back() behavior via injected callback.
  • Extended createMappedRouteRendererGroups(...) return with callbackRouteRenderersByPath.
  • Updated map builder in lib/routing/breadcrumbRouteMaps.js to include callback map in explicit precedence order:
    • simple -> link-text -> myportal -> new-appeal -> callback -> case detail -> detail/account.
  • Updated components/breadcrumbs.js:
    • pass onBack: () => { router.back(); } into grouped factory composition
    • include callback route map in buildBreadcrumbRendererMaps(...)
    • remove now-redundant inline isPath("/myportal/case/id/[incident]") and isPath("/case") branches.
  • Expanded tests:
    • tests/phase22/breadcrumb-route-maps-helper.test.cjs
      • update grouped map order assertions to seven maps including callback group.
    • tests/phase22/breadcrumbs-route-map-structure.test.cjs
      • assert callback factory export and mapped callback route presence
      • assert callback map inclusion in component grouped destructuring and map-order invariant
      • assert inline callback branches are removed while router.back(); remains preserved.

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 smaller slice candidate: add a focused helper test for empty-string/whitespace path lookups to assert strict null behavior for non-exact keys.
  • Next larger slice candidate: evaluate whether /newappeal/[appealtypes] and /myportal/[appealtypes] back-link branches can be extracted with explicit callback/setter injection while preserving state-step semantics.

CL-22541-W: breadcrumbs larger slice — step-back route-group extraction (/newappeal/[appealtypes], /myportal/[appealtypes])

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 extraction slice by moving step-based back-link branches into a dedicated mapped route group with explicit setter callback injection, preserving section-navigation semantics. impact: Structural refactor with preserved back-link and step-state behavior; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added createStepBackRouteRenderers(...) in lib/routing/breadcrumbRendererFactories.js to map:
    • /newappeal/[appealtypes]
    • /myportal/[appealtypes]
  • Preserved existing step semantics in mapped handlers:
    • no crumb when currentSection <= 1
    • service-name crumb when currentSection === 9999
    • back-link crumb with existing class parity (govuk-breadcrumbs__link-item for new-appeal flow)
  • Introduced explicit injected handlers (onStepBack, onStepBackWithInlineClass) and injected crumb renderer dependency (renderBackCrumb) into grouped factory composition.
  • Extended map builder in lib/routing/breadcrumbRouteMaps.js with stepBackRouteRenderersByPath and updated precedence order:
    • simple -> link-text -> myportal -> new-appeal -> callback -> step-back -> case detail -> detail/account.
  • Updated components/breadcrumbs.js:
    • pass currentSection and step-back callbacks into createMappedRouteRendererGroups(...)
    • include step-back map in buildBreadcrumbRendererMaps(...)
    • remove inline isPath("/newappeal/[appealtypes]") and isPath("/myportal/[appealtypes]") branches.
  • Expanded tests:
    • tests/phase22/breadcrumb-route-maps-helper.test.cjs
      • updated grouped map count/order to include step-back map (8 total)
    • tests/phase22/breadcrumbs-route-map-structure.test.cjs
      • assert step-back factory export + mapped route presence
      • assert grouped destructuring includes step-back map
      • assert inline step-back branches are removed
      • assert updated map-order invariant includes step-back group.

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 smaller slice candidate: add focused helper test for strict null behavior on empty-string/whitespace/non-exact path keys.
  • Next larger slice candidate: evaluate extractability of /myportal/representation back-link states via explicit callback injection, only if questionnaire/submit side-effects remain parity-safe and readable.

CL-22541-X: breadcrumbs smaller slice — strict null lookup guard coverage (empty/whitespace/non-exact paths)

date: 2026-04-09 author: Cline scope: tests/phase22/breadcrumb-route-maps-helper.test.cjs type: change rationale: Execute the queued smaller follow-up slice by hardening helper regression coverage for strict exact-match route lookup semantics. impact: Test-only hardening; no runtime route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added focused helper-level test case in tests/phase22/breadcrumb-route-maps-helper.test.cjs:
    • resolveMappedRouteRenderer returns null for empty, whitespace, and non-exact path variants
  • New assertions verify resolver returns null for:
    • empty path ("")
    • whitespace-only path (" ")
    • trailing/leading whitespace variants ("/known ", " /known")
    • case-variant non-exact key ("/KNOWN")
  • Confirms route-map resolver remains strict/exact and does not auto-trim or case-normalize lookup keys.

Validation:

  • npx eslint 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 6/6).

Follow-ups:

  • Next larger slice candidate remains /myportal/representation callback/state branch extraction only if side-effects can be injected/parity-preserved cleanly.

CL-22541-Y: breadcrumbs larger slice — representation route-group extraction (/myportal/representation)

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 queued larger follow-up slice by extracting /myportal/representation callback/state breadcrumb branches into a dedicated mapped route group with explicit callback injection while preserving questionnaire/submit side-effects. impact: Structural refactor with preserved representation back-link state behavior; no intended route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added createRepresentationRouteRenderers(...) in lib/routing/breadcrumbRendererFactories.js mapping:
    • /myportal/representation
  • Preserved existing representation-state semantics in mapped handler:
    • submit/back from confirmation (setRepresentationSubmit(qcount))
    • non-questionnaire back path (setRepresentationSubmit(false))
    • questionnaire step-back path (setShowQuestionnaireSection(showQuestionnaireSection - 1) + setRepresentationSubmit(false))
  • Introduced explicit injected state/callback dependencies into grouped composition:
    • currentView, hasRepDetails, repDetails, showQuestionnaireSection
    • onRepresentationSubmitBackFromConfirmation
    • onRepresentationSubmitBackToDetails
    • onRepresentationQuestionnaireBack
  • Extended buildBreadcrumbRendererMaps(...) in lib/routing/breadcrumbRouteMaps.js with representationRouteRenderersByPath and updated precedence order:
    • simple -> link-text -> myportal -> new-appeal -> callback -> step-back -> representation -> case detail -> detail/account.
  • Updated components/breadcrumbs.js:
    • pass representation state/callback injections into createMappedRouteRendererGroups(...)
    • include representationRouteRenderersByPath in map builder call
    • remove inline isPath("/myportal/representation") branch block.
  • Expanded tests:
    • tests/phase22/breadcrumb-route-maps-helper.test.cjs
      • updated map-count/order assertions to include representation group (9 total)
      • updated map build fixtures to include representationRouteRenderersByPath
    • tests/phase22/breadcrumbs-route-map-structure.test.cjs
      • assert representation factory export + mapped route presence
      • assert grouped destructuring includes representation map
      • assert inline representation branch removal
      • assert updated map-order invariant includes representation group.

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 smaller slice candidate: helper-level assertion for missing/undefined representation dependency values yielding no renderer output regressions.
  • Next larger slice candidate: evaluate whether remaining /newappeal edge branch can be absorbed into mapped grouping without reducing readability or explicitness.

CL-22541-Z: breadcrumbs closure slice 1 — mapped-route precedence/callback invariants decision

date: 2026-04-09 author: Cline scope: memory-bank/decisions.md type: change rationale: Execute closure slice 1 by documenting explicit architectural invariants for breadcrumb mapped-route precedence and callback injection contracts so future refactors remain safe and auditable. impact: Documentation/governance hardening only; no runtime route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added D-006 to memory-bank/decisions.md.
  • Captured accepted invariant contracts for:
    • explicit breadcrumb map precedence ordering (9 groups)
    • callback/state behavior dependency injection requirement (no ad-hoc inline reintroduction)
    • structure test guardrails as contract checks for intentional future changes.

Validation:

  • Documentation consistency review against current breadcrumb map structure and phase22 structure test expectations.

Follow-ups:

  • Closure slice 2: add focused negative-path representation map/dependency tolerance test coverage.
  • Closure slice 3: resolve and guard remaining /newappeal inline edge branch treatment.

CL-22541-Z2: breadcrumbs closure slice 2 — missing representation map negative-path guard

date: 2026-04-09 author: Cline scope: tests/phase22/breadcrumb-route-maps-helper.test.cjs type: change rationale: Execute closure slice 2 by adding focused negative-path coverage ensuring resolver behavior remains stable when representation route-map dependency is missing from grouped map input. impact: Test hardening only; no runtime route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Added focused helper-level test:
    • resolveMappedRouteRenderer tolerates missing representation map entry in grouped map input
  • New assertions verify:
    • missing representationRouteRenderersByPath resolves /myportal/representation to null
    • later maps still resolve correctly (case detail fallback remains intact).

Validation:

  • node tests/phase22/breadcrumb-route-maps-helper.test.cjs -> pass (7/7).
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Closure slice 3: resolve and guard remaining /newappeal inline edge branch treatment.

CL-22541-Z3: breadcrumbs closure slice 3 — remove lingering /newappeal no-op inline branch

date: 2026-04-09 author: Cline scope: components/breadcrumbs.js, tests/phase22/breadcrumbs-route-map-structure.test.cjs type: change rationale: Execute closure slice 3 by resolving the remaining inline /newappeal edge branch (a no-op render guard) and locking its removal with explicit structure coverage. impact: Refactor/test hardening only; no runtime route/auth/session/API/EN-CY/a11y behavior change. status: completed

Summary:

  • Removed lingering no-op branch from breadcrumbs JSX:
    • deleted {isPath("/newappeal") && <></>}
  • Added structure guard assertion that /newappeal inline branch is absent.

Validation:

  • node tests/phase22/breadcrumbs-route-map-structure.test.cjs -> pass (4/4).
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Breadcrumb route-state extraction closure slices (1/2/3) complete.

CL-22541-Z4: breadcrumbs post-closure hotfix — callable label resolver parity (static + view-all)

date: 2026-04-09 author: Cline scope: components/breadcrumbs.js, tests/phase22/breadcrumbs-route-map-structure.test.cjs type: change rationale: Fix post-refactor regression where some breadcrumbs rendered blank labels because mapped label entries were returned as function references instead of resolved values. impact: Restores user-visible breadcrumb labels for static and view-all keyed routes; no auth/session/API/security behavior change. status: completed

Summary:

  • Fixed static text breadcrumb label resolver to invoke mapped label functions:
    • getStaticTextCrumbLabel(path) now resolves callable entries (resolver ? resolver() : null).
  • Fixed view-all label resolver parity to support both function-backed and string-backed map entries:
    • getViewAllLabel(viewKey) now invokes function entries and returns string entries directly.
  • Added structure guard assertions to prevent regression of callable label resolution behavior.

Validation:

  • node tests/phase22/breadcrumbs-route-map-structure.test.cjs -> pass (4/4).
  • node tests/phase22/index.test.cjs -> pass (combined suite).

Follow-ups:

  • Breadcrumb label resolver parity now aligned across mapped/static/view-all label paths.

CL-22541-Z5: architecture comparison overview refresh (senior architecture review)

date: 2026-04-09 author: Cline scope: context/architecture-overview-2026-04-09.md type: change rationale: User requested a fresh senior architecture review and comparison baseline covering scalability, maintainability, coupling, boundary quality, operational/deployment risk, and prioritized technical debt actions. impact: Documentation-only architecture guidance update; no runtime/auth/session/security/API behavior change. status: completed

Summary:

  • Added new architecture comparison baseline document:
    • context/architecture-overview-2026-04-09.md
  • Included requested structure:
    1. current architecture summary
    2. strengths
    3. risks (scalability, maintainability, coupling, weak boundaries, operational/deployment)
    4. prioritized recommendations
    5. low-risk next improvements
  • Added explicit comparison notes vs existing context/architecture.md and identified strategic focus areas for the next modernization wave.

Validation:

  • Documentation coherence review against current repo architecture docs (context/architecture.md, context/integration-map.md, context/project-overview.md).

Follow-ups:

  • Optionally elevate this architecture overview into a periodic architecture scorecard cadence and add measurable KPIs in memory-bank.

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

date: 2026-04-07 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.