Files
Robert Bond c39e4bcc9a Merged PR 2375: update representation policy
## Summary

Introduces a new `representation-policy` domain boundary and incrementally extracts low-risk representation entry policy logic while preserving existing behaviour.

This PR intentionally stops before extracting ROW and Advert entry rules because characterization uncovered behavioural differences between consumers that require a separate business decision.

## What Changed

Added:

```text
lib/domain/representation-policy/
```

Including:

- `resolveRepresentationWindow(...)`
- `isRepresentationWindowOpen(...)`
- `isRepresentationWindowClosed(...)`
- `canShowRepresentationButtonForAppealType(...)`
- `canStartHouseholderRepresentation(...)`
- `canStartCpoRepresentation(...)`

Updated consumers:

```text
components/case/summary/utils/representationEntry.js
components/search/repsonresults.js
```

## Extracted Behaviour

### Representation Window Calculation
Centralised shared representation window logic and adopted it in both consumers.

### Appeal Type Entry Gating
Centralised excluded appeal-type logic while preserving existing behaviour.

### Householder Rule
Centralised Householder (`846040004`) entry rule.

Preserved behaviour:

```text
Householder representations can only be started by LPAs.
```

### CPO Rule
Centralised CPO (`846040019`) entry rule.

Preserved behaviour using:

```text
pinswg_startdate
pinswg_statementduedate
```

No fallback date broadening introduced.

## Characterization Added

### ROW (`846040015`)
Documented:
- hearing vs non-hearing behaviour
- specialist-process field behaviour
- date gating
- appeal-type coercion behaviour

### Advert (`846040018`)
Documented:
- LPA/non-LPA behaviour
- specialist-process behaviour
- appeal-type coercion behaviour

## Important Findings

### ROW Divergence
Summary and Search consumers currently behave differently when only:

```text
pinswg_speacialistcaseprocess
```

exists.

### Advert Divergence
Summary and Search consumers currently use different specialist-process resolution paths.

### CRM Compatibility
Both fields remain in production use and must be preserved:

```text
pinswg_specialistcaseprocess
pinswg_speacialistcaseprocess
```

## Documentation

Added:

```text
lib/domain/representation-policy/README.md
```

Documenting:
- ownership
- non-goals
- CRM compatibility requirements
- ROW divergence
- Advert divergence
- future extraction constraints

## Validation

Executed during the slice series:

```bash
node tests/phase22/representation-window.test.cjs
node tests/phase22/representation-appeal-type-entry-gating.test.cjs
node tests/phase22/representation-householder-entry-rule.test.cjs
node tests/phase22/representation-cpo-entry-rule.test.cjs
node tests/phase22/representation-row-entry-rule.test.cjs
node tests/phase22/representation-advert-entry-rule.test.cjs
npm run lint
```

All passing.

## Out of Scope

No changes to:

- submission/finalisation
- uploads
- dashboards
- CRM/OData queries
- API routes
- Redux state
- translations
- blocked-message rendering
- CTA l...
2026-06-08 10:51:02 +00:00

4.6 KiB

Representation Entry Policy Boundary

Purpose

This directory contains a narrow representation entry policy boundary.

It currently owns only behaviour-preserving extracted entry-policy helpers.

It is not a full representation domain model.

This boundary should be treated as a small refactor seam for read-only entry-policy interpretation, not as a place to redesign representation behaviour.

Current ownership

resolveRepresentationWindow(...)

  • current general representation window resolution
  • preserves current start/end field usage for the extracted general window path
  • does not define full representation entry policy

isRepresentationWindowOpen(...)

  • current inclusive date-window open check
  • preserves current date semantics used by extracted consumers

isRepresentationWindowClosed(...)

  • current date-window closed check
  • preserves current date > start && date > end semantics

canShowRepresentationButtonForAppealType(...)

  • current appeal-type exclusion list only
  • preserves current excluded IDs
  • preserves current permissive unknown behaviour
  • preserves current string/number coercion behaviour

canStartHouseholderRepresentation(...)

  • current Householder LPA-only entry rule only
  • preserves current truthy/falsy LPA handling
  • does not infer LPA from CRM fields

canStartCpoRepresentation(...)

  • current CPO entry rule only
  • preserves current pinswg_startdatepinswg_statementduedate behaviour
  • does not broaden to other start-date variants

Explicit non-goals

This boundary does not currently own:

  • ROW entry extraction
  • Advert entry extraction
  • blocked-message rendering
  • CTA label selection
  • consultation entry policy
  • representation type options
  • capacity rules
  • submission/finalisation rules
  • upload logic
  • dashboard/worklist grouping
  • CRM/OData queries
  • API routes
  • Redux state
  • translations / EN-CY text

CRM compatibility requirements

Both specialist-process fields may exist and must be handled carefully:

  • pinswg_specialistcaseprocess
  • pinswg_speacialistcaseprocess

The misspelled field exists because of historical CRM schema/data and must not be removed casually.

Start-date variants also exist and must not be normalized unless explicitly characterized:

  • pinswg_startdate
  • pinswg_startdates
  • pinswg_applicationacceptedasvalid

Future slices must treat field compatibility as behavior-sensitive.

Known divergences

ROW 846040015

Summary consumer (components/case/summary/utils/representationEntry.js):

  • uses only pinswg_specialistcaseprocess in the ROW branch
  • if only pinswg_speacialistcaseprocess exists, it falls through to non-hearing behaviour
  • result can be allowed where search would be date-gated

Search consumer (components/search/repsonresults.js):

  • uses normalized canonical/misspelled specialist-process fallback
  • if misspelled field contains hearing value 846040001, hearing/date gate applies

Also:

  • ROW hearing uses pinswg_startdatepinswg_finalcommentsduedate
  • ROW does not currently use pinswg_startdates or pinswg_applicationacceptedasvalid
  • string appeal type "846040015" falls through to default allowed behaviour

Advert 846040018

Summary consumer (components/case/summary/utils/representationEntry.js):

  • reads only pinswg_speacialistcaseprocess
  • ignores canonical-only specialist process values in the Advert branch

Search consumer (components/search/repsonresults.js):

  • uses normalized specialist process fallback
  • canonical field takes precedence when both fields exist

Also:

  • written reps 846040000 is LPA-only
  • hearing 846040001 is allowed for LPA and non-LPA
  • other specialist process values are blocked
  • Advert is not date-gated today
  • string appeal type "846040018" falls through to default allowed behaviour

Behaviour-preservation invariants

Future slices must preserve:

  • current consumer-specific divergence unless an explicit behaviour-change decision is made
  • current string/number appeal type handling
  • current specialist-process field precedence where present
  • current start-date field usage
  • current permissive unknown behaviour
  • current CTA/rendering behaviour

Future extraction guidance

Do not extract ROW or Advert helpers until product/CRM decision confirms whether to preserve divergence or standardize behaviour.

If preserving divergence:

  • helper contracts must support consumer-specific mode explicitly

If standardizing behaviour:

  • it must be treated as a behaviour change, not a refactor

The next safe work is likely blocked/suppression rendering characterization, not extraction.