448 KiB
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.mdcontext/maintainability-adoption-programme.mdcontext/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
- missing CRM display value -> return
- 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(...)
- use
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.jsand left ambiguous fallback cases unchanged - updated
memory-bank/change-log.md
- edited only
- 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")
- case type ->
- 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).valuein theisMyRepresentationsbranch because removing it could alter non-CRM derived display behaviour
- authority fallback using
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.jsfallbacks 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 activejsonpath(...)call were edited
- Stale import removed from:
components/case/summaryTypes/pinswg_advertsid.jscomponents/case/summaryTypes/pinswg_communityinfrastructurelevyid.jscomponents/case/summaryTypes/pinswg_environmentalpermittingid.jscomponents/case/summaryTypes/pinswg_lawfuldevelopmentcertificatappid.jscomponents/case/summaryTypes/pinswg_maintenanceoflands217id.jscomponents/case/summaryTypes/pinswg_miscellaneouscasewordid.jscomponents/case/summaryTypes/pinswg_planningobligationappeals106id.jscomponents/case/summaryTypes/pinswg_wayleaveid.jscomponents/case/summaryTypes/pinswg_compulsorypurchaseordersid.js
- Intentionally left unchanged because they had no stale
JSONPathimport in the narrowed list:pinswg_callinss77id.jspinswg_commonlandid.jspinswg_enforcementlistedbuildingconseid.jspinswg_enforcementnoticeappeals174id.jspinswg_householderappealhasid.jspinswg_ldpid.jspinswg_listedbuildingandconservationrid.jspinswg_nonvalidationid.jspinswg_planningconditionss73s79id.jspinswg_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
- no active
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
- if no CRM display value exists, return
- 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.jscomponents/myportal/viewall.jscomponents/case/summaryTypes/pinswg_nonvalidationid.jscomponents/case/summaryTypes/pinswg_ldpid.jscomponents/case/summaryTypes/pinswg_householderappealhasid.jscomponents/case/summaryTypes/pinswg_callinss77id.jscomponents/case/summaryTypes/pinswg_environmentalpermittingid.jscomponents/case/summaryTypes/pinswg_dnsid.jscomponents/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.cjsnpx 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.jscomponents/case/summaryTypes/pinswg_lawfuldevelopmentcertificatappid.jscomponents/case/summaryTypes/pinswg_communityinfrastructurelevyid.jscomponents/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.jsonand returnedvalue_cy
- the same fields used direct JSONPath lookup against
- 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")
- case type ->
- 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
- LPA display via
- English behaviour:
- All four files were structurally equivalent and safe, so none needed to be skipped.
Validation:
node tests/phase22/crm-display-resolver.test.cjsnpx 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.jscomponents/case/summaryTypes/pinswg_miscellaneouscasewordid.jscomponents/case/summaryTypes/pinswg_wayleaveid.jscomponents/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.jsonand returnedvalue_cy
- these branches used direct JSONPath lookup into
- 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")
- case type ->
- 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_lpakeyed translation branch
- relevant-authority lookup via
pinswg_hedgeshedgerowstreepreservatioid.js- specialist case process translation branch
- relevant-authority lookup via
pinswg_lpakeyed translation branch
- LPA rendering via
getCaseLpaDisplayLines(...) - linked-case rendering and status
- date sections and field-specific source variations
- Shared compatible CRM formatted/display-value branches confirmed in all four files:
- Safe-adoption exclusions observed:
- no keyed lookup translation branches changed
- no
pinswg_lpalookup 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.cjsnpx 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.jscomponents/case/summaryTypes/pinswg_enforcementlistedbuildingconseid.jscomponents/case/summaryTypes/pinswg_listedbuildingandconservationrid.jscomponents/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
jsonpathlookup intolookuptranslations.jsonand returnedvalue_cy
- each compatible row used direct
- 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")
- case type ->
- 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
- LPA rendering via
- Compatible CRM formatted/display-value branches matched exactly:
- Safe-adoption exclusions observed:
- no keyed lookup translations changed
- no
pinswg_lpalookup 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.cjsnpx 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
- standard CRM formatted/display value -> Welsh lookup (
- Adopted consumers completed in this stream:
components/admin/tabs/appeals.jscomponents/search/searchresults.jscomponents/search/addresssearchresults.jscomponents/search/dnssearchresults.jscomponents/myportal/viewall.jscomponents/case/summaryTypes/pinswg_nonvalidationid.jscomponents/case/summaryTypes/pinswg_ldpid.jscomponents/case/summaryTypes/pinswg_householderappealhasid.jscomponents/case/summaryTypes/pinswg_callinss77id.jscomponents/case/summaryTypes/pinswg_environmentalpermittingid.jscomponents/case/summaryTypes/pinswg_dnsid.jscomponents/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.localremains 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.
- begin a new clearly scoped maintainability stream outside this closeout, likely one of:
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
- case type renders
- Welsh behaviour:
- safe adopted CRM branches used JSONPath against
lookuptranslations.jsonand returnedvalue_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
- safe adopted CRM branches used JSONPath against
- 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
- safe subset matched:
- 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
- case type -> original display value or
- 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.FormattedValuepinswg_choiceofprocedure@OData.Community.Display.V1.FormattedValuepinswg_essubmitted@OData.Community.Display.V1.FormattedValuepinswg_deemedmarinelicenseappliedfor@OData.Community.Display.V1.FormattedValuepinswg_decision@OData.Community.Display.V1.FormattedValue
- unadopted branches / mixed concerns:
projectType.pinswg_namelist translationpinswg_webaddresspinswg_redetermination@OData.Community.Display.V1.FormattedValue
- adopted branches:
- 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
- per-project-type item translation through
- 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
- existing
- 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
- English behaviour:
- 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
- project-type list translation
- Consumer-specific nuances preserved:
- procedure retains
to-be-confirmedfallback when source branch is null - ES Submitted retains
to-be-confirmedfallback when source branch is null - Deemed Marine License Applied for retains
to-be-confirmedfallback when source branch is null - decision retains
no-information-to-datefallback 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
- procedure retains
- 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.cjsnpx 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_apiand 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"]
- case type renders
- Welsh behaviour:
- the adopted CRM display branches used JSONPath against
lookuptranslations.jsonand returnedvalue_cy - website row also used JSONPath in Welsh, but it is not a standard CRM formatted/display-value branch and remains unchanged
- the adopted CRM display branches used JSONPath against
- duplicated JSONPath lookup pattern:
- safe subset matched the established pattern:
$..[?(@ && @.value=="<CRM formatted value>")].value_cy
- one branch (
EIA required) used the shorter historicaljsonpath(path, transLookup)form but still matched the same translation concern
- safe subset matched the established 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
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
- case type -> original display value or
- 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.FormattedValuepinswg_eiarequired@OData.Community.Display.V1.FormattedValuepinswg_choiceofprocedure@OData.Community.Display.V1.FormattedValuepinswg_typeofevent@OData.Community.Display.V1.FormattedValuepinswg_decision@OData.Community.Display.V1.FormattedValue
- unadopted mixed concerns:
pinswg_webaddress- project description/map inputs
- adopted branches:
- DNS-specific derived values:
- project description section uses
getBilingualText(...) - map section derives coordinates through
OSPointandMapComponent - these were intentionally left unchanged
- project description section uses
- 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
- existing
- map/project-specific sections:
- project description section unchanged
- website row unchanged
- map rendering unchanged
- option-set / yes-no translations:
EIA requiredremains 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
- English behaviour:
- 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
- website row
- Consumer-specific nuances preserved:
- procedure keeps
to-be-confirmedfallback when source branch is null - type of event keeps empty-string fallback
- decision keeps
no-information-to-datefallback when source branch is null - website link behaviour remains unchanged
- project/map rendering remains unchanged
- procedure keeps
- 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.cjsnpx 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_apiand 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
- case type renders
- Welsh behaviour:
- the six standard CRM display fields used JSONPath against
lookuptranslations.jsonand returnedvalue_cy - relevant authority used a different lookup shape through
pinswg_lpa -> value_cy
- the six standard CRM display fields used JSONPath against
- duplicated JSONPath lookup pattern:
- safe subset matched the standard pattern:
$..[?(@ && @.value=="<CRM formatted value>")].value_cy
- one branch (
EIA required) used the shorter historicaljsonpath(path, transLookup)form but still matched the same translation concern - one branch (
relevant authority) did not match the helper abstraction because it resolves bypinswg_lpakey rather than by CRM formatted/displayvalue
- safe subset matched the standard 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") - relevant authority -> existing
pinswg_lpaJSONPath lookup result ort("case:summary-no-date-entered-label")
- case type -> original display value or
- 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.FormattedValuepinswg_eiarequired@OData.Community.Display.V1.FormattedValuepinswg_procedure@OData.Community.Display.V1.FormattedValuepinswg_typeofevent@OData.Community.Display.V1.FormattedValuestatuscode@OData.Community.Display.V1.FormattedValuepinswg_decision@OData.Community.Display.V1.FormattedValue
- unadopted branch:
detailsObj.pinswg_relevantauthoritynamematched against translation entries usingpinswg_lpa
- adopted branches:
- linked-case behaviour:
- existing
numberofchildincidents/linkedCasesList(...)rendering remains unchanged
- existing
- 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 authoritylookup branch was intentionally preserved unchanged
- derived display values:
- none for the adopted CRM display branches
- option-set/yes-no translations:
EIA requiredremains 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_cyhelper abstraction
- English behaviour:
- 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
- relevant authority
- Consumer-specific nuances preserved:
EIA requiredretainsto-be-confirmedfallbackprocedureretains empty-string fallbacktype of eventretains empty-string fallbackrelevant authorityretains bespokepinswg_lpalookup 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.cjsnpx 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_apiand 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.jsbecause 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"]
- case type renders
- Welsh behaviour:
- each of the above fields used JSONPath against
lookuptranslations.jsonand returnedvalue_cy
- each of the above fields used JSONPath against
- duplicated JSONPath lookup pattern:
$..[?(@ && @.value=="<CRM formatted value>")].value_cy- one branch (
EIA required) used the shorter historicaljsonpath(path, transLookup)form but still followed the same value ->value_cytranslation 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")
- case type -> original display value or
- 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.FormattedValuepinswg_eiarequired@OData.Community.Display.V1.FormattedValuepinswg_procedure@OData.Community.Display.V1.FormattedValuepinswg_typeofevent@OData.Community.Display.V1.FormattedValuestatuscode@OData.Community.Display.V1.FormattedValuepinswg_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
- existing
- 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
- English behaviour:
- 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
- LPA rendering via
- Consumer-specific nuances preserved:
EIA requiredretainsto-be-confirmedfallbackprocedureretains empty-string fallbacktype of eventretains 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.cjsnpx 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_apiand 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"]
- case type renders
- Welsh behaviour:
- each of the above fields used JSONPath against
lookuptranslations.jsonand returnedvalue_cy
- each of the above fields used JSONPath against
- duplicated JSONPath lookup pattern:
$..[?(@ && @.value=="<CRM formatted value>")].value_cy- one branch (
EIA required) used the shorter historicaljsonpath(path, transLookup)form but still followed the same value ->value_cytranslation 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")
- case type -> original display value or
- 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.FormattedValuepinswg_eiarequired@OData.Community.Display.V1.FormattedValuepinswg_procedure@OData.Community.Display.V1.FormattedValuestatuscode@OData.Community.Display.V1.FormattedValuepinswg_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
- existing
- additional CRM lookup branches:
- EIA-required option-set translation branch was present and was compatible with the existing helper abstraction, so it was adopted
- English behaviour:
- 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
- LPA rendering via
- Consumer-specific nuances preserved:
EIA requiredretainsto-be-confirmedfallback rather thanno-date-enteredprocedureretains 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.cjsnpx 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_apiand 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.jscomponents/case/summaryTypes/pinswg_callinss77id.jscomponents/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.jswas the safest medium-risk candidate because it has fewer duplicated CRM lookup branches than the other two reviewed files- unlike
pinswg_householderappealhasid.jsandpinswg_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"]
- case type renders
- Welsh behaviour:
- each of the above fields used JSONPath against
lookuptranslations.jsonand returnedvalue_cy
- each of the above fields used JSONPath against
- 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")
- case type -> original display value or
- 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.FormattedValuepinswg_procedure@OData.Community.Display.V1.FormattedValuepinswg_typeofevent@OData.Community.Display.V1.FormattedValuestatuscode@OData.Community.Display.V1.FormattedValuepinswg_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
- existing
- English behaviour:
- 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
- LPA rendering remains via
- 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.cjsnpx 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_apiand 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.jscomponents/case/summaryTypes/pinswg_callinss77id.jscomponents/case/summaryTypes/pinswg_ldpid.js
- High Risk:
components/case/summaryTypes/pinswg_dnsid.jscomponents/case/summaryTypes/pinswg_sipscase.jscomponents/case/summaryTypes/pinswg_environmentalpermittingid.js
- Low Risk:
- Selection rationale:
pinswg_nonvalidationid.jsis 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"]
- case type renders
- Welsh behaviour:
- each of the above fields used JSONPath against
lookuptranslations.jsonand renderedvalue_cy
- each of the above fields used JSONPath against
- 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")
- case type -> original display value or
- 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.FormattedValuestatuscode@OData.Community.Display.V1.FormattedValuepinswg_decisionissued@OData.Community.Display.V1.FormattedValue
- summary-specific derived values:
- none for the adopted CRM display fields
- English behaviour:
- 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
- LPA rendering remains delegated to
- 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.cjsnpx 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_apiand 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:
awaitingSubmissionDetailswatchedCasesmyCasesmyRepresentations
- 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.jsonand rendersvalue_cyfor 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 myRepresentationspreserves several blank-string fallbacks instead of globalN/A
- many worklists use original display value or
- 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
awaitingSubmissionDetailsstatus usesstatuscodelookup by status-code key rather than the common formatted-value lookup pattern, so that branch was intentionally left unchangedmyRepresentationsstatus 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.cjsnpx 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_apiwhile 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.jsonand rendervalue_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
DNSorSIP
- fallback behaviour:
- where source field exists, English uses original display value or
N/A - Welsh falls back to original display value / existing
N/Apath when translation missing - where source field is absent, rendered value remains empty string
- where source field exists, English uses original display value or
- 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.cjsnpx 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_apiwhile 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.jsonand rendersvalue_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/Apath when translation missing - where source field is absent, rendered value remains empty string
- where source field exists, English uses original display value or
- 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_appealTyperather than formattedpinswg_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.cjsnpx 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_apiwhile 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.jsonand rendersvalue_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/Apath when translation missing - when source field is absent, rendered value remains empty string
- when source field exists, English uses original display value or
- 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.cjsnpx 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_apiwhile 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.mdcontext/architecture.mdcontext/maintainability-adoption-programme.mdcontext/implementation-governance.mdGUARDRAILS.mdmemory-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.jscomponents/search/addresssearchresults.jscomponents/search/dnssearchresults.jscomponents/myportal/viewall.jscomponents/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_cypattern, 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_cyvia encapsulated JSONPath lookup - missing/null/empty values preserve provided fallback behaviour
- Adopted helper only in
components/admin/tabs/appeals.jsfor:- 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 programme’s cited completed Bilingual CRM Display Resolution Audit.
Validation:
- Focused validation intended for this slice:
npm run lintnode tests/phase22/crm-display-resolver.test.cjsnode 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.jsorcomponents/search/addresssearchresults.js, while preserving each consumer’s 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.mdcontext/maintainability-adoption-programme.mdcontext/implementation-governance.mdGUARDRAILS.mdmemory-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.mdwith a short business-capability context section without altering established architectural classifications. - Updated
context/maintainability-adoption-programme.mdso the programme purpose clearly reflects support for both statutory public information capabilities and current/future supported digital appeal submission capabilities. - Updated
context/implementation-governance.mdwith a short clarification that implementation slices should recognise the distinction between digital appeal submission capabilities and statutory public information capabilities. - Left
GUARDRAILS.mdunchanged 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.mdas 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.mdcontext/maintainability-adoption-programme.mdcontext/architecture.mdcontext/api-grouping-adoption-roadmap.mdcontext/remaining-architecture-candidates.mdmemory-bank/change-log.mdmemory-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.mdwith a short section clarifying that the maintainability programme defines what to advance, while implementation governance defines how future slices should be executed. - Updated
GUARDRAILS.mdRelated Docs with minimal references to:context/implementation-governance.mdcontext/maintainability-adoption-programme.md
- Did not update
memory-bank/open-questions.mdbecause no new unresolved governance question was created by this slice. - Did not create
memory-bank/maintainability-tracker.mdbecause 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.mdas 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.mdcontext/portal-api-platform-assessment.mdcontext/api-grouping-adoption-roadmap.mdcontext/remaining-architecture-candidates.mdmemory-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:
- domain layer adoption
- CRM display infrastructure
- navigation infrastructure
- 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.mdcontext/journey-architecture-map.mdcontext/api-route-map.mdcontext/api-grouping-workflow-boundary-assessment.mdcontext/api-grouping-adoption-roadmap.mdcontext/portal-api-platform-assessment.mdcontext/integration-map.mdmemory-bank/change-log.mdmemory-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 bycontext/api-route-map.md,context/api-grouping-workflow-boundary-assessment.md,context/architecture.md, andcontext/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.mdcontext/api-grouping-adoption-roadmap.mdcontext/api-route-map.mdcontext/journey-architecture-map.mdcontext/portal-api-platform-assessment.mdcontext/architecture.mdmemory-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.mdcontext/api-grouping-adoption-roadmap.mdcontext/api-route-map.mdcontext/journey-architecture-map.mdcontext/architecture.mdmemory-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.jsforgetMyCases/getMyLPACasesactions/services/caseDirectService.jsforgetPortalModuleDetails
- the active CRM-backed dashboard path is:
getMyCases(loggedInUser)orgetMyLPACases(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.jsandgetawaitingsubmission_api.jswere 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
- the active entry page is
- Added grouped façade routes under
pages/api/myportal/for the proven active CRM dashboard family only:get-my-cases.jsget-my-lpa-cases.jsget-portal-module-details.js
- Implemented each façade as the smallest safe compatibility wrapper:
- import the existing legacy endpoint handler
- delegate
reqandresdirectly to that existing handler
- Updated only the existing owning service call sites needed for the bounded slice:
actions/services/portalDirectService.jsgetMyCasesgetMyLPACases
actions/services/caseDirectService.jsgetPortalModuleDetails
- 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.cjsnode 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.mdcontext/api-grouping-adoption-roadmap.mdcontext/api-route-map.mdcontext/journey-architecture-map.mdcontext/architecture.mdmemory-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].jsandpages/myportal/case/[ticketnumber].jsstill bootstrap primarily through the Public Search family usinggetBasicSearch(...)andgetSearchDetails(...)pages/case/id/[incident].jsandpages/myportal/case/id/[incident].jsactively use the case-specific incident-id read route throughgetIncidentbyID(...)getCaseMessage(...)is actively used in case-page loading for banner/message contentgetLinkedCases(...)is actively used insidecomponents/case/summary.jsas a case-details read concerngetSIPSEvents(...)andgetSIPSMedia(...)are active case-page enrichments for relevant SIPS casescomponents/case/documents.jsand document metadata routes remain part of the documents slice, not this case-details slicegetcase_api.js,getcasebyid_api.js, andgetportalmoduledetails_api.jsare 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.jsget-case-message.jsget-linked-cases.jsget-sips-events.jsget-sips-media.js
- Implemented each façade as the smallest safe compatibility wrapper:
- import the existing legacy endpoint handler
- delegate
reqandresdirectly to that existing handler
- Updated only the existing owning service call sites needed for the bounded slice:
actions/services/caseDirectService.jsgetIncidentbyIDgetCaseMessagegetSIPSEventsgetSIPSMedia
actions/services/searchDirectService.jsgetLinkedCases
- 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.mdnode 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.mdcontext/api-grouping-adoption-roadmap.mdcontext/api-route-map.mdcontext/portal-api-platform-assessment.mdcontext/architecture.mdmemory-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.cjsnode tests/phase22/api-grouping-facade-rollout.test.cjsnode tests/phase22/api-grouping-facade-subscriptions.test.cjsnode tests/phase22/api-grouping-facade-documents.test.cjsnode 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.mdcontext/api-grouping-adoption-roadmap.mdcontext/api-route-map.mdcontext/journey-architecture-map.mdcontext/portal-api-platform-assessment.mdcontext/architecture.mdmemory-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.jsandcomponents/search/searchresults.js - the advanced results page
pages/advancedsearchresults.jsis part of the active public-search scope - the common owning service layer is
actions/services/searchDirectService.js components/search/searchresults.jsactively uses:getBasicSearchPagedgetAdvancedSearchPagedgetSearchDetailsPaged(...), which in turn usesgetBasicSearchDetailsPaged
pages/advancedsearchresults.jsactively usesgetAdvancedSearch(...)for bootstrapgetbasicsearch_api.jsandgetbasicsearchdetails_api.jsremain 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
- the active public results UI is driven through
- Added grouped façade routes under
pages/api/search/for the proven active public-results path only:basic-paged.jsadvanced.jsadvanced-paged.jsbasic-details-paged.js
- Implemented each façade as the smallest safe compatibility wrapper:
- import the existing legacy endpoint handler
- delegate
reqandresdirectly to that existing handler
- Updated only the suitable common service layer in
actions/services/searchDirectService.jsto target grouped façade routes for:getBasicSearchPagedgetAdvancedSearchgetAdvancedSearchPagedgetBasicSearchDetailsPaged
- 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.cjsnode 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.mdcontext/api-grouping-adoption-roadmap.mdcontext/journey-architecture-map.mdcontext/api-route-map.mdcontext/architecture.mdmemory-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_hashlinkvalues that continue to point at the unchanged grouped download route getsearchdocumenthistory_api.jsandgetsearchdocumenthistorypaged_api.jsremain present but were not surfaced in the currently active UI journey and therefore remained outside grouped adoption scope
- active document metadata retrieval is surfaced through
- Added grouped façade routes under
pages/api/documents/for the active metadata journey only:get-search-document-details.jsget-search-document-details-paged.jsget-search-document-types.js
- Implemented each façade as the smallest safe compatibility wrapper:
- import the existing legacy endpoint handler
- delegate
reqandresdirectly to that existing handler
- Updated only the suitable common service layer in
actions/services/searchDirectService.jsto target grouped façade routes for:getSearchDocumentDetailsgetSearchDocumentDetailsPagedgetSearchDocumentTypes
- 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.mdnode 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 journeydeletewatchedcasesproxy_apiremains 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.jssogetWatchedCasesProxynow 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.jsnode 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.mdcontext/api-grouping-adoption-roadmap.mdcontext/api-route-map.mdmemory-bank/change-log.md
- Updated only the watched-case methods in
actions/services/portalDirectService.jsto target grouped façade routes:getWatchedCases->/api/subscriptions/get-watched-casescreateWatchedCases->/api/subscriptions/create-watched-casedeleteWatchedCases->/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.mdnode 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.mdcontext/api-grouping-adoption-roadmap.mdcontext/api-route-map.mdcontext/journey-architecture-map.mdcontext/portal-api-platform-assessment.mdcontext/architecture.mdmemory-bank/change-log.md
- Added the first grouped façade routes under
pages/api/subscriptions/:get-watched-cases.jscreate-watched-case.jsdelete-watched-case.js
- Implemented each façade as the smallest safe compatibility wrapper:
- import the existing watched-case endpoint handler
- delegate
reqandresdirectly to that existing handler
- Preserved the existing canonical watched-case routes unchanged:
pages/api/endpoint/getwatchedcases_api.jspages/api/endpoint/createwatchedcases_api.jspages/api/endpoint/deletewatchedcases_api.js
- Did not change frontend call sites;
actions/services/portalDirectService.jscontinues 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.cjsnode 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.mdcontext/api-route-map.mdcontext/journey-architecture-map.mdcontext/portal-api-platform-assessment.mdcontext/architecture.mdcontext/integration-map.mdmemory-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.mdcontext/journey-architecture-map.mdcontext/portal-api-platform-assessment.mdcontext/architecture.mdcontext/integration-map.mdmemory-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/apilisting 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
CL-2026-06-22-JOURNEY-ARCHITECTURE-MAP-SLICE-8: case-messages-notices and related-published-case-communications lifecycle map
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.mdcontext/api-route-map.mdcontext/portal-api-platform-assessment.mdcontext/architecture.mdcontext/integration-map.mdmemory-bank/change-log.md
- Extended
context/journey-architecture-map.mdwith 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) messagesObjis passed as page propsCaseNoticeBannerrenders active banner-style notices on the case-details tab
- case loader retrieves
- 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, andpages/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
- targeted searches across
- 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.mdcontext/api-route-map.mdcontext/portal-api-platform-assessment.mdcontext/architecture.mdcontext/integration-map.mdmemory-bank/change-log.md
- Extended
context/journey-architecture-map.mdwith 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
- metadata-generated
- 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, andpages/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
- targeted searches across
- 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.mdcontext/api-route-map.mdcontext/portal-api-security-boundary-assessment.mdcontext/architecture.mdcontext/integration-map.mdmemory-bank/change-log.md
- Extended
context/journey-architecture-map.mdwith 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, andpages/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
- targeted searches across
- 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.mdcontext/api-route-map.mdcontext/portal-api-platform-assessment.mdcontext/architecture.mdcontext/integration-map.mdmemory-bank/change-log.md
- Extended
context/journey-architecture-map.mdwith 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)/myportalor/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.jsand supporting data routes
- thin direct Notify sends via
- 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, andpages/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
- targeted searches across
- 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.mdcontext/api-route-map.mdcontext/portal-api-platform-assessment.mdcontext/architecture.mdcontext/integration-map.mdmemory-bank/change-log.md
- Extended
context/journey-architecture-map.mdwith 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
- registration bridges
- 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, andstore - direct review of homepage bootstrap, registration page/components, personal-details page/components, account service helpers, account endpoint handlers, and relevant auth support routes
- targeted searches across
- 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.mdcontext/api-route-map.mdcontext/architecture.mdcontext/integration-map.mdmemory-bank/change-log.md
- Extended
context/journey-architecture-map.mdwith 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
- representation drafts are more tightly case-linked and
Validation:
- Documentation-only work; no runtime code changed.
- Non-destructive evidence gathering only:
- targeted searches across
pages,lib,actions/services,pages/api, andstore - direct review of representation loaders, representation completion component, storage routes, queue/completion routes, and representation-specific Redux state
- targeted searches across
- 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.mdcontext/api-route-map.mdcontext/architecture.mdcontext/integration-map.mdmemory-bank/change-log.md
- Extended
context/journey-architecture-map.mdwith 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, andstore - direct review of draft/resume loaders, Redux hydration helpers, new appeal flow components, storage helpers, finalisation handlers, and adjacent CRM mutation routes
- targeted searches across
- 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.mdcontext/portal-api-security-boundary-assessment.mdcontext/integration-map.mdmemory-bank/debt-list.mdmemory-bank/change-log.md
- Assessed only the requested first slice folders:
pages/api/endpointpages/api/filepages/api/emailpages/api/documentspages/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/respondErrorresponse 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
- small directory listings of sampled API folders and
- 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 ofpages/api/endpointto 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.mdcontext/portal-api-security-boundary-assessment.mdcontext/architecture.mdmemory-bank/change-log.md
- Assessed only a bounded representative subset inside
pages/api/endpointacross 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
- newer helper-oriented read routes using
- 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.nextLinknormalization- 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.jsgetbasicsearchpaged_api.jsgetadvancedsearch_api.jsgetsearchdocumentdetails_api.jsgetsearchdocumenthistory_api.jsgetmycases_api.jsgetmyrepresentations_api.jsgetwatchedcases_api.jsgetawaitingsubmission_api.jsgetpersonalaccount_api.jscreatecase_api.jscreateaccount_api.jscreatewatchedcases_api.jsupdateaccount_api.jspatchcase_api.jsdeletewatchedcases_api.jsdeletemyrepresentations_api.jsgetwatchedcasesproxy_api.js
- targeted endpoint-only searches for helper/direct-wrapper indicators:
relayGet(...)relayGetData(...)axios(...)hashAPIPath(...)respondSuccess(...)/respondError(...)transformData@odata.nextLink- common status-code patterns
- direct inspection of representative endpoint routes including:
- 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 representativepages/api/endpointroutes that use transforms,@odata.nextLinknormalization, paged/unpaged variants, and supplementaryrelayGetData(...)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.mdcontext/architecture.mdcontext/integration-map.mdmemory-bank/debt-list.mdmemory-bank/change-log.md
- Built a bounded maintenance map for the top-level API areas:
endpointfileemaildocumentsauthadminmiddleware- 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 + hashAPIPathpatterns.
Validation:
- Documentation-only assessment; no runtime code changed.
- Non-destructive evidence gathering only:
- small top-level listings of
pages/apiandpages/api/admin - direct representative inspection of:
pages/api/admin/getnewappeals_api.jspages/api/admin/getlatestdocuments_api.jspages/api/health.jspages/api/doc.tspages/api/notices/index.jspages/api/middleware/{apiResponse,relayForwarding,relayPolicyPresets,middleware}.jsactions/azurestorage.jsactions/clients/signedRequestClient.js
- targeted searches across
pages/apifor existing helper and integration-building-block usage - reuse of already completed bounded evidence from Slices A and B for folder/feature classification
- small top-level listings of
- 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.mdcontext/architecture.mdcontext/integration-map.mdmemory-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, andstorefor the four sampled journeys - direct representative inspection of:
components/myportal/viewall.jspages/account/personaldetails.jscomponents/account/personaldetails.jscomponents/search/searchresults.jslib/myportal/loadMyPortalAppealPage.jscomponents/case/representation/representationComplete.js
- reuse of already reviewed route/service evidence from prior slices for linked API ownership and helper classification
- targeted bounded searches across
- 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.mdcontext/architecture.mdcontext/integration-map.mdmemory-bank/change-log.md
- Performed bounded top-level API folder sizing and validation:
endpoint-> 74 filesfile-> 26 filesemail-> 6 filesdocuments-> 1 fileauth-> 2 filesadmin-> 5 filesmiddleware-> 4 files- top-level files -> 4
- Confirmed that the earlier API platform model still appears representative:
endpointremains the dominant historical CRM relay catch-allfileremains 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
- low drift:
- Identified main maintenance hotspots:
- high:
endpoint,file - medium-high:
auth,middleware - medium:
email - low/medium-low:
documents,admin
- high:
- Recorded a high-confidence conclusion that Slices A–D 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, andpages/api/file - direct representative inspection of selected wider-surface outlier routes:
pages/api/endpoint/getmandatoryfields_api.jspages/api/endpoint/createcrmtask_api.jspages/api/file/createcaseinvolvement_api.jspages/api/file/generateappealpdf.jspages/api/email/getdocuments.jspages/api/admin/getStatusCountsByAppealAndLPA_api.js
- comparison against the documented conclusions from Slices A–D
- 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 A–E 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 A–E 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.mdcontext/architecture.mdmemory-bank/change-log.md
- Added a final synthesis section to
context/portal-api-platform-assessment.mdand 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
endpointandfile - smaller folders are more coherent but still require sensitivity in
authandmiddleware
- highest drift/hotspots remain
- 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.mdso 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 A–E.
- 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.mdcontext/architecture.mdcontext/integration-map.mdmemory-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:
relayGetrelayGetDatarespondSuccessrespondError- 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
- deeper
- 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.mdas a concise first-pass assessment ofpages/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
- recursive listing of
- Lint/tests not run because no implementation files were changed.
Follow-ups:
- Next assessment pass: trace
gethash_apiconsumers 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.mdwith 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
pinsUsercookie 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.jslib/representation/pageLoaders.jslib/newappeal/loadNewAppealPage.jslib/myportal/loadMyPortalAppealPage.jspages/myportal/index.jspages/myportal/case/[ticketnumber].jspages/myportal/representation.jsactions/services/{accountDirectService,portalDirectService,documentDirectService}.jspages/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
pinsUsercookie and compare it against thegetPortalLogin(session.user.email)path to determine which identity source is canonical and where divergence risk exists.
CL-2026-06-19-PORTAL-API-PINSUSER-TRACE: cookie identity provenance and comparison with CRM contact resolution
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
pinsUsertrace covering:- cookie contents
- creation source
- overwrite/refresh behavior
- clear/delete paths
- consumption across SSR and client flows
- Determined that
pinsUserstores a CRMcontactidvalue. - Identified the primary creation path in
pages/index.js:NextAuth session->session.user.email->getPortalLogin(email)-> CRMcontactid->setCookie("pinsUser", contactid)
- Identified that later flows frequently consume
pinsUserdirectly without re-resolving CRM contact from the current session. - Recorded the architectural conclusion that
pinsUserbehaves 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.idremains 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.jspages/account/personaldetails.jscomponents/search/searchresults.jscomponents/search/addresssearchresults.jslib/auth/sessionClient.jslib/auth/resolveMyPortalAuthContext.jslib/newappeal/loadNewAppealPage.jslib/myportal/loadMyPortalAppealPage.jspages/error.jspages/_error.jspages/componentssearch results forpinsUser,setCookie,destroyCookie, andparseCookies
- 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.mdwith 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/contactidto a session-derived CRM contact
- Confirmed that the reviewed personal-details UI does not allow direct email editing because
emailaddress1is rendered disabled, but also recorded thatupdateaccount_api.jsforwards 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 writespinswg_custom_passwordthroughupdateaccount_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.jspages/account/changepassword.jscomponents/account/{personaldetails,personaldetailsCheck,personaldetailsComplete,changepassword}.jscomponents/myportal/youraccount.jspages/index.jspages/myportal/index.jsactions/services/accountDirectService.jspages/api/endpoint/{getpersonalaccount_api,updateaccount_api,updatepassword_api,getemailaccountcheck_api,getportallogin_api}.jsstore/accountDetails/{action,reducer}.js
- Additional targeted searches performed for
setLoggedInUserId,setAccountDetails,updatePassword,pinswg_custom_password, andupdatepassword_apiusage. - 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.mdwith 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}.jspages/api/file/{createcaseinvolvement_api,createrepinvolvement_api,createappealcompletemessage_api,createrepcompletemessage_api,createcase_api,updatecase_api}.jsactions/services/{portalDirectService,caseDirectService}.jscomponents/{case/summary,myportal/viewall,newappeal/createCase,newappeal/buildsection,case/representation/representationComplete}.jslib/newappeal/journeyEffects.js
- Additional targeted searches performed across
pages/api,actions/services,components, andlibfor 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_apiand 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.mdwith 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:
watchedCaseIDoriginates 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.jsactions/clients/relayClient.jsactions/clients/signedRequestClient.jsactions/services/portalDirectService.jspages/api/endpoint/deletewatchedcases_api.jscomponents/myportal/topthree.jscomponents/myportal/viewall.jscomponents/case/summary.jscomponents/search/searchresults.js
- Additional targeted searches performed for
gethash_api,buildHashedQueryUrl,buildSignedUrl,deleteWatchedCases,watchedCaseID, andpinswg_watchlistidacrossactions,components, andpages/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_apiand 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.mdwith 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.idas the container identity for draft reads and store hydration. - Confirmed many file/blob APIs accept
container/containerIDas 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.jslib/myportal/loadMyPortalAppealPage.jslib/representation/pageLoaders.jsactions/services/documentDirectService.jsactions/azurestorage.jspages/api/file/{getprogressobjblob,getbloblist,getawaitingsubmissionfromblob,upload,uploadsinglefile,deleteblobcase,deleteblobrep,downloadblob,setupcontainer,createappealcompletemessage_api,createrepcompletemessage_api,editRepJson}.js
- Additional searches performed across
lib,pages,actions, andpages/api/fileforsession.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.mdtitled 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
pinsUserlifecycle 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.mdwith a dedicated Programme status section stating:Portal API Security & Access Boundary AssessmentStatus: 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.mdto 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.mdbefore adding stream close-out language. - Reviewed
context/architecture.mdand 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.jscomponents/account/registerform.jscomponents/account/registerCheck.jscomponents/account/registerComplete.jsactions/services/accountDirectService.createAccountpages/api/endpoint/createaccount_api.js
- Determined that registration uses the authenticated session email as the intended identity source:
loggedInUserEmailcomes fromgetServerSideProps- form
emailaddress1is 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
contactsviacreateaccount_api, withpinswg_typeofinvolvementset 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-runsgetPortalLogin(session.user.email), finds the newly created contact, setspinsUser, 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.jspages/account/register.jscomponents/account/registerform.jscomponents/account/registerCheck.jscomponents/account/registerComplete.jsactions/services/accountDirectService.jspages/api/endpoint/createaccount_api.jspages/api/endpoint/getemailaccountcheck_api.jspages/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)
- CRM
- Recorded that
pinswg_casestageis 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.mdwith business stakeholders. - Do not broaden representation-policy boundaries further until validation outcomes are agreed.
CL-22541: breadcrumb/back-link route-state helper extraction (va/adv/ads/key)
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.jswith:normalizeRouteStateQuery(query)resolveSearchResultsHref(options)
- Updated
components/breadcrumbs.jsto 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.
- breadcrumb href resolution (
- Updated
components/case/summary.jsto replace localbuildSearchHrefwith 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.cjsand registered it intests/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 incomponents/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) withReferenceError: resolveLocale is not definedinpages/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.jswith:resolveSearchBreadcrumbLabel(...)isDnsRoutePath(pathname)
- Updated
components/breadcrumbs.jsto useresolveSearchBreadcrumbLabel(...)and removed inline label decision IIFE. - Updated
components/case/summary.jsto useisDnsRoutePath(router.pathname)and removed inline DNS path checks. - Expanded
tests/phase22/route-state-helper.test.cjswith 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 incomponents/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.jsinto 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(...)inlib/routing/routeState.jsto return:breadcrumbHrefcaseResultsHrefbreadcrumbLabel
- Updated
components/breadcrumbs.jsto replace three separate helper invocations with one resolver call. - Extended
tests/phase22/route-state-helper.test.cjswith 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 incomponents/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.
newappealparent-title crumb,cyMyPortalHrefaccount 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 incomponents/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.
CL-22541-F: breadcrumbs multi-pattern helper bundle (text + link crumbs)
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 preserverouter.back()behavior. - Combined with existing
renderMyPortalCrumbandrenderCaseReferenceCrumbhelpers 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.
CL-22541-G: breadcrumbs inline Link-crumb consolidation bundle
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.
CL-22541-H: breadcrumbs anchor/back-link helper consolidation bundle
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)incomponents/breadcrumbs.jsfor repeated<a ...>breadcrumb actions. - Replaced repeated anchor/back-link crumb blocks in:
/myportal/searchresultsmy-portal anchor crumb/newappeal/[appealtypes]and/myportal/[appealtypes]step-back crumbs/myportal/representationback-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 optionalrenderBackCrumb(...)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 torenderAnchorCrumb(...)with existing back-link text. - Replaced remaining one-off inline edge crumbs in:
/newappeal/[appealtypes]and/myportal/[appealtypes](currentSection === 9999service-name link and back-link branch)/casesearch-results link crumb (including existingrouter.back()callback)/myportal/representationback-link branches now userenderBackCrumb(...).
- Preserved existing href targets, callback behavior, and class semantics (including special
govuk-breadcrumbs__link-itemusage).
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)incomponents/breadcrumbs.jsto 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)incomponents/breadcrumbs.jsfor 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)incomponents/breadcrumbs.jsfor 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]withrouter.back()). - Added focused phase22 suite
tests/phase22/breadcrumbs-route-map-structure.test.cjsasserting:- 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)incomponents/breadcrumbs.jsand 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.cjsto 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]withrouter.back()). - Expanded
tests/phase22/breadcrumbs-route-map-structure.test.cjsguardrails 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:simpleRouteRenderersByPathsimpleLinkTextPairRenderersByPathsimpleMyPortalRouteRenderersByPath
- 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.jspure 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.jsto import/use shared helper logic:- map composition now via
buildBreadcrumbRendererMaps(...) - resolver now delegates to
resolveMappedRouteRendererByMaps(...) - dynamic/callback-sensitive explicit branches remain unchanged.
- map composition now via
- Added focused pure-helper tests in
tests/phase22/breadcrumb-route-maps-helper.test.cjsfor:- map build order
- map lookup null/renderer behavior
- resolver precedence behavior
- Expanded
tests/phase22/breadcrumbs-route-map-structure.test.cjsto 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.cjsto 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.jsinto module-level pure factories (still injected witht/router/currentView) to further shrink component body. - Next slice option B (smaller): add one focused parity test asserting mapped resolver fallback returns
nullin 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(...)inlib/routing/breadcrumbRendererFactories.jsto compose and return all deterministic mapped renderer groups in one place:simpleRouteRenderersByPathsimpleLinkTextPairRenderersByPathsimpleMyPortalRouteRenderersByPathcaseDetailRouteRenderersByPathdetailAndAccountRouteRenderersByPath
- Updated
components/breadcrumbs.jsto consumecreateMappedRouteRendererGroups(...)and remove local per-group factory composition boilerplate while preserving existing map precedence and resolver flow. - Expanded
tests/phase22/breadcrumbs-route-map-structure.test.cjsto assert:- grouped renderer maps are composed via
createMappedRouteRendererGroups(...) - mapped rendering path explicitly preserves null fallback (
routeRenderer ? routeRenderer() : null)
- grouped renderer maps are composed via
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(...)inlib/routing/breadcrumbRendererFactories.jsto map:/myportal/case/case/id/[incident]
- Injected additional deterministic inputs required by these routes:
currentReferencenestedSearchString
- Updated
createMappedRouteRendererGroups(...)wiring to pass the new inputs to case-detail factory composition. - Updated
components/breadcrumbs.jsto passnestedSearchStringinto grouped factory composition. - Removed now-redundant inline
isPath(...)branches for:/myportal/case/case/id/[incident]
- Expanded
tests/phase22/breadcrumbs-route-map-structure.test.cjsto assert:- both new routes are mapped in
createCaseDetailRouteRenderers - both inline branches are absent from
components/breadcrumbs.js
- both new routes are mapped in
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(...)inlib/routing/breadcrumbRendererFactories.js. - Mapped
/newappeal/selectappealinto the new grouped renderer factory. - Extended grouped composition return from
createMappedRouteRendererGroups(...)withnewAppealRouteRenderersByPath. - Updated
lib/routing/breadcrumbRouteMaps.jsto includenewAppealRouteRenderersByPathin explicit map precedence ordering. - Updated
components/breadcrumbs.jsto consume/passnewAppealRouteRenderersByPathinto 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
/casebranch 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
nullwhen map is missing/non-object - returns
nullwhen mapped value is not a function
- returns
- 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.
- invalid map entries (
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(...)inlib/routing/breadcrumbRendererFactories.jsto map:/myportal/case/id/[incident]/case
- Introduced explicit callback injection (
onBack) into grouped factory composition and route renderers, preservingrouter.back()behavior via injected callback. - Extended
createMappedRouteRendererGroups(...)return withcallbackRouteRenderersByPath. - Updated map builder in
lib/routing/breadcrumbRouteMaps.jsto 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]")andisPath("/case")branches.
- pass
- 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(...)inlib/routing/breadcrumbRendererFactories.jsto 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-itemfor new-appeal flow)
- no crumb when
- Introduced explicit injected handlers (
onStepBack,onStepBackWithInlineClass) and injected crumb renderer dependency (renderBackCrumb) into grouped factory composition. - Extended map builder in
lib/routing/breadcrumbRouteMaps.jswithstepBackRouteRenderersByPathand updated precedence order:- simple -> link-text -> myportal -> new-appeal -> callback -> step-back -> case detail -> detail/account.
- Updated
components/breadcrumbs.js:- pass
currentSectionand step-back callbacks intocreateMappedRouteRendererGroups(...) - include step-back map in
buildBreadcrumbRendererMaps(...) - remove inline
isPath("/newappeal/[appealtypes]")andisPath("/myportal/[appealtypes]")branches.
- pass
- 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/representationback-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
nullfor:- empty path (
"") - whitespace-only path (
" ") - trailing/leading whitespace variants (
"/known "," /known") - case-variant non-exact key (
"/KNOWN")
- empty path (
- 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/representationcallback/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(...)inlib/routing/breadcrumbRendererFactories.jsmapping:/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))
- submit/back from confirmation (
- Introduced explicit injected state/callback dependencies into grouped composition:
currentView,hasRepDetails,repDetails,showQuestionnaireSectiononRepresentationSubmitBackFromConfirmationonRepresentationSubmitBackToDetailsonRepresentationQuestionnaireBack
- Extended
buildBreadcrumbRendererMaps(...)inlib/routing/breadcrumbRouteMaps.jswithrepresentationRouteRenderersByPathand 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
representationRouteRenderersByPathin map builder call - remove inline
isPath("/myportal/representation")branch block.
- pass representation state/callback injections into
- 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
/newappealedge 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-006tomemory-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
/newappealinline 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
representationRouteRenderersByPathresolves/myportal/representationtonull - later maps still resolve correctly (case detail fallback remains intact).
- missing
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
/newappealinline 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") && <></>}
- deleted
- Added structure guard assertion that
/newappealinline 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:
- current architecture summary
- strengths
- risks (scalability, maintainability, coupling, weak boundaries, operational/deployment)
- prioritized recommendations
- low-risk next improvements
- Added explicit comparison notes vs existing
context/architecture.mdand 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.jsgetThumbnailIconByMimeTypegetDocumentTypePrefixvalidateUploadFilename
components/elements/helpers/translationHelpers.jsgetFieldTranslationgetPickListTranslation
- Updated
components/elements/index.jsto 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.mdwith 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.jsin 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.jscontaining the extractedRenderRichMultilinerenderer. - Updated
components/elements/index.jsto import the extracted renderer and removed the inline duplicate implementation. - Kept
RichMultiLinefieldusage 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.jsandcomponents/elements/fields/renderMultiline.js. - Updated
components/elements/index.jsto import the extracted renderers. - Removed inline
RenderTextfieldandRenderMultilineimplementations fromindex.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/respondSuccessusage. - Added explicit required-input guards:
DOCUMENT_ID_REQUIREDfor history/historypagedINCIDENT_ID_REQUIREDfor details/detailspaged/typesORDER_BY_REQUIRED,FIELD_SORT_REQUIRED,SHOW_NUMBER_OF_RECORDS_REQUIREDfor 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-existingreact-hooks/exhaustive-depswarnings; 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.jsto userespondSuccessandrespondErrorfrompages/api/middleware/apiResponse. - Removed legacy raw
res.status(...).json(...)/bare status assignment pattern and dead logging artifacts. - Added explicit catch-path contract:
TOKEN_FETCH_FAILEDwith 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-existingreact-hooks/exhaustive-depswarnings; 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):
b57f3desearch-document endpoint contracts + phase21 coverage9af541amy-portal retrieval endpoint contractsb880364basic search endpoint contractsa106deaDNS basic search endpoint contractsb5a3a62portal module + LPA case endpoint contracts4601d7ccase detail endpoint contracts2959c7ddelete/watched-case endpoint contractsb59f13ametadata + linked-case endpoint contractsbcf03a6form + publication endpoint contractse0e91c8DNS + representation endpoint contracts98e159dcase creation + media endpoint contracts88e4586advanced-search-paged endpoint contractcb69bbecase update + CRM task endpoint contracts722ef98hash + metadata endpoint contracts134f99caddress-search endpoint contract8b6ed73new-appeal appeal-types endpoint contracteec59e8token 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-existingreact-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
respondErrorwithDOWNLOAD_BLOB_FAILED - kept success behavior intact (attachment header + raw file body)
- removed dead internal helper (
streamToBuffer) and tightened local declarations
- added explicit catch-path response via
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.jsandgetappealtypesfornewappeal_api.js:- added method guard for non-GET requests using
METHOD_NOT_ALLOWED(405) - preserved existing GET success payloads
- added method guard for non-GET requests using
- Expanded phase21 tests:
file-handler-contract.test.cjs: added coverage for download failure + full generated PDF copy contract/negative pathsendpoint-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-existingreact-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
blobnameinput:- filename only (legacy)
- full prefixed path (already includes
casefolderID/...)
- prevents double-prefix lookup failures
- sets attachment filename from final path segment only
- accepts both forms of
- Hotfix 2 (
bd3bf68): hash compatibility validation- validates against a bounded set of canonical query-path variants (raw/encoded combinations for
casefolderIDandblobname) - fixes
INVALID_HASHfalse negatives for legitimate caller-generated links - keeps strict HMAC requirement in place (no unauthenticated bypass)
- validates against a bounded set of canonical query-path variants (raw/encoded combinations for
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, anddeleteblobrep.jsby aligning hash validation canonicalization and explicitrespondErrorcontracts (MISSING_REQUIRED_QUERY,INVALID_HASH, operation-specific*_FAILED). - Extend
tests/phase21/file-handler-contract.test.cjsfor 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
casefolderIDandblobname - normalized delete path handling for both filename-only and already-prefixed blob paths
- added explicit catch-path contract:
DELETE_BLOB_FAILED
- added bounded hash candidate validation for encoded/raw combinations of
deleteblobcase.js- added hash candidate validation for raw/encoded
casefolderID - added explicit catch-path contract:
DELETE_BLOB_CASE_FAILED
- added hash candidate validation for raw/encoded
deleteblobrep.js- added hash candidate validation for encoded/raw
casefolderID+repfile - added explicit catch-path contract:
DELETE_BLOB_REP_FAILED
- added hash candidate validation for encoded/raw
- 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)inactions/azurestorage.js:- fixed async misuse (
blobClient.getProperties().contentLengthwithout 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/undefinedname filtering intact
- fixed async misuse (
- Added phase21 contract coverage for
getrepsblobroute:- 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
findBlobsByTagsresults 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
getawaitingsubmissionfromblobhandler to structuredtry/catchflow. - Preserved existing guard behavior:
MISSING_REQUIRED_QUERYfor missing container/hashINVALID_HASHfor 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/.catchchain to explicittry/catch - encoded forwarded
containerandcasefolderIDquery values - preserved error contract:
GET_BLOB_LIST_PROXY_FAILED
- converted
getrepsblobproxy.js- converted
.then/.catchchain to explicittry/catch - encoded forwarded
container - preserved error contract:
GET_REPS_BLOB_PROXY_FAILED
- converted
getawaitingsubmissionfromblobproxy.js- converted
.then/.catchchain to explicittry/catch - preserved error contract:
GET_AWAITING_SUBMISSION_PROXY_FAILED
- converted
createappealcompletemessageproxy_api.js- converted
.then/.catchchain to explicittry/catch - encoded forwarded
containerandtempcaseref - preserved error contract:
CREATE_APPEAL_COMPLETE_MESSAGE_PROXY_FAILED
- converted
- 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
CryptoJSimport - refactored axios
.then/.catchto explicittry/catch - preserved conflict behavior: status 412 -> success
{ record: "exists" } - preserved failure contract:
CREATE_CASE_INVOLVEMENT_FAILED
- removed unused
createrepinvolvement_api.js- removed unused
CryptoJSimport - refactored axios
.then/.catchto explicittry/catch - preserved conflict behavior: status 412 -> success
{ record: "exists" } - preserved failure contract:
CREATE_REP_INVOLVEMENT_FAILED
- removed unused
updatecase_api.js- removed unused
CryptoJSimport - refactored axios
.then/.catchto explicittry/catch - preserved failure contract:
UPDATE_CASE_FAILED
- removed unused
- 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/.catchtotry/catch - preserved flattening behavior and error contract
MAILING_LIST_FETCH_FAILED
- converted axios
pages/api/email/getcaseref.js- converted axios
.then/.catchtotry/catch - preserved flattening behavior and error contract
CASE_REF_FETCH_FAILED
- converted axios
pages/api/email/notify.js- converted notify client
.then/.catchtotry/catch - preserved success payload and error contract
EMAIL_NOTIFY_FAILED
- converted notify client
pages/api/admin/getnewappeals_api.js- removed unused
CryptoJSimport - converted axios
.then/.catchtotry/catch - preserved
@odata.nextLinknormalization and error contractADMIN_NEW_APPEALS_FETCH_FAILED
- removed unused
pages/api/admin/getlatestdocuments_api.js- converted axios
.then/.catchtotry/catch - preserved flatten/enrich behavior and error contract
ADMIN_LATEST_DOCS_FETCH_FAILED
- converted axios
pages/api/endpoint/getportallogin_api.js- retained required query/hash guards
- expanded hash validation to accept raw + encoded
emailAddressquery-path candidates - preserved error contract
PORTAL_LOGIN_FETCH_FAILED
- phase21 endpoint tests expanded:
getportalloginencoded hash variant success pathgetnewappeals_apicatch contractgetlatestdocuments_apicatch 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(+ optionalpages/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
- converted mixed promise-chain flow to
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
- converted create/upload promise-chain to
- phase21 file tests expanded:
generatepdfcatch-path contract (GENERATE_PDF_FAILED)generateappealpdfcatch-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-forgetupdateAccount(...)with explicit async IIFE +try/catchandvoidinvocation - preserved non-blocking behavior and logging semantics for account-update failure path
- preserved primary route contracts and success payload (
{ status: "success" })
- replaced inline
- phase21 file contract tests expanded:
- success path for encoded hash candidate on
createappealcompletemessage_api - dependency-failure contract assertion for
CREATE_APPEAL_COMPLETE_MESSAGE_FAILED
- success path for encoded hash candidate on
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 directMyDocument(...)invocation inReactPDF.renderToStream(...)
- switched render call input from
pages/api/file/generateappealpdf.js- switched render call input from
React.createElement(MyDocument, ...)to directMyDocument(...)invocation inReactPDF.renderToStream(...)
- switched render call input from
tests/phase21/file-handler-contract.test.cjs- added
Bufferinjection forgeneratepdfcatch-path test harness to align VM context expectations
- added
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/renderernamed imports - removed unused imports (
middleware,nextConnect,fs) - removed unused locals (
casefolderID,representationType,repRaiser,localeSelect,repCapacity,repType)
- pruned unused Azure storage imports, leaving only
pages/api/file/generateappealpdf.js- pruned unused Azure storage imports to only required functions
- pruned unused
@react-pdf/renderernamed 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
idandhash - unified fallback redirect path via constant (
/filenotavailable) - preserved streaming download behavior and retry flow
- added explicit required-query guard for
- added
tests/phase21/documents-handler-contract.test.cjscovering:- 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/respondSuccessenvelope 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) }
- replaced:
- 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.logdiagnostics 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.jsgetrepresentationsproxy_api.jsgetawaitingsubmissionproxy_api.js(with existing payload transform preserved)
- Updated endpoint contract tests to mock
relayGetfor 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
relayGetto support optionalrequestOptionsBuilderfor handlers requiring paged header variants. - Migrated Batch 1 endpoints to
relayGet:getaccounts_api.jsgetemailaccountcheck_api.jsgetpreferredlanguage_api.jsgetlogin_api.js(usesazureHeadersPagedviarequestOptionsBuilder)getpersonalaccount_api.jsgetportalloginproxy_api.js(usesazureHeadersPagedviarequestOptionsBuilder)
- Updated phase21 endpoint contract tests to mock
relayGetfor 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.jsgetwatchedcases_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
relayGetin 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.nextLinknormalization)getcasebyid_api.js(preserved@odata.nextLinknormalization and array-wrapped success payload)getincidentbyid_api.jsgetsipsevents_api.js
- Updated phase21 endpoint contract tests to mock
relayGetfor 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.jsgetformdata_api.jsgetsearchdocumenthistory_api.js(usesrequestOptionsBuilderwithazureHeadersPaged)getsearchdocumenthistorypaged_api.js(usesrequestOptionsBuilderwithazureHeadersPaged)getportalmoduledetails_api.js
- Preserved all existing required-query validation guards and existing error codes/messages.
- Updated Phase 21 endpoint contract tests to inject
relayGetmocks 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-existingreact-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.jsgetsearchdocumentTypes_api.jsgetsearchdocumentdetails_api.jsgetsearchdocumentdetailspaged_api.jsgetappealpdfdocuments_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.nextLinknormalization where present- appeal PDF document name projection
- Updated phase21 endpoint tests to mock
relayGetfor 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-existingreact-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.jsgetbasicsearchdetails_api.jsgetbasicsearchdetailspaged_api.jsgetbasicdnssearchdetails_api.jsgetbasicdnssearchdetailspaged_api.js
- Preserved existing validation guards and error contracts.
- Preserved existing endpoint-specific transforms:
@odata.nextLinknormalization in paged responses- flattened ticketnumber enrichment for search detail handlers
- Updated phase21 endpoint contract tests to inject
relayGetmocks 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-existingreact-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.jsgetbasicdnssearchpaged_api.jsgetbasicpartsaveddetails_api.jsgetdnslist_api.js
- Preserved existing input validation guards and endpoint-specific error contracts.
- Preserved endpoint-specific transform behavior (
@odata.nextLinknormalization in DNS list/paged handlers). - Updated phase21 endpoint contract tests to inject
relayGetmocks 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-existingreact-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 callgetmylpacases_api: JSONPath LPA lookup withLPA_NOT_FOUNDhandling andpinswg_titleenrichment transform
- Updated phase21 endpoint contract tests to inject
relayGetmocks 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-existingreact-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.jsto sharedrelayGet. - Preserved existing guard behavior and error contracts:
SEARCH_STRING_REQUIREDORDER_BY_REQUIREDFIELD_SORT_REQUIREDSHOW_NUMBER_OF_RECORDS_REQUIREDINVALID_SEARCH_STRINGADVANCED_SEARCH_PAGED_FETCH_FAILED
- Preserved endpoint-specific transform behavior (
@odata.nextLinknormalization viatransformData). - Updated phase21 endpoint contract tests to inject
relayGetmocks forgetadvancedsearchpaged_apiguard/catch assertions.
Validation:
node tests/phase21/endpoint-handler-contract.test.cjs-> pass (152/152)npm run lint-> warnings only (pre-existingreact-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:
relayGetDatanow supports token reuse viatransformDatapath- Added
forwardGetDatato support custom base URLs (used by proxy-to-local endpoint) while preserving hash behavior
- Migrated remaining Batch 12 candidates:
getadvancedsearch_api.js->relayGet+relayGetDatafor project-type enrichment loopgetdnscoords_api.js->relayGetDatafor both DNS + SIPS source pullsgetbasicsearch_by_lparref_api.js->relayGetDatagetbasicsearch_by_address_api.js->relayGetDatafor appeal-type and incident fetchesdeletewatchedcasesproxy_api.js->forwardGetDatawithBASE_URLcreatewatchedcases_api.js->relayGetDatafor 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/forwardGetDatawhere appropriate.
Validation:
node tests/phase21/endpoint-handler-contract.test.cjs-> pass (152/152)npm run lint-> warnings only (pre-existingreact-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
forwardGetDatainrelayForwarding.jswith:- 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)
- configurable timeout (
- Added structured, redacted operational relay logs:
relay_request_retryingrelay_request_failed
- Preserved compatibility behaviors:
- existing token/header/hash handling
- optional
appendHashand customrequestOptionsBuilder - endpoint-level
relayGeterror 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-existingreact-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
- explicitly non-retryable statuses:
- 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
- timeout clamped to
- Reduced duplicate logging noise:
- non-retry terminal relay failures are marked as already logged in middleware
relayGetcatch now skipsconsoleLoggerwhen 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
- no retry on
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-existingreact-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.mdcovering:- 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_startedrelay_request_retryingrelay_request_succeededrelay_request_failed
- Added shared telemetry fields for correlation and analysis:
relayRequestId(per request correlation id)attemptsMade,retryCountUsed,remainingRetrieselapsedMsstatusClass(2xx/4xx/5xxstyle 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
relayRequestIdacross 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-existingreact-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
- per-call override of
- 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
- env flag
- retries allowed by default only for idempotent methods (
- 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-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Future non-GET relay adoption should explicitly opt in/out per route using
relayPolicyand 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
relayPolicyusage to a broader endpoint set:- account/login:
getaccounts,getemailaccountcheck,getpreferredlanguage,getpersonalaccount,getlogin - search:
getbasicsearch,getbasicsearchpaged,getadvancedsearch,getadvancedsearchpaged
- account/login:
- 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
- login endpoint (
- Kept method explicit as
GETin policy for clarity and future-proofing. - Extended phase21 endpoint tests with relayPolicy propagation assertions:
getaccountsrelayPolicy pass-throughgetloginstrict relayPolicy pass-throughgetbasicsearchpagedrelayPolicy 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-existingreact-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
relayPolicyfor 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-existingreact-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
relayPolicydeclarations to:getmycases_apigetmyrepresentations_apigetwatchedcases_apigetawaitingsubmission_apigetrepresentations_api
- Applied bounded read profile across the batch:
method: "GET"timeoutMs: 8000maxRetries: 2retryBaseDelayMs: 150retryMaxDelayMs: 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-existingreact-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.jsRELAY_POLICY_STRICT_LOGINRELAY_POLICY_LOOKUPRELAY_POLICY_BOUNDED_READRELAY_POLICY_SEARCH_PAGED
- Refactored 18 relayGet endpoints to import and use shared presets instead of inline
relayPolicyobject 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
- lookup profile:
- 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-existingreact-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.jsexportingbuildHashedQueryUrl
- Added
actions/clients/index.jsbarrel and exposed client exports viaactions/index.js. - Updated direct services to consume shared relay client helper instead of duplicating local helper implementations:
actions/services/accountDirectService.jsactions/services/portalDirectService.jsactions/services/documentDirectService.js
- Updated
actions/clients/README.mdto reflect the now-implemented relay client extraction and future incremental client split path.
Validation:
npm run lint-> warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)- Verified no remaining duplicated local
buildHashedQueryUrldefinitions acrossactions/services/*DirectService.js
Follow-ups:
- Optional next TASK22260 increment: extract common axios invocation helpers into dedicated clients (
endpointClient,fileClient,notifyClient) while keepingactions/index.jsAPI stable.
Addendum (same TASK22260 slice):
- Added shared
endpointClientwithgetJsonandrequestJsonhelpers (actions/clients/endpointClient.js) and exported it viaactions/clients/index.js. - Migrated additional direct services to consume shared endpoint client helpers:
actions/services/notifyDirectService.js(POST viarequestJson)actions/services/integrationDirectService.js(POST viarequestJson)actions/services/adminDirectService.js(GET flows viagetJson)
- Updated
actions/clients/README.mdto includeendpointClientin 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.jsfrom directaxios.get(...).then(res => res.data)patterns to sharedgetJson(...)helper fromactions/clients/endpointClient. - Preserved existing error behavior:
logAndReturnEmptyValueErrorResponsefor appeals/project/LPA fetchesconsoleLoggercatch handling for form/mandatory/picklist/notice fetches
- Updated
actions/clients/README.mdusage notes to include reference-data service reuse.
Validation:
npm run lint-> warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Next optional bounded slice: adopt
getJson/requestJsonfor selected low-risk read paths insearchDirectServiceorcaseDirectServicewhile 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.jsto consume shared endpoint client helpers:getJson(...)for GET requestsrequestJson(...)for config-based POST requests
- Kept existing relay hash-signing behavior unchanged via
buildHashedQueryUrlfromrelayClient. - Preserved existing catch-path semantics, including:
- logging with
consoleLogger - returning
JSON.stringify(error)in portal login functions - returning
error.responsein preferred-language failure path
- logging with
Validation:
npm run lint-> warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: adopt endpoint client helpers in selected
portalDirectServiceGET/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
portalDirectServicefunctions to use shared endpoint client helpers:getJson(...)for read/listing routes (getMyCases,getMyInvolvements,getMyLPACases, representations, watched, awaiting submission variants)requestJson(...)forcreateWatchedCases
- 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-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: migrate remaining non-hash POST helpers in
portalDirectService(setRepInvolvment,setCaseInvolvment) torequestJsonfor 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:
setRepInvolvmentsetCaseInvolvment
- 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-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: begin selective
requestJsonadoption for remaining config-based calls indocumentDirectServicewhere 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
getJsonusage in selected document service helpers:getRepsFromBlobProxygetAwaitingSubmissionFromBlobProxygetFilesFromBlobproxygetFilesFromBlobHashedgetProgressFromBlobcreateContainerProxy
- Preserved existing behavior contracts:
- same query composition and hash query fragments
- same catch-path logging and return conventions (including JSON string return in
createContainerProxyerror path)
- Left hash-sensitive delete/upload/generation flows unchanged in this slice.
Validation:
npm run lint-> warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: migrate selected
requestJson-eligible upload/generation helpers indocumentDirectService(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.datatorequestJson(config):uploadFilesuploadSingleFileuploadRepFilesgenerateRepPDFgenerateAppealPDF
- 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
- hashed URL creation via
- Left delete/download/hash-sensitive GET helpers unchanged in this slice.
Validation:
npm run lint-> warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: evaluate remaining legacy direct
axios.get(...).then(res.data)helpers indocumentDirectService(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(...):getAwaitingSubmissionFromBlobgetRepsFromBlobgetFilesFromBlob
- 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
- same BASE_URL and hash query composition via
Validation:
npm run lint-> warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: targeted hygiene in
documentDirectServiceto 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
getJsonusage in selectedcaseDirectServicehelper functions:getAppealPDFDocsgetPortalModuleDetailsgetPortalModuleDetailsProxy
- 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-existingreact-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)togetJsonwhere 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)togetJson(...):getCaseMessagegetIncidentbyIDgetIsPublishedbyIDgetPartSavedAppealgetSIPSEventsgetSIPSMedia
- 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-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: evaluate remaining GET helpers in
caseDirectServicewith 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)togetJson(...):getCasegetCaseByIDgetAppealPDFDocument
- Preserved behavior contracts:
- unchanged request URL/query construction
- unchanged catch semantics:
getCase/getCaseByIDstill log viaconsoleLoggergetAppealPDFDocumentstill logs and returnserror.responseon failure
- left non-targeted POST/update/create/patch flows unchanged.
Validation:
npm run lint-> warnings only (pre-existingreact-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 selectiverequestJson(...)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):createNewCasecreateNewCaseBlobupdateCaseupdateCaseBlob
- 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-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: assess
patchCaseandgetAppealIDfor 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(...):getAppealIDpatchCase
- Preserved behavior contracts:
getAppealIDstill extracts first non-underscore key value from returned entity and logs on failurepatchCasestill swallows failure (no explicit returned error contract introduced)
- Removed now-unused
axiosimport fromcaseDirectService.
Validation:
npm run lint-> warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: review other direct service modules for any remaining legacy
axiosimport 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):deleteAwaitingSubmissionssendCaseCompleteMessagesendCaseCompleteMessageProxysendRepCompleteMessage
- 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-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: assess whether signed-delete helpers in
portalDirectServiceshould 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:
- Portal signed-delete flow parity (
portalDirectService)- migrated signed delete helpers from direct
axios({...}).then(res.data)torequestJson({...}):deleteMyRepresentationsdeleteWatchedCases
- removed now-unused
axiosimport from module - preserved hash-signing flow and custom headers semantics
- migrated signed delete helpers from direct
- Search direct service axios reduction (
searchDirectService)- migrated service GET helpers from direct
axios.get(...).then(res.data)andaxios(config)togetJson(...) - preserved existing catch semantics (
logAndReturnResponse,logAndReturnEmptyValueErrorResponse, and explicit throw path ingetSearchDocumentDetails) - removed direct
axiosimport from module
- migrated service GET helpers from direct
- Portal signed-delete flow parity (
- Documentation alignment:
- retained architecture assessment section in
context/architecture.mdthat calls out these residual-risk slices and prioritisation.
- retained architecture assessment section in
Validation:
npm run lint-> warnings only (pre-existingreact-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)torequestJson({...}):deleteAwaitingSubmissionsFromBlobdeleteMyRepresentationsFromBlob
- Migrated delete helper GET calls from
axios.get(...).then(res.data)togetJson(...):deleteBlobdeleteRepBlob
- Preserved behavior contracts:
- unchanged query/hash composition and endpoint URLs
- unchanged catch-path logging with
consoleLogger
- Left
downloadBlobunchanged in this slice (special-case behavior path retained for separate focused handling).
Validation:
npm run lint-> warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: isolate and correct
downloadBlobbehavior indocumentDirectService(including legacyresusage) 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)indocumentDirectService:- removed legacy direct
axios.get(...).then(response => res.status(...))pattern that referenced undefinedresin service layer - now returns blob response data via
requestJson({ method: "get", url, responseType: "blob" }) - preserved catch-path logging (
consoleLogger)
- removed legacy direct
- Removed now-unused module-level
axiosimport fromdocumentDirectService.
Validation:
npm run lint-> warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: add a focused test (or integration harness assertion) around
downloadBlobservice 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 toaxios.get(...).then(res.data) - added default
requestJson(...)mock delegating toaxios(config).then(res.data) - added default
buildHashedQueryUrl(...)mock resolving hash via/api/endpoint/gethash_apicompatibility path
- added default
- Updated notify behavior assertions to align with shared request client usage (
requestJsoninvokes axios config-style call):- switched notify test handlers from
axios.postHandlertoaxios.requestHandler - assertions now inspect
axios.calls[0].config.{url,method,data}
- switched notify test handlers from
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
loadServiceModuledefault context intests/phase6/service-behaviour.test.cjs:- added default
getJson(...)mock backed byaxios.get(...).then(res.data) - added default
requestJson(...)mock backed byaxios(config).then(res.data)
- added default
- 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.cjswith reusable:createAxiosMockcreateLoggerMockcreateAxiosErrorloadServiceModule(with defaultgetJson/requestJson/buildHashedQueryUrlinjections)normalize
- Refactored
tests/phase6/service-behaviour.test.cjsto import shared harness utilities and remove duplicated local harness implementation. - Refactored
tests/phase7/service-behaviour.test.cjsto 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-existingreact-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.cjsto 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
getTokeninactions/core/token.js:- replaced direct
axios.post(...).then(res => res.data)chain with sharedrequestJson({...}) - migrated function to
async/awaitwith equivalenttry/catchbehavior - retained existing semantics:
- successful token payload cached in
cache.tokenResponse - failures logged via
consoleLoggerand returned to caller
- successful token payload cached in
- replaced direct
- Removed direct
axiosdependency fromactions/core/token.jsin favor ofactions/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-existingreact-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:
getTokensuccess path returns token payload and callsrequestJsonwith expected URL/method/body/headers- failure path logs via
consoleLoggerand 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-existingreact-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.cjsto exportrunand addrequire.main === moduleguard. - 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-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: add a dedicated
tests/phase22/index.test.cjsaggregate 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.cjsaggregate runner. - Runner currently executes
core-token-behaviour.test.cjsand prints a phase-level completion line. - Exported
runand 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-existingreact-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.getJsonreturnsaxios.get(...).dataendpointClient.requestJsonreturnsaxios(config).datarelayClient.buildHashedQueryUrlappends browser hash-service response- server fallback path uses
hashAPIPathwhenHASHKEYis present - browser/no-HASHKEY failure path rethrows hash-service error
- Updated
tests/phase22/index.test.cjsto 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-existingreact-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
fileClientfromactions/clients/index.js(and therefore viaactions/index.jsbarrel path). - Migrated a bounded subset of
actions/services/documentDirectService.jscall sites tofileClientwhile preserving catch-path logging behavior:getFilesFromBlobproxy->getFileJsondeleteAwaitingSubmissionsFromBlob->getSignedFileJsondeleteMyRepresentationsFromBlob->getSignedFileJsondeleteBlob->getFileJsondeleteRepBlob->getFileJsondownloadBlob->downloadFileBlob
- Added
tests/phase22/file-client-behaviour.test.cjscovering:- delegation to
getJson - signed URL generation +
requestJsoninvocation - blob download config contract (
responseType: "blob")
- delegation to
- Updated aggregate runner
tests/phase22/index.test.cjsto 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-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: evaluate whether additional
documentDirectServicefile-route call sites can adoptfileClientwithout 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.cjsto inject file-client compatible helpers when not explicitly provided:getFileJson(delegates to defaultgetJson)getSignedFileJson(signs viabuildHashedQueryUrlthen callsrequestJsonwith GET config)downloadFileBlob(callsrequestJsonwith GET +responseType: "blob")
- This preserves existing VM import-stripping strategy while preventing
ReferenceErrorin 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-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next bounded slice: add a focused phase6/phase7 behavioural assertion for
documentDirectService.downloadBlobto explicitly lock thedownloadFileBlobdelegation 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:
downloadBloblower-cases container in URL composition- request is issued through config-style request path (
axios.requestin harness) - request method is
get responseTypeisblob- 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-existingreact-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 suite’s 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.jswith:postSignedFileJson(queryUrl, data, config = {})- signs query URL via
buildHashedQueryUrland executes POST viarequestJson
- Migrated all remaining signed POST helper paths in
actions/services/documentDirectService.jstopostSignedFileJson:uploadFilesuploadSingleFileuploadRepFilesgenerateRepPDFgenerateAppealPDF
- 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-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next widened slice: evaluate consolidating remaining direct
getJsonfile-read flows indocumentDirectServicebehindfileClientfor 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
endpointClientusage fromdocumentDirectServicefor file reads. - Migrated remaining file-read/helper routes from
getJsontogetFileJson:getAwaitingSubmissionFromBlobgetRepsFromBlobgetRepsFromBlobProxygetAwaitingSubmissionFromBlobProxygetFilesFromBlobgetFilesFromBlobHashedgetProgressFromBlobcreateContainerProxy
- Removed now-unused imports from
documentDirectService:buildHashedQueryUrlgetJsonrequestJson
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-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next widened slice: introduce a small
fileClientURL-builder helper set for repeated query-string composition indocumentDirectService(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.jsto 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.cjsfor new helper symbols:buildFileQuerywithBaseUrlappendQuerySuffix
- Expanded phase22 utility coverage in
tests/phase22/client-utils-behaviour.test.cjswith 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-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next widened slice: evaluate applying
fileRouteBuilderto 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.jsto use shared route helpers:buildFileQuerywithBaseUrlappendQuerySuffix
- 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)
- read/list endpoints (
- 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-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next widened slice: apply the same query-normalization helpers in
caseDirectServiceand add dedicated phase22 behavioral assertions forportalDirectServiceroute-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.jsto usefileRouteBuilderhelpers:buildFileQuerywithBaseUrl
- 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)
- case retrieval/search flows (
- Preserved existing contracts:
- BASE_URL usage patterns where previously applied
- method/payload semantics for
requestJsonpaths - catch-path logging and return behavior (
logAndReturnResponse,consoleLogger,error.responsepaths)
- 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-existingreact-hooks/exhaustive-deps; no new lint errors)
Follow-ups:
- Optional next widened slice: add a focused phase22 behavioral suite for
caseDirectServiceand 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.cjswith focused behavioural assertions for:getPortalModuleDetailsBASE_URL + encoded case reference compositiongetPortalModuleDetailsProxyapostrophe escape behavior in case referencegetAppealIDquery composition and non-underscore value extractiongetAppealPDFDocumentfailure-path logging +error.responsepassthrough
- Updated
tests/phase22/index.test.cjsaggregate runner to includecase-service-behavioursuite.
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-existingreact-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
portalDirectServicewrite 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(...):createWatchedCasessetRepInvolvmentsetCaseInvolvment
- Added new phase22 suite
tests/phase22/portal-service-behaviour.test.cjscovering:- 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
sendRepCompleteMessagewhen hash-signing fails
- signed hash suffix append path in
- Updated
tests/phase22/index.test.cjsaggregate runner to includeportal-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-existingreact-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
- validates locale resolution precedence (
- 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
- asserts presence of required CY rewrite aliases for auth/policy routes in
- Updated
tests/phase22/index.test.cjsto 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-existingreact-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:
deleteWatchedCasesinactions/services/portalDirectService.js- from inline
buildHashedQueryUrl(...).then(requestJson(...))todeleteSignedJson(queryUrl) - preserved existing catch/log behavior (
consoleLogger+undefinedreturn on catch)
- Added test harness compatibility for VM import-stripping suites:
tests/serviceHarness.cjsnow injects defaultdeleteSignedJsonmock behavior.
- Expanded portal behavioral tests with explicit negative-path assertion:
tests/phase22/portal-service-behaviour.test.cjs- verifies
deleteWatchedCaseslogs and safely returnsundefinedwhen signed delete fails.
Validation:
npm run lint-> pass with warnings only (pre-existingreact-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
deleteMyRepresentationsfrom inlinebuildHashedQueryUrl(...).then(requestJson(...))to shareddeleteSignedJson(queryUrl, { headers }). - Preserved behavior-critical headers exactly:
OData-MaxVersionOData-VersionAcceptPreferContent-Type
- Preserved existing catch/log behavior (
consoleLoggerwith safe undefined return on failure).
Validation:
npm run lint-> pass with warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)node tests/phase22/index.test.cjs-> passnode 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 + requestJsonlogic in:getSignedFileJson-> now delegates togetSignedJsonpostSignedFileJson-> now delegates topostSignedJson
- retained
downloadFileBlobandgetFileJsonbehavior unchanged.
- replaced direct
- Updated
tests/phase22/file-client-behaviour.test.cjsto assert delegation contracts forgetSignedJsonandpostSignedJsonrather than direct signing internals.
Validation:
npm run lint-> pass with warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)node tests/phase22/index.test.cjs-> passnode 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 insendCaseCompleteMessage) 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
- added
- Migrated account signed GET candidate:
accountDirectService.getPortalLoginnow usesgetSignedJson(queryUrl, { baseUrl: BASE_URL })- preserved existing error semantics (
consoleLogger+JSON.stringify(error))
- Migrated portal signed GET candidates:
portalDirectService.sendRepCompleteMessagenow usesbuildSignedUrl(hashQueryPath)portalDirectService.sendCaseCompleteMessagenow usesbuildSignedUrl(hashQueryPath)+ existing signed suffix append behavior- preserved existing request method/URL shape and catch-path behavior
- Updated test harness and suites:
tests/serviceHarness.cjsnow provides defaults forbuildSignedUrl,getSignedJson,postSignedJsontests/phase22/portal-service-behaviour.test.cjsincludes assertion forsendRepCompleteMessagesigned-helper delegationtests/phase7/service-behaviour.test.cjsaccount portal-login expectations aligned to request-config path used by shared signed helper
Validation:
npm run lint-> pass with warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)node tests/phase22/index.test.cjs-> passnode 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 sharedbuildSignedUrl), 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)tofileRouteBuilder. - Migrated document service deterministic hash-suffix paths to new helper:
getAwaitingSubmissionFromBlobgetRepsFromBlobgetFilesFromBlobgetProgressFromBlobcreateContainerProxy
- Updated shared VM harness defaults (
tests/serviceHarness.cjs) to injectappendHashSuffix. - 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-existingreact-hooks/exhaustive-deps; no new lint errors)node tests/phase22/index.test.cjs-> passnode tests/phase7/service-behaviour.test.cjs-> pass (13/13)
Follow-ups:
- Remaining non-service candidate for this stream is
actions/azurestorage.jsdirecthashAPIPathmetadata 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:
getBlobsgetProgressBlobsgetRepsFilesBlobs
- Preserved existing behavior semantics for hash path construction:
- encoded
casefolderID/blobnamewhere previously encoded - unchanged
containerNameandcasefolderIDvalue sourcing - unchanged returned object field names and shape
- encoded
Validation:
npm run lint-> pass with warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)node tests/phase22/index.test.cjs-> passnode tests/phase7/service-behaviour.test.cjs-> pass (13/13)
Follow-ups:
- Remaining potential cleanups in
actions/azurestorage.jsare 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:
getBlobsgetProgressBlobsgetRepsFilesBlobs
- Preserved existing metadata contracts:
- keys unchanged:
hashedfilepath,hasheddeletepath,hashgetblobs - same encoded query path inputs and route semantics.
- keys unchanged:
Validation:
npm run lint-> pass with warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)node tests/phase22/index.test.cjs-> passnode tests/phase7/service-behaviour.test.cjs-> pass (13/13)
Follow-ups:
- Any further
azurestorage.jscleanup 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.jsand verifies:buildDownloadBlobQueryPathdefault encoding outputbuildDeleteBlobQueryPathnon-encoded option behaviorbuildGetBlobListQueryPathquery output contractbuildHashMetadataPathskey/value shape (hashedfilepath,hasheddeletepath,hashgetblobs)
- Wired suite into aggregate runner
tests/phase22/index.test.cjs.
Validation:
npm run lint-> pass with warnings only (pre-existingreact-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
casefolderIDvariants 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:blobPathPartsfileName/appealBlobName/repFileNamecasefolderPath
- 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-existingreact-hooks/exhaustive-deps; no new lint errors)node tests/phase22/index.test.cjs-> passnode 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:
getProgressBlobsgetRepsFilesBlobs
- Replaced selected direct
console.log(...)calls withconsoleLogger(...):- progress blob path-parts trace
- per-blob name trace in reps file listing
- final
blobObjtrace 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-existingreact-hooks/exhaustive-deps; no new lint errors)node tests/phase22/index.test.cjs-> passnode tests/phase7/service-behaviour.test.cjs-> pass (13/13)
Follow-ups:
- Optional next bounded slice: prune currently-unused local
blobDocumentTypevariables 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
blobDocumentTypedeclarations from:getBlobsgetRepsFilesBlobs
- No object shape, query generation, hash metadata logic, or routing behavior changed.
Validation:
npm run lint-> pass with warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)node tests/phase22/index.test.cjs-> passnode tests/phase7/service-behaviour.test.cjs-> pass (13/13)
Follow-ups:
- Optional next bounded slice: align remaining low-risk direct
console.logcalls in these functions toconsoleLoggeronly 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:
getBlobsgetRepsFilesBlobs
- Replaced inline
filepathstring concatenation withbuildDownloadBlobQueryPath(...). - Replaced inline
deletepathstring concatenation withbuildDeleteBlobQueryPath(...). - Preserved previous encoding behavior where required by passing explicit options:
- kept non-encoded
casefolderID/blobnamebehavior ingetRepsFilesBlobs.deletepathvia helper options.
- kept non-encoded
Validation:
npm run lint-> pass with warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)node tests/phase22/index.test.cjs-> passnode 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
caseObjstring assembly in touched functions:getBlobsgetProgressBlobs
- Preserved existing
caseObjoutput format (<caseRef>/<caseRef>_case.json).
Validation:
npm run lint-> pass with warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)node tests/phase22/index.test.cjs-> passnode 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:
getBlobsgetProgressBlobsgetRepsFilesBlobs
- Added local
contentLengthvariable (blob.properties.contentLength) per loop iteration. - Replaced repeated inline
blob.properties.contentLengthassignments in object assembly with the local variable. - Preserved field contracts (
contentLength,size) and values.
Validation:
npm run lint-> pass with warnings only (pre-existingreact-hooks/exhaustive-deps; no new lint errors)node tests/phase22/index.test.cjs-> passnode 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 = 10inuploadsinglefileAPI 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
dataarray for response
- Kept existing error handling unchanged (
UPLOAD_SINGLE_FILE_FAILEDon 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
uploadSingleFileinvocations 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 = 10to document direct service. - Updated
uploadSingleFile(filesObj, containerID, casefolderID)to:- split selected files into batches of 10
- create a fresh
FormDataper batch - call
/api/file/uploadsinglefileonce per batch (sequentialawait) - aggregate
dataandinvalidFilesfrom 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
uploadSingleFileissues 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
optionsargument - invokes
options.onChunkComplete(...)after each chunk upload - emits cumulative counters (
cumulativeUploaded,cumulativeInvalid,processedFiles, chunk index metadata)
- new
- Wired per-chunk UI progress updates in both upload entry points:
components/elements/index.jscomponents/case/representation/representationElements.js
- Both now pass
onChunkCompleteand updateuploadCountMessagefrom 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
totalUploadFilesstate in both upload UIs (new appeal + representations). - In-progress upload message now renders as:
${uploadCountMessage} of ${totalUploadFiles}
- Upload start now initializes as
0 of Yand 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-ytranslation 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
renderFieldhad no usages outside its declaration. - Removed the unused local
renderFieldfunction fromcomponents/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 usesuseRouter) was invoked within conditional render branches inMultiLinefield, causing hook order mismatch when branch conditions changed. - Fix: precomputed
translatedLabelonce inMultiLinefieldrender 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 renderingRenderSubFields. - Fix: moved initial row insertion into
useEffect, guarded byfields.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/removecalls 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
useEffectguarded by!showIfHasParentShowValue. - Added
useEffectimport incomponents/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/submittingdestructure
- Kept visibility logic, effect-driven clearing behavior, and
FieldArrayrendering 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.jsand moved the full existingRenderFileUploadimplementation unchanged. - Added
components/elements/fields/fieldArrayForm.jsand moved the full existingFieldArrayFormimplementation unchanged. - Updated
components/elements/index.jsimports to consume extracted modules. - Removed inline
RenderFileUpload/FieldArrayFormimplementations and related now-unused imports/constants fromindex.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 repeatedrequired/emoji/postcodemessage 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.jsand moved the existingPickListwrapper implementation. - Updated
components/elements/index.jsto import/exportPickListfrom the new field module. - Removed inline
PickListwrapper implementation fromindex.js. - Removed now-unused
RenderPickListimport fromindex.jsafter 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,setFileCountaction import, redux hooks import) - unused local validators and helpers in wrappers (e.g., unused
required/postcode/normalizeDecimalvariants) - unused placeholder constant
RenderCaseID - unused local fallbacks in
FileUploadField(uploadCount, localsetFileCount)
- top-level unused imports (
- 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.jsand movedCheckBoxfieldwrapper. - Added
components/elements/fields/dateFieldPicker.jsand movedDateFieldPickerwrapper logic. - Added
components/elements/fields/yesNoField.jsand movedYesNofieldwrapper logic. - Updated
components/elements/index.jsto import/export these wrappers from field modules. - Removed inline implementations of
CheckBoxfield,DateFieldPicker, andYesNofieldfromindex.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.jsforRadiofield. - Added
components/elements/fields/numericField.jsforNumericField. - Added
components/elements/fields/decimalField.jsforDecimalField. - Updated
components/elements/index.jsto import/export these wrappers from field modules. - Removed inline
Radiofield,NumericField, andDecimalFieldimplementations fromindex.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.