Repository: ministryofjustice/hmpps-incident-reporting-api
Pull request #299: IR-959: Type families
Author: login: ushkarev
Created: 2025-04-01T11:41:27Z | Merged: 2025-04-02T13:03:23Z

Incident types are now grouped into families which share the same description.

Type codes have been renamed to stick to a sensible pattern: `[family-code]_[version]`

```
ABSCONDER → ABSCOND_1
ASSAULT → ASSAULT_5
ATTEMPTED_ESCAPE_FROM_CUSTODY → ATTEMPTED_ESCAPE_FROM_PRISON_1
ATTEMPTED_ESCAPE_FROM_ESCORT → ATTEMPTED_ESCAPE_FROM_ESCORT_1
BOMB_THREAT → BOMB_1
BREACH_OF_SECURITY → BREACH_OF_SECURITY_1
DAMAGE → DAMAGE_1
DEATH_IN_CUSTODY → DEATH_PRISONER_1
DEATH_OTHER → DEATH_OTHER_1
DISORDER → DISORDER_2
DRONE_SIGHTING → DRONE_SIGHTING_3
ESCAPE_FROM_CUSTODY → ESCAPE_FROM_PRISON_1
ESCAPE_FROM_ESCORT → ESCAPE_FROM_ESCORT_1
FINDS → FIND_6
FIRE → FIRE_1
FOOD_REFUSAL → FOOD_REFUSAL_1
FULL_CLOSE_DOWN_SEARCH → CLOSE_DOWN_SEARCH_1
KEY_LOCK_INCIDENT → KEY_OR_LOCK_2
MISCELLANEOUS → MISCELLANEOUS_1
OLD_ASSAULT → ASSAULT_1
OLD_ASSAULT1 → ASSAULT_2
OLD_ASSAULT2 → ASSAULT_3
OLD_ASSAULT3 → ASSAULT_4
OLD_BARRICADE → BARRICADE_1
OLD_CONCERTED_INDISCIPLINE → CONCERTED_INDISCIPLINE_1
OLD_DISORDER → DISORDER_1
OLD_DRONE_SIGHTING → DRONE_SIGHTING_1
OLD_DRONE_SIGHTING1 → DRONE_SIGHTING_2
OLD_DRUGS → DRUGS_1
OLD_FINDS → FIND_1
OLD_FINDS1 → FIND_2
OLD_FINDS2 → FIND_3
OLD_FINDS3 → FIND_4
OLD_FINDS4 → FIND_5
OLD_FIREARM_ETC → FIREARM_1
OLD_HOSTAGE → HOSTAGE_1
OLD_KEY_LOCK_INCIDENT → KEY_OR_LOCK_1
OLD_MOBILES → MOBILE_PHONE_1
OLD_ROOF_CLIMB → INCIDENT_AT_HEIGHT_1
OLD_TEMPORARY_RELEASE_FAILURE → TEMPORARY_RELEASE_FAILURE_1
OLD_TEMPORARY_RELEASE_FAILURE1 → TEMPORARY_RELEASE_FAILURE_2
OLD_TEMPORARY_RELEASE_FAILURE2 → TEMPORARY_RELEASE_FAILURE_3
RADIO_COMPROMISE → RADIO_COMPROMISE_1
RELEASED_IN_ERROR → RELEASE_IN_ERROR_1
SELF_HARM → SELF_HARM_1
TEMPORARY_RELEASE_FAILURE → TEMPORARY_RELEASE_FAILURE_4
TOOL_LOSS → TOOL_LOSS_1
```


Diff:
diff --git a/docs/0004-modeling-irs.md b/docs/0004-modeling-irs.md
index e71bfc6e..8f485823 100644
--- a/docs/0004-modeling-irs.md
+++ b/docs/0004-modeling-irs.md
@@ -2,7 +2,6 @@
 
 [Next >>](9999-end.md)
 
-
 Date: 2024-03-04
 
 ## Status
@@ -16,248 +15,280 @@ This document details the new design of Incident Reports that will support old N
 
 ```mermaid
 classDiagram
-    direction BT
-    class CorrectionRequest {
-        Long  id
-        LocalDateTime  correctionRequestedAt
-        String  correctionRequestedBy
-        String  descriptionOfChange
-        int  sequence
-    }
-    class Event {
-        UUID  id
-        LocalDateTime  createdAt
-        String  description
-        LocalDateTime  eventDateAndTime
-        String  eventReference
-        String  location
-        LocalDateTime  modifiedAt
-        String  modifiedBy
-        String  title
-    }
-    class HistoricalQuestion {
-        Long  id
-        String  additionalInformation
-        String  code
-        String  question
-        int  sequence
-    }
-    class HistoricalResponse {
-        Long  id
-        String  additionalInformation
-        LocalDateTime  recordedAt
-        String  recordedBy
-        String  response
-        LocalDate  responseDate
-        int  sequence
-    }
-    class History {
-        Long  id
-        LocalDateTime  changedAt
-        String  changedBy
-        Type  type
-    }
-    class PrisonerInvolvement {
-        Long  id
-        String  comment
-        String  firstName
-        String  lastName
-        PrisonerOutcome  outcome
-        String  prisonerNumber
-        PrisonerRole  prisonerRole
-        int  sequence
-    }
-    class Question {
-        Long  id
-        String  additionalInformation
-        String  code
-        String  question
-        int  sequence
-    }
-    class Report {
-        UUID  id
-        String  assignedTo
-        LocalDateTime  createdAt
-        String  description
-        LocalDateTime  incidentDateAndTime
-        String  location
-        LocalDateTime  modifiedAt
-        String  modifiedBy
-        InformationSource  modifiedIn
-        boolean  prisonerInvolvementDone
-        String  questionSetId
-        String  reportReference
-        LocalDateTime  reportedAt
-        String  reportedBy
-        InformationSource  source
-        boolean  staffInvolvementDone
-        Status  status
-        String  title
-        Type  type
-    }
-    class Response {
-        Long  id
-        String  additionalInformation
-        LocalDateTime  recordedAt
-        String  recordedBy
-        String  response
-        LocalDate  responseDate
-        int  sequence
-    }
-    class StaffInvolvement {
-        Long  id
-        String  comment
-        String  firstName
-        String  lastName
-        int  sequence
-        StaffRole  staffRole
-        String  staffUsername
-    }
-    class StatusHistory {
-        Long  id
-        LocalDateTime  changedAt
-        String  changedBy
-        Status  status
-    }
+  direction BT
+  class CorrectionRequest {
+    Long id
+    LocalDateTime correctionRequestedAt
+    String correctionRequestedBy
+    String descriptionOfChange
+    int sequence
+  }
+  class Event {
+    UUID id
+    LocalDateTime createdAt
+    String description
+    LocalDateTime eventDateAndTime
+    String eventReference
+    String location
+    LocalDateTime modifiedAt
+    String modifiedBy
+    String title
+  }
+  class HistoricalQuestion {
+    Long id
+    String additionalInformation
+    String code
+    String question
+    int sequence
+  }
+  class HistoricalResponse {
+    Long id
+    String additionalInformation
+    LocalDateTime recordedAt
+    String recordedBy
+    String response
+    LocalDate responseDate
+    int sequence
+  }
+  class History {
+    Long id
+    LocalDateTime changedAt
+    String changedBy
+    Type type
+  }
+  class PrisonerInvolvement {
+    Long id
+    String comment
+    String firstName
+    String lastName
+    PrisonerOutcome outcome
+    String prisonerNumber
+    PrisonerRole prisonerRole
+    int sequence
+  }
+  class Question {
+    Long id
+    String additionalInformation
+    String code
+    String question
+    int sequence
+  }
+  class Report {
+    UUID id
+    String assignedTo
+    LocalDateTime createdAt
+    String description
+    LocalDateTime incidentDateAndTime
+    String location
+    LocalDateTime modifiedAt
+    String modifiedBy
+    InformationSource modifiedIn
+    boolean prisonerInvolvementDone
+    String questionSetId
+    String reportReference
+    LocalDateTime reportedAt
+    String reportedBy
+    InformationSource source
+    boolean staffInvolvementDone
+    Status status
+    String title
+    Type type
+  }
+  class Response {
+    Long id
+    String additionalInformation
+    LocalDateTime recordedAt
+    String recordedBy
+    String response
+    LocalDate responseDate
+    int sequence
+  }
+  class StaffInvolvement {
+    Long id
+    String comment
+    String firstName
+    String lastName
+    int sequence
+    StaffRole staffRole
+    String staffUsername
+  }
+  class StatusHistory {
+    Long id
+    LocalDateTime changedAt
+    String changedBy
+    Status status
+  }
 
-CorrectionRequest "0..*" <--> "1" Report
-HistoricalQuestion "1" <--> "1..*" HistoricalResponse
-HistoricalQuestion "0..*" <--> "1" History
-PrisonerInvolvement "0..*" <--> "1" Report
-Question "0..*" <--> "1" Report
-Question "1" <--> "1..*" Response
-Report "1..*" <--> "1" Event
-Report "1" <--> "0..*" History
-Report "1" <--> "0..*" StaffInvolvement
-StatusHistory "1..*" <--> "1" Report
+  CorrectionRequest "0..*" <--> "1" Report
+  HistoricalQuestion "1" <--> "1..*" HistoricalResponse
+  HistoricalQuestion "0..*" <--> "1" History
+  PrisonerInvolvement "0..*" <--> "1" Report
+  Question "0..*" <--> "1" Report
+  Question "1" <--> "1..*" Response
+  Report "1..*" <--> "1" Event
+  Report "1" <--> "0..*" History
+  Report "1" <--> "0..*" StaffInvolvement
+  StatusHistory "1..*" <--> "1" Report
 ```
 
 ### DB Schema
 
 ```mermaid
 classDiagram
-    direction BT
-    class correction_request {
-        uuid report_id
-        text description_of_change
-        timestamp correction_requested_at
-        varchar(120) correction_requested_by
-        integer sequence
-        integer id
-    }
-    class event {
-        varchar(25) event_reference
-        timestamp event_date_and_time
-        varchar(255) title
-        text description
-        varchar(20) location
-        timestamp created_at
-        varchar(120) modified_by
-        timestamp modified_at
-        uuid id
-    }
-    class historical_question {
-        integer history_id
-        integer sequence
-        varchar(60) code
-        text question
-        text additional_information
-        integer id
-    }
-    class historical_response {
-        integer historical_question_id
-        integer sequence
-        text response
-        date response_date
-        text additional_information
-        timestamp recorded_at
-        varchar(120) recorded_by
-        integer id
-    }
-    class history {
-        uuid report_id
-        varchar(60) type
-        timestamp changed_at
-        varchar(120) changed_by
-        integer id
-    }
-    class prisoner_involvement {
-        uuid report_id
-        varchar(7) prisoner_number
-        varchar(60) prisoner_role
-        varchar(60) outcome
-        text comment
-        integer sequence
-        varchar(255) first_name
-        varchar(255) last_name
-        integer id
-    }
-    class question {
-        uuid report_id
-        integer sequence
-        varchar(60) code
-        text question
-        text additional_information
-        integer id
-    }
-    class report {
-        uuid event_id
-        varchar(25) report_reference
-        varchar(255) title
-        text description
-        varchar(20) location
-        varchar(60) type
-        varchar(5) source
-        varchar(5) modified_in
-        varchar(60) status
-        timestamp incident_date_and_time
-        timestamp reported_at
-        varchar(120) reported_by
-        varchar(120) assigned_to
-        varchar(20) question_set_id
-        timestamp created_at
-        varchar(120) modified_by
-        timestamp modified_at
-        boolean staff_involvement_done
-        boolean prisoner_involvement_done
-        uuid id
-    }
-    class response {
-        integer question_id
-        integer sequence
-        text response
-        date response_date
-        text additional_information
-        timestamp recorded_at
-        varchar(120) recorded_by
-        integer id
-    }
-    class staff_involvement {
-        uuid report_id
-        varchar(120) staff_username
-        varchar(60) staff_role
-        text comment
-        integer sequence
-        varchar(255) first_name
-        varchar(255) last_name
-        integer id
-    }
-    class status_history {
-        uuid report_id
-        varchar(60) status
-        timestamp changed_at
-        varchar(120) changed_by
-        integer id
-    }
+  direction BT
+  class constant_prisoner_outcome {
+    integer sequence
+    varchar(60) description
+    varchar(60) code
+  }
+  class constant_prisoner_role {
+    integer sequence
+    varchar(60) description
+    varchar(60) code
+  }
+  class constant_staff_role {
+    integer sequence
+    varchar(60) description
+    varchar(60) code
+  }
+  class constant_status {
+    integer sequence
+    varchar(60) description
+    varchar(60) code
+  }
+  class constant_type {
+    integer sequence
+    varchar(60) description
+    boolean active
+    varchar(60) family_code
+    varchar(60) code
+  }
+  class constant_type_family {
+    integer sequence
+    varchar(60) description
+    varchar(60) code
+  }
+  class correction_request {
+    uuid report_id
+    text description_of_change
+    timestamp correction_requested_at
+    varchar(120) correction_requested_by
+    integer sequence
+    integer id
+  }
+  class event {
+    varchar(25) event_reference
+    timestamp event_date_and_time
+    varchar(255) title
+    text description
+    varchar(20) location
+    timestamp created_at
+    varchar(120) modified_by
+    timestamp modified_at
+    uuid id
+  }
+  class historical_question {
+    integer history_id
+    integer sequence
+    varchar(60) code
+    text question
+    text additional_information
+    integer id
+  }
+  class historical_response {
+    integer historical_question_id
+    integer sequence
+    text response
+    date response_date
+    text additional_information
+    timestamp recorded_at
+    varchar(120) recorded_by
+    integer id
+  }
+  class history {
+    uuid report_id
+    varchar(60) type
+    timestamp changed_at
+    varchar(120) changed_by
+    integer id
+  }
+  class prisoner_involvement {
+    uuid report_id
+    varchar(7) prisoner_number
+    varchar(60) prisoner_role
+    varchar(60) outcome
+    text comment
+    integer sequence
+    varchar(255) first_name
+    varchar(255) last_name
+    integer id
+  }
+  class question {
+    uuid report_id
+    integer sequence
+    varchar(60) code
+    text question
+    text additional_information
+    integer id
+  }
+  class report {
+    uuid event_id
+    varchar(25) report_reference
+    varchar(255) title
+    text description
+    varchar(20) location
+    varchar(60) type
+    varchar(5) source
+    varchar(5) modified_in
+    varchar(60) status
+    timestamp incident_date_and_time
+    timestamp reported_at
+    varchar(120) reported_by
+    varchar(120) assigned_to
+    varchar(20) question_set_id
+    timestamp created_at
+    varchar(120) modified_by
+    timestamp modified_at
+    boolean staff_involvement_done
+    boolean prisoner_involvement_done
+    uuid id
+  }
+  class response {
+    integer question_id
+    integer sequence
+    text response
+    date response_date
+    text additional_information
+    timestamp recorded_at
+    varchar(120) recorded_by
+    integer id
+  }
+  class staff_involvement {
+    uuid report_id
+    varchar(120) staff_username
+    varchar(60) staff_role
+    text comment
+    integer sequence
+    varchar(255) first_name
+    varchar(255) last_name
+    integer id
+  }
+  class status_history {
+    uuid report_id
+    varchar(60) status
+    timestamp changed_at
+    varchar(120) changed_by
+    integer id
+  }
 
-correction_request  -->  report : report_id
-historical_question  -->  history : history_id
-historical_response  -->  historical_question : historical_question_id
-history  -->  report : report_id
-prisoner_involvement  -->  report : report_id
-question  -->  report : report_id
-report  -->  event : event_id
-response  -->  question : question_id
-staff_involvement  -->  report : report_id
-status_history  -->  report : report_id
+  correction_request --> report: report_id
+  historical_question --> history: history_id
+  historical_response --> historical_question: historical_question_id
+  history --> report: report_id
+  prisoner_involvement --> report: report_id
+  question --> report: report_id
+  report --> event: event_id
+  response --> question: question_id
+  staff_involvement --> report: report_id
+  status_history --> report: report_id
 ```
diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/constants/Status.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/constants/Status.kt
index eda2d436..e1c8edbc 100644
--- a/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/constants/Status.kt
+++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/constants/Status.kt
@@ -19,7 +19,7 @@ enum class Status(
   AWAITING_ANALYSIS("Awaiting analysis", "AWAN"),
   IN_ANALYSIS("In analysis", "INAN"),
   INFORMATION_REQUIRED("Information required", "INREQ"),
-  INFORMATION_AMENDED("Information amened", "INAME"),
+  INFORMATION_AMENDED("Information amended", "INAME"),
   CLOSED("Closed", "CLOSE"),
   POST_INCIDENT_UPDATE("Post-incident update", "PIU"),
   INCIDENT_UPDATED("Incident updated", "IUP"),
diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/constants/Type.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/constants/Type.kt
index 78358977..2a13a53d 100644
--- a/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/constants/Type.kt
+++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/constants/Type.kt
@@ -2,74 +2,72 @@ package uk.gov.justice.digital.hmpps.incidentreporting.constants
 
 import jakarta.validation.ValidationException
 
-// TODO: need to check start/end dates for all types in NOMIS db to ensure descriptions make sense. eg. ASSAULT*
-
 /**
  * The type of reportable incident.
  *
  * NB:
- *   - new items should have a reasonably readable code
- *   - items cannot be removed, only deactivated, to preserve database integrity
+ *   - new types should use their family code and increment the suffix
+ *   - existing types cannot be removed, only deactivated, to preserve database integrity
  *   - any additions, changes to order, codes or descriptions require a new migration of relevant constants DB table!
- *   - code & description are expected to be 60 chars max
+ *   - code is expected to be 60 chars max
+ *   - more than one active type can exist in a family
  */
 enum class Type(
-  val description: String,
+  val typeFamily: TypeFamily,
   val nomisType: String?,
   val active: Boolean = true,
 ) {
-  // active
-  ABSCONDER("Absconder", "ABSCOND"),
-  ASSAULT("Assault", "ASSAULTS3"),
-  ATTEMPTED_ESCAPE_FROM_CUSTODY("Attempted escape from custody", "ATT_ESCAPE"),
-  ATTEMPTED_ESCAPE_FROM_ESCORT("Attempted escape from escort", "ATT_ESC_E"),
-  BOMB_THREAT("Bomb threat", "BOMB"),
-  BREACH_OF_SECURITY("Breach of security", "BREACH"),
-  DEATH_IN_CUSTODY("Death in custody", "DEATH"),
-  DEATH_OTHER("Death (other)", "DEATH_NI"),
-  DISORDER("Disorder", "DISORDER1"),
-  DRONE_SIGHTING("Drone sighting", "DRONE2"),
-  ESCAPE_FROM_CUSTODY("Escape from custody", "ESCAPE_EST"),
-  ESCAPE_FROM_ESCORT("Escape from escort", "ESCAPE_ESC"),
-  FINDS("Finds", "FIND0422"),
-  FIRE("Fire", "FIRE"),
-  FOOD_REFUSAL("Food refusal", "FOOD_REF"),
-  FULL_CLOSE_DOWN_SEARCH("Full close down search", "CLOSE_DOWN"),
-  KEY_LOCK_INCIDENT("Key lock incident", "KEY_LOCKNEW"),
-  MISCELLANEOUS("Miscellaneous", "MISC"),
-  RADIO_COMPROMISE("Radio compromise", "RADIO_COMP"),
-  RELEASED_IN_ERROR("Released in error", "REL_ERROR"),
-  SELF_HARM("Self harm", "SELF_HARM"),
-  TEMPORARY_RELEASE_FAILURE("Temporary release failure", "TRF3"),
-  TOOL_LOSS("Tool loss", "TOOL_LOSS"),
-
-  // inactive
-  DAMAGE("Damage", "DAMAGE", active = false),
-  OLD_ASSAULT("Assault", "ASSAULT", active = false),
-  OLD_ASSAULT1("Assault (from April 2017)", "ASSAULTS", active = false),
-  OLD_ASSAULT2("Assault (from April 2017)", "ASSAULTS1", active = false),
-  OLD_ASSAULT3("Assault (from April 2017)", "ASSAULTS2", active = false),
-  OLD_BARRICADE("Barricade/prevention of access", "BARRICADE", active = false),
-  OLD_CONCERTED_INDISCIPLINE("Concerted indiscipline", "CON_INDISC", active = false),
-  OLD_DISORDER("Disorder", "DISORDER", active = false),
-  OLD_DRONE_SIGHTING("Drone sighting", "DRONE", active = false),
-  OLD_DRONE_SIGHTING1("Drone sighting (from 2017)", "DRONE1", active = false),
-  OLD_DRUGS("Drugs", "DRUGS", active = false),
-  OLD_FINDS("Finds", "FINDS", active = false),
-  OLD_FINDS1("Finds (from August 2015)", "FIND", active = false),
-  OLD_FINDS2("Finds (from September 2015)", "FIND1", active = false),
-  OLD_FINDS3("Finds (from March 2022)", "FIND0322", active = false),
-  OLD_FINDS4("Finds (from September 2016)", "FINDS1", active = false),
-  OLD_FIREARM_ETC("Firearm/ammunition/chemical incapacitant", "FIREARM_ETC", active = false),
-  OLD_HOSTAGE("Hostage", "HOSTAGE", active = false),
-  OLD_KEY_LOCK_INCIDENT("Key lock incident", "KEY_LOCK", active = false),
-  OLD_MOBILES("Mobile phones", "MOBILES", active = false),
-  OLD_ROOF_CLIMB("Incident at height", "ROOF_CLIMB", active = false),
-  OLD_TEMPORARY_RELEASE_FAILURE("Temporary release failure", "TRF", active = false),
-  OLD_TEMPORARY_RELEASE_FAILURE1("Temporary release failure (from July 2015)", "TRF1", active = false),
-  OLD_TEMPORARY_RELEASE_FAILURE2("Temporary release failure (from April 2016)", "TRF2", active = false),
+  ABSCOND_1(TypeFamily.ABSCOND, "ABSCOND"),
+  ASSAULT_1(TypeFamily.ASSAULT, "ASSAULT", active = false),
+  ASSAULT_2(TypeFamily.ASSAULT, "ASSAULTS", active = false),
+  ASSAULT_3(TypeFamily.ASSAULT, "ASSAULTS1", active = false),
+  ASSAULT_4(TypeFamily.ASSAULT, "ASSAULTS2", active = false),
+  ASSAULT_5(TypeFamily.ASSAULT, "ASSAULTS3"),
+  ATTEMPTED_ESCAPE_FROM_PRISON_1(TypeFamily.ATTEMPTED_ESCAPE_FROM_PRISON, "ATT_ESCAPE"),
+  ATTEMPTED_ESCAPE_FROM_ESCORT_1(TypeFamily.ATTEMPTED_ESCAPE_FROM_ESCORT, "ATT_ESC_E"),
+  BARRICADE_1(TypeFamily.BARRICADE, "BARRICADE", active = false),
+  BOMB_1(TypeFamily.BOMB, "BOMB"),
+  BREACH_OF_SECURITY_1(TypeFamily.BREACH_OF_SECURITY, "BREACH"),
+  CLOSE_DOWN_SEARCH_1(TypeFamily.CLOSE_DOWN_SEARCH, "CLOSE_DOWN"),
+  CONCERTED_INDISCIPLINE_1(TypeFamily.CONCERTED_INDISCIPLINE, "CON_INDISC", active = false),
+  DAMAGE_1(TypeFamily.DAMAGE, "DAMAGE", active = false),
+  DEATH_PRISONER_1(TypeFamily.DEATH_PRISONER, "DEATH"),
+  DEATH_OTHER_1(TypeFamily.DEATH_OTHER, "DEATH_NI"),
+  DISORDER_1(TypeFamily.DISORDER, "DISORDER", active = false),
+  DISORDER_2(TypeFamily.DISORDER, "DISORDER1"),
+  DRONE_SIGHTING_1(TypeFamily.DRONE_SIGHTING, "DRONE", active = false),
+  DRONE_SIGHTING_2(TypeFamily.DRONE_SIGHTING, "DRONE1", active = false),
+  DRONE_SIGHTING_3(TypeFamily.DRONE_SIGHTING, "DRONE2"),
+  DRUGS_1(TypeFamily.DRUGS, "DRUGS", active = false),
+  ESCAPE_FROM_PRISON_1(TypeFamily.ESCAPE_FROM_PRISON, "ESCAPE_EST"),
+  ESCAPE_FROM_ESCORT_1(TypeFamily.ESCAPE_FROM_ESCORT, "ESCAPE_ESC"),
+  FIND_1(TypeFamily.FIND, "FINDS", active = false),
+  FIND_2(TypeFamily.FIND, "FIND", active = false),
+  FIND_3(TypeFamily.FIND, "FIND1", active = false),
+  FIND_4(TypeFamily.FIND, "FIND0322", active = false),
+  FIND_5(TypeFamily.FIND, "FINDS1", active = false),
+  FIND_6(TypeFamily.FIND, "FIND0422"),
+  FIRE_1(TypeFamily.FIRE, "FIRE"),
+  FIREARM_1(TypeFamily.FIREARM, "FIREARM_ETC", active = false),
+  FOOD_REFUSAL_1(TypeFamily.FOOD_REFUSAL, "FOOD_REF"),
+  HOSTAGE_1(TypeFamily.HOSTAGE, "HOSTAGE", active = false),
+  INCIDENT_AT_HEIGHT_1(TypeFamily.INCIDENT_AT_HEIGHT, "ROOF_CLIMB", active = false),
+  KEY_OR_LOCK_1(TypeFamily.KEY_OR_LOCK, "KEY_LOCK", active = false),
+  KEY_OR_LOCK_2(TypeFamily.KEY_OR_LOCK, "KEY_LOCKNEW"),
+  MISCELLANEOUS_1(TypeFamily.MISCELLANEOUS, "MISC"),
+  MOBILE_PHONE_1(TypeFamily.MOBILE_PHONE, "MOBILES", active = false),
+  RADIO_COMPROMISE_1(TypeFamily.RADIO_COMPROMISE, "RADIO_COMP"),
+  RELEASE_IN_ERROR_1(TypeFamily.RELEASE_IN_ERROR, "REL_ERROR"),
+  SELF_HARM_1(TypeFamily.SELF_HARM, "SELF_HARM"),
+  TEMPORARY_RELEASE_FAILURE_1(TypeFamily.TEMPORARY_RELEASE_FAILURE, "TRF", active = false),
+  TEMPORARY_RELEASE_FAILURE_2(TypeFamily.TEMPORARY_RELEASE_FAILURE, "TRF1", active = false),
+  TEMPORARY_RELEASE_FAILURE_3(TypeFamily.TEMPORARY_RELEASE_FAILURE, "TRF2", active = false),
+  TEMPORARY_RELEASE_FAILURE_4(TypeFamily.TEMPORARY_RELEASE_FAILURE, "TRF3"),
+  TOOL_LOSS_1(TypeFamily.TOOL_LOSS, "TOOL_LOSS"),
   ;
 
+  val description = typeFamily.description
+
   companion object {
     fun fromNomisCode(type: String): Type = entries.find { it.nomisType == type }
       ?: throw ValidationException("Unknown NOMIS incident type: $type")
diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/constants/TypeFamily.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/constants/TypeFamily.kt
new file mode 100644
index 00000000..3af06f32
--- /dev/null
+++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/constants/TypeFamily.kt
@@ -0,0 +1,47 @@
+package uk.gov.justice.digital.hmpps.incidentreporting.constants
+
+/**
+ * Incident types are grouped into families and share a description.
+ *
+ * NB:
+ *   - new families should have a reasonably readable code
+ *   - existing families cannot be removed to preserve database integrity
+ *   - any additions, changes to order, codes or descriptions require a new migration of relevant constants DB table!
+ *   - code & description are expected to be 60 chars max
+ *   - a family might not have any active types
+ */
+enum class TypeFamily(
+  val description: String,
+) {
+  ABSCOND("Abscond"),
+  ASSAULT("Assault"),
+  ATTEMPTED_ESCAPE_FROM_PRISON("Attempted escape from establishment"),
+  ATTEMPTED_ESCAPE_FROM_ESCORT("Attempted escape from escort"),
+  BARRICADE("Barricade"),
+  BOMB("Bomb explosion or threat"),
+  BREACH_OF_SECURITY("Breach or attempted breach of security"),
+  CLOSE_DOWN_SEARCH("Close down search"),
+  CONCERTED_INDISCIPLINE("Incident involving 2 or more prisioners acting together"),
+  DAMAGE("Deliberate damage to prison property"),
+  DEATH_PRISONER("Death of prisoner"),
+  DEATH_OTHER("Death of other person"),
+  DISORDER("Disorder"),
+  DRONE_SIGHTING("Drone sighting"),
+  DRUGS("Drugs"),
+  ESCAPE_FROM_PRISON("Escape from establishment"),
+  ESCAPE_FROM_ESCORT("Escape from escort"),
+  FIND("Find of illicit items"),
+  FIRE("Fire"),
+  FIREARM("Firearm, ammunition or chemical incapacitant"),
+  FOOD_REFUSAL("Food or liquid refusual"),
+  HOSTAGE("Hostage incident"),
+  INCIDENT_AT_HEIGHT("Incident at height"),
+  KEY_OR_LOCK("Key or lock compromise"),
+  MISCELLANEOUS("Miscellaneous"),
+  MOBILE_PHONE("Mobile phone"),
+  RADIO_COMPROMISE("Radio compromise"),
+  RELEASE_IN_ERROR("Release in error"),
+  SELF_HARM("Self harm"),
+  TEMPORARY_RELEASE_FAILURE("Temporary release failure"),
+  TOOL_LOSS("Tool or implement loss"),
+}
diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/dto/response/TypeConstantDescription.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/dto/response/TypeConstantDescription.kt
index 4b5ea11c..a64a6edf 100644
--- a/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/dto/response/TypeConstantDescription.kt
+++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/dto/response/TypeConstantDescription.kt
@@ -3,19 +3,25 @@ package uk.gov.justice.digital.hmpps.incidentreporting.dto.response
 import com.fasterxml.jackson.annotation.JsonInclude
 import io.swagger.v3.oas.annotations.media.Schema
 
-@Schema(description = "Report type constant", accessMode = Schema.AccessMode.READ_ONLY)
+@Schema(description = "Report incident type constant", accessMode = Schema.AccessMode.READ_ONLY)
 @JsonInclude(JsonInclude.Include.ALWAYS)
 data class TypeConstantDescription(
-  @Schema(description = "Machine-readable identifier of this value", example = "DISORDER")
+  @Schema(description = "Machine-readable identifier for this family of incident types", example = "DISORDER")
+  val familyCode: String,
+  @Schema(description = "Machine-readable identifier of this incident type", example = "DISORDER")
   val code: String,
-  @Schema(description = "Human-readable description of this value", example = "Disorder")
+  @Schema(
+    description = "Human-readable description of this family of incident types " +
+      "(all incident types in one family share a description)",
+    example = "Disorder",
+  )
   val description: String,
   @Schema(description = "Whether this type is currently active and usable in new reports", example = "true")
   val active: Boolean = true,
 
   // NB: this property can be removed once fully migrated off NOMIS and reconciliation checks are turned off
   @Schema(
-    description = "Machine-readable NOMIS identifier of this value, which may be null for newer incident types",
+    description = "Machine-readable NOMIS identifier of this incident type, which may be null for newer ones",
     nullable = true,
     example = "DISORDER1",
     deprecated = true,
diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/dto/response/TypeFamilyConstantDescription.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/dto/response/TypeFamilyConstantDescription.kt
new file mode 100644
index 00000000..2e914d60
--- /dev/null
+++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/dto/response/TypeFamilyConstantDescription.kt
@@ -0,0 +1,13 @@
+package uk.gov.justice.digital.hmpps.incidentreporting.dto.response
+
+import com.fasterxml.jackson.annotation.JsonInclude
+import io.swagger.v3.oas.annotations.media.Schema
+
+@Schema(description = "Report incident type family constant", accessMode = Schema.AccessMode.READ_ONLY)
+@JsonInclude(JsonInclude.Include.ALWAYS)
+data class TypeFamilyConstantDescription(
+  @Schema(description = "Machine-readable identifier of this family", example = "DRONE_SIGHTING")
+  val code: String,
+  @Schema(description = "Human-readable description of this family", example = "Drone sighting")
+  val description: String,
+)
diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/ConstantsResource.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/ConstantsResource.kt
index 21bd597c..bb3d5f39 100644
--- a/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/ConstantsResource.kt
+++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/ConstantsResource.kt
@@ -16,12 +16,14 @@ import uk.gov.justice.digital.hmpps.incidentreporting.constants.PrisonerRole
 import uk.gov.justice.digital.hmpps.incidentreporting.constants.StaffRole
 import uk.gov.justice.digital.hmpps.incidentreporting.constants.Status
 import uk.gov.justice.digital.hmpps.incidentreporting.constants.Type
+import uk.gov.justice.digital.hmpps.incidentreporting.constants.TypeFamily
 import uk.gov.justice.digital.hmpps.incidentreporting.dto.response.ConstantDescription
 import uk.gov.justice.digital.hmpps.incidentreporting.dto.response.PrisonerOutcomeConstantDescription
 import uk.gov.justice.digital.hmpps.incidentreporting.dto.response.PrisonerRoleConstantDescription
 import uk.gov.justice.digital.hmpps.incidentreporting.dto.response.StaffRoleConstantDescription
 import uk.gov.justice.digital.hmpps.incidentreporting.dto.response.StatusConstantDescription
 import uk.gov.justice.digital.hmpps.incidentreporting.dto.response.TypeConstantDescription
+import uk.gov.justice.digital.hmpps.incidentreporting.dto.response.TypeFamilyConstantDescription
 
 @RestController
 @Validated
@@ -143,7 +145,24 @@ class ConstantsResource {
   )
   fun types(): List<TypeConstantDescription> {
     return Type.entries.map {
-      TypeConstantDescription(it.name, it.description, it.active, it.nomisType)
+      TypeConstantDescription(it.typeFamily.name, it.name, it.description, it.active, it.nomisType)
+    }
+  }
+
+  @GetMapping("/type-families")
+  @ResponseStatus(HttpStatus.OK)
+  @Operation(
+    summary = "List codes and descriptions of incident types families",
+    responses = [
+      ApiResponse(
+        responseCode = "200",
+        description = "Returns codes and descriptions",
+      ),
+    ],
+  )
+  fun typeFamilies(): List<TypeFamilyConstantDescription> {
+    return TypeFamily.entries.map {
+      TypeFamilyConstantDescription(it.name, it.description)
     }
   }
 }
diff --git a/src/main/resources/db/migration/V1_16__type_families.sql b/src/main/resources/db/migration/V1_16__type_families.sql
new file mode 100644
index 00000000..ec3cef84
--- /dev/null
+++ b/src/main/resources/db/migration/V1_16__type_families.sql
@@ -0,0 +1,627 @@
+-- This is a COPY of constants/enumerations for use in reporting; Analytical Platform & DPR.
+--
+-- The application will continue to only use all values solely from internal constants
+-- in `uk.gov.justice.digital.hmpps.incidentreporting.constants`.
+--
+-- NB:
+--   - any changes to constants / enumeration classes REQUIRE new migrations!
+--   - these tables should NOT be used in foreign key constaints! otherwise migrations would be overly-complicated
+
+create table constant_type_family
+(
+  code        varchar(60) primary key,
+  sequence    integer     not null,
+  description varchar(60) not null
+);
+
+-- add new type families
+insert into constant_type_family(sequence, code, description)
+values (0, 'ABSCOND', 'Abscond'),
+       (1, 'ASSAULT', 'Assault'),
+       (1, 'ATTEMPTED_ESCAPE_FROM_PRISON', 'Attempted escape from establishment'),
+       (3, 'ATTEMPTED_ESCAPE_FROM_ESCORT', 'Attempted escape from escort'),
+       (4, 'BARRICADE', 'Barricade'),
+       (5, 'BOMB', 'Bomb explosion or threat'),
+       (6, 'BREACH_OF_SECURITY', 'Breach or attempted breach of security'),
+       (7, 'CLOSE_DOWN_SEARCH', 'Close down search'),
+       (8, 'CONCERTED_INDISCIPLINE', 'Incident involving 2 or more prisioners acting together'),
+       (9, 'DAMAGE', 'Deliberate damage to prison property'),
+       (10, 'DEATH_PRISONER', 'Death of prisoner'),
+       (11, 'DEATH_OTHER', 'Death of other person'),
+       (12, 'DISORDER', 'Disorder'),
+       (13, 'DRONE_SIGHTING', 'Drone sighting'),
+       (14, 'DRUGS', 'Drugs'),
+       (15, 'ESCAPE_FROM_PRISON', 'Escape from establishment'),
+       (16, 'ESCAPE_FROM_ESCORT', 'Escape from escort'),
+       (17, 'FIND', 'Find of illicit items'),
+       (18, 'FIRE', 'Fire'),
+       (19, 'FIREARM', 'Firearm, ammunition or chemical incapacitant'),
+       (20, 'FOOD_REFUSAL', 'Food or liquid refusual'),
+       (21, 'HOSTAGE', 'Hostage incident'),
+       (22, 'INCIDENT_AT_HEIGHT', 'Incident at height'),
+       (23, 'KEY_OR_LOCK', 'Key or lock compromise'),
+       (24, 'MISCELLANEOUS', 'Miscellaneous'),
+       (25, 'MOBILE_PHONE', 'Mobile phone'),
+       (26, 'RADIO_COMPROMISE', 'Radio compromise'),
+       (27, 'RELEASE_IN_ERROR', 'Release in error'),
+       (28, 'SELF_HARM', 'Self harm'),
+       (29, 'TEMPORARY_RELEASE_FAILURE', 'Temporary release failure'),
+       (30, 'TOOL_LOSS', 'Tool or implement loss');
+
+alter table constant_type
+  add column family_code varchar(60);
+
+-- put types into families and rename codes to match
+update constant_type
+set family_code='ABSCOND',
+    code='ABSCOND_1',
+    sequence=0,
+    description='Abscond'
+where code = 'ABSCONDER';
+update constant_type
+set family_code='ASSAULT',
+    code='ASSAULT_5',
+    sequence=5,
+    description='Assault'
+where code = 'ASSAULT';
+update constant_type
+set family_code='ATTEMPTED_ESCAPE_FROM_PRISON',
+    code='ATTEMPTED_ESCAPE_FROM_PRISON_1',
+    sequence=6,
+    description='Attempted escape from establishment'
+where code = 'ATTEMPTED_ESCAPE_FROM_CUSTODY';
+update constant_type
+set family_code='ATTEMPTED_ESCAPE_FROM_ESCORT',
+    code='ATTEMPTED_ESCAPE_FROM_ESCORT_1',
+    sequence=7,
+    description='Attempted escape from escort'
+where code = 'ATTEMPTED_ESCAPE_FROM_ESCORT';
+update constant_type
+set family_code='BOMB',
+    code='BOMB_1',
+    sequence=9,
+    description='Bomb explosion or threat'
+where code = 'BOMB_THREAT';
+update constant_type
+set family_code='BREACH_OF_SECURITY',
+    code='BREACH_OF_SECURITY_1',
+    sequence=10,
+    description='Breach or attempted breach of security'
+where code = 'BREACH_OF_SECURITY';
+update constant_type
+set family_code='DEATH_PRISONER',
+    code='DEATH_PRISONER_1',
+    sequence=14,
+    description='Death of prisoner'
+where code = 'DEATH_IN_CUSTODY';
+update constant_type
+set family_code='DEATH_OTHER',
+    code='DEATH_OTHER_1',
+    sequence=15,
+    description='Death of other person'
+where code = 'DEATH_OTHER';
+update constant_type
+set family_code='DISORDER',
+    code='DISORDER_2',
+    sequence=17,
+    description='Disorder'
+where code = 'DISORDER';
+update constant_type
+set family_code='DRONE_SIGHTING',
+    code='DRONE_SIGHTING_3',
+    sequence=20,
+    description='Drone sighting'
+where code = 'DRONE_SIGHTING';
+update constant_type
+set family_code='ESCAPE_FROM_PRISON',
+    code='ESCAPE_FROM_PRISON_1',
+    sequence=22,
+    description='Escape from establishment'
+where code = 'ESCAPE_FROM_CUSTODY';
+update constant_type
+set family_code='ESCAPE_FROM_ESCORT',
+    code='ESCAPE_FROM_ESCORT_1',
+    sequence=23,
+    description='Escape from escort'
+where code = 'ESCAPE_FROM_ESCORT';
+update constant_type
+set family_code='FIND',
+    code='FIND_6',
+    sequence=29,
+    description='Find of illicit items'
+where code = 'FINDS';
+update constant_type
+set family_code='FIRE',
+    code='FIRE_1',
+    sequence=30,
+    description='Fire'
+where code = 'FIRE';
+update constant_type
+set family_code='FOOD_REFUSAL',
+    code='FOOD_REFUSAL_1',
+    sequence=32,
+    description='Food or liquid refusual'
+where code = 'FOOD_REFUSAL';
+update constant_type
+set family_code='CLOSE_DOWN_SEARCH',
+    code='CLOSE_DOWN_SEARCH_1',
+    sequence=11,
+    description='Close down search'
+where code = 'FULL_CLOSE_DOWN_SEARCH';
+update constant_type
+set family_code='KEY_OR_LOCK',
+    code='KEY_OR_LOCK_2',
+    sequence=36,
+    description='Key or lock compromise'
+where code = 'KEY_LOCK_INCIDENT';
+update constant_type
+set family_code='MISCELLANEOUS',
+    code='MISCELLANEOUS_1',
+    sequence=37,
+    description='Miscellaneous'
+where code = 'MISCELLANEOUS';
+update constant_type
+set family_code='RADIO_COMPROMISE',
+    code='RADIO_COMPROMISE_1',
+    sequence=39,
+    description='Radio compromise'
+where code = 'RADIO_COMPROMISE';
+update constant_type
+set family_code='RELEASE_IN_ERROR',
+    code='RELEASE_IN_ERROR_1',
+    sequence=40,
+    description='Release in error'
+where code = 'RELEASED_IN_ERROR';
+update constant_type
+set family_code='SELF_HARM',
+    code='SELF_HARM_1',
+    sequence=41,
+    description='Self harm'
+where code = 'SELF_HARM';
+update constant_type
+set family_code='TEMPORARY_RELEASE_FAILURE',
+    code='TEMPORARY_RELEASE_FAILURE_4',
+    sequence=45,
+    description='Temporary release failure'
+where code = 'TEMPORARY_RELEASE_FAILURE';
+update constant_type
+set family_code='TOOL_LOSS',
+    code='TOOL_LOSS_1',
+    sequence=46,
+    description='Tool or implement loss'
+where code = 'TOOL_LOSS';
+update constant_type
+set family_code='DAMAGE',
+    code='DAMAGE_1',
+    sequence=13,
+    description='Deliberate damage to prison property'
+where code = 'DAMAGE';
+update constant_type
+set family_code='ASSAULT',
+    code='ASSAULT_1',
+    sequence=1,
+    description='Assault'
+where code = 'OLD_ASSAULT';
+update constant_type
+set family_code='ASSAULT',
+    code='ASSAULT_2',
+    sequence=2,
+    description='Assault'
+where code = 'OLD_ASSAULT1';
+update constant_type
+set family_code='ASSAULT',
+    code='ASSAULT_3',
+    sequence=3,
+    description='Assault'
+where code = 'OLD_ASSAULT2';
+update constant_type
+set family_code='ASSAULT',
+    code='ASSAULT_4',
+    sequence=4,
+    description='Assault'
+where code = 'OLD_ASSAULT3';
+update constant_type
+set family_code='BARRICADE',
+    code='BARRICADE_1',
+    sequence=8,
+    description='Barricade'
+where code = 'OLD_BARRICADE';
+update constant_type
+set family_code='CONCERTED_INDISCIPLINE',
+    code='CONCERTED_INDISCIPLINE_1',
+    sequence=12,
+    description='Incident involving 2 or more prisioners acting together'
+where code = 'OLD_CONCERTED_INDISCIPLINE';
+update constant_type
+set family_code='DISORDER',
+    code='DISORDER_1',
+    sequence=16,
+    description='Disorder'
+where code = 'OLD_DISORDER';
+update constant_type
+set family_code='DRONE_SIGHTING',
+    code='DRONE_SIGHTING_1',
+    sequence=18,
+    description='Drone sighting'
+where code = 'OLD_DRONE_SIGHTING';
+update constant_type
+set family_code='DRONE_SIGHTING',
+    code='DRONE_SIGHTING_2',
+    sequence=19,
+    description='Drone sighting'
+where code = 'OLD_DRONE_SIGHTING1';
+update constant_type
+set family_code='DRUGS',
+    code='DRUGS_1',
+    sequence=21,
+    description='Drugs'
+where code = 'OLD_DRUGS';
+update constant_type
+set family_code='FIND',
+    code='FIND_1',
+    sequence=24,
+    description='Find of illicit items'
+where code = 'OLD_FINDS';
+update constant_type
+set family_code='FIND',
+    code='FIND_2',
+    sequence=25,
+    description='Find of illicit items'
+where code = 'OLD_FINDS1';
+update constant_type
+set family_code='FIND',
+    code='FIND_3',
+    sequence=26,
+    description='Find of illicit items'
+where code = 'OLD_FINDS2';
+update constant_type
+set family_code='FIND',
+    code='FIND_4',
+    sequence=27,
+    description='Find of illicit items'
+where code = 'OLD_FINDS3';
+update constant_type
+set family_code='FIND',
+    code='FIND_5',
+    sequence=28,
+    description='Find of illicit items'
+where code = 'OLD_FINDS4';
+update constant_type
+set family_code='FIREARM',
+    code='FIREARM_1',
+    sequence=31,
+    description='Firearm, ammunition or chemical incapacitant'
+where code = 'OLD_FIREARM_ETC';
+update constant_type
+set family_code='HOSTAGE',
+    code='HOSTAGE_1',
+    sequence=33,
+    description='Hostage incident'
+where code = 'OLD_HOSTAGE';
+update constant_type
+set family_code='KEY_OR_LOCK',
+    code='KEY_OR_LOCK_1',
+    sequence=35,
+    description='Key or lock compromise'
+where code = 'OLD_KEY_LOCK_INCIDENT';
+update constant_type
+set family_code='MOBILE_PHONE',
+    code='MOBILE_PHONE_1',
+    sequence=38,
+    description='Mobile phone'
+where code = 'OLD_MOBILES';
+update constant_type
+set family_code='INCIDENT_AT_HEIGHT',
+    code='INCIDENT_AT_HEIGHT_1',
+    sequence=34,
+    description='Incident at height'
+where code = 'OLD_ROOF_CLIMB';
+update constant_type
+set family_code='TEMPORARY_RELEASE_FAILURE',
+    code='TEMPORARY_RELEASE_FAILURE_1',
+    sequence=42,
+    description='Temporary release failure'
+where code = 'OLD_TEMPORARY_RELEASE_FAILURE';
+update constant_type
+set family_code='TEMPORARY_RELEASE_FAILURE',
+    code='TEMPORARY_RELEASE_FAILURE_2',
+    sequence=43,
+    description='Temporary release failure'
+where code = 'OLD_TEMPORARY_RELEASE_FAILURE1';
+update constant_type
+set family_code='TEMPORARY_RELEASE_FAILURE',
+    code='TEMPORARY_RELEASE_FAILURE_3',
+    sequence=44,
+    description='Temporary release failure'
+where code = 'OLD_TEMPORARY_RELEASE_FAILURE2';
+
+-- update all reports and histories with new codes
+update report
+set type='ABSCOND_1'
+where type = 'ABSCONDER';
+update report
+set type='ASSAULT_5'
+where type = 'ASSAULT';
+update report
+set type='ATTEMPTED_ESCAPE_FROM_PRISON_1'
+where type = 'ATTEMPTED_ESCAPE_FROM_CUSTODY';
+update report
+set type='ATTEMPTED_ESCAPE_FROM_ESCORT_1'
+where type = 'ATTEMPTED_ESCAPE_FROM_ESCORT';
+update report
+set type='BOMB_1'
+where type = 'BOMB_THREAT';
+update report
+set type='BREACH_OF_SECURITY_1'
+where type = 'BREACH_OF_SECURITY';
+update report
+set type='DEATH_PRISONER_1'
+where type = 'DEATH_IN_CUSTODY';
+update report
+set type='DEATH_OTHER_1'
+where type = 'DEATH_OTHER';
+update report
+set type='DISORDER_2'
+where type = 'DISORDER';
+update report
+set type='DRONE_SIGHTING_3'
+where type = 'DRONE_SIGHTING';
+update report
+set type='ESCAPE_FROM_PRISON_1'
+where type = 'ESCAPE_FROM_CUSTODY';
+update report
+set type='ESCAPE_FROM_ESCORT_1'
+where type = 'ESCAPE_FROM_ESCORT';
+update report
+set type='FIND_6'
+where type = 'FINDS';
+update report
+set type='FIRE_1'
+where type = 'FIRE';
+update report
+set type='FOOD_REFUSAL_1'
+where type = 'FOOD_REFUSAL';
+update report
+set type='CLOSE_DOWN_SEARCH_1'
+where type = 'FULL_CLOSE_DOWN_SEARCH';
+update report
+set type='KEY_OR_LOCK_2'
+where type = 'KEY_LOCK_INCIDENT';
+update report
+set type='MISCELLANEOUS_1'
+where type = 'MISCELLANEOUS';
+update report
+set type='RADIO_COMPROMISE_1'
+where type = 'RADIO_COMPROMISE';
+update report
+set type='RELEASE_IN_ERROR_1'
+where type = 'RELEASED_IN_ERROR';
+update report
+set type='SELF_HARM_1'
+where type = 'SELF_HARM';
+update report
+set type='TEMPORARY_RELEASE_FAILURE_4'
+where type = 'TEMPORARY_RELEASE_FAILURE';
+update report
+set type='TOOL_LOSS_1'
+where type = 'TOOL_LOSS';
+update report
+set type='DAMAGE_1'
+where type = 'DAMAGE';
+update report
+set type='ASSAULT_1'
+where type = 'OLD_ASSAULT';
+update report
+set type='ASSAULT_2'
+where type = 'OLD_ASSAULT1';
+update report
+set type='ASSAULT_3'
+where type = 'OLD_ASSAULT2';
+update report
+set type='ASSAULT_4'
+where type = 'OLD_ASSAULT3';
+update report
+set type='BARRICADE_1'
+where type = 'OLD_BARRICADE';
+update report
+set type='CONCERTED_INDISCIPLINE_1'
+where type = 'OLD_CONCERTED_INDISCIPLINE';
+update report
+set type='DISORDER_1'
+where type = 'OLD_DISORDER';
+update report
+set type='DRONE_SIGHTING_1'
+where type = 'OLD_DRONE_SIGHTING';
+update report
+set type='DRONE_SIGHTING_2'
+where type = 'OLD_DRONE_SIGHTING1';
+update report
+set type='DRUGS_1'
+where type = 'OLD_DRUGS';
+update report
+set type='FIND_1'
+where type = 'OLD_FINDS';
+update report
+set type='FIND_2'
+where type = 'OLD_FINDS1';
+update report
+set type='FIND_3'
+where type = 'OLD_FINDS2';
+update report
+set type='FIND_4'
+where type = 'OLD_FINDS3';
+update report
+set type='FIND_5'
+where type = 'OLD_FINDS4';
+update report
+set type='FIREARM_1'
+where type = 'OLD_FIREARM_ETC';
+update report
+set type='HOSTAGE_1'
+where type = 'OLD_HOSTAGE';
+update report
+set type='KEY_OR_LOCK_1'
+where type = 'OLD_KEY_LOCK_INCIDENT';
+update report
+set type='MOBILE_PHONE_1'
+where type = 'OLD_MOBILES';
+update report
+set type='INCIDENT_AT_HEIGHT_1'
+where type = 'OLD_ROOF_CLIMB';
+update report
+set type='TEMPORARY_RELEASE_FAILURE_1'
+where type = 'OLD_TEMPORARY_RELEASE_FAILURE';
+update report
+set type='TEMPORARY_RELEASE_FAILURE_2'
+where type = 'OLD_TEMPORARY_RELEASE_FAILURE1';
+update report
+set type='TEMPORARY_RELEASE_FAILURE_3'
+where type = 'OLD_TEMPORARY_RELEASE_FAILURE2';
+update history
+set type='ABSCOND_1'
+where type = 'ABSCONDER';
+update history
+set type='ASSAULT_5'
+where type = 'ASSAULT';
+update history
+set type='ATTEMPTED_ESCAPE_FROM_PRISON_1'
+where type = 'ATTEMPTED_ESCAPE_FROM_CUSTODY';
+update history
+set type='ATTEMPTED_ESCAPE_FROM_ESCORT_1'
+where type = 'ATTEMPTED_ESCAPE_FROM_ESCORT';
+update history
+set type='BOMB_1'
+where type = 'BOMB_THREAT';
+update history
+set type='BREACH_OF_SECURITY_1'
+where type = 'BREACH_OF_SECURITY';
+update history
+set type='DEATH_PRISONER_1'
+where type = 'DEATH_IN_CUSTODY';
+update history
+set type='DEATH_OTHER_1'
+where type = 'DEATH_OTHER';
+update history
+set type='DISORDER_2'
+where type = 'DISORDER';
+update history
+set type='DRONE_SIGHTING_3'
+where type = 'DRONE_SIGHTING';
+update history
+set type='ESCAPE_FROM_PRISON_1'
+where type = 'ESCAPE_FROM_CUSTODY';
+update history
+set type='ESCAPE_FROM_ESCORT_1'
+where type = 'ESCAPE_FROM_ESCORT';
+update history
+set type='FIND_6'
+where type = 'FINDS';
+update history
+set type='FIRE_1'
+where type = 'FIRE';
+update history
+set type='FOOD_REFUSAL_1'
+where type = 'FOOD_REFUSAL';
+update history
+set type='CLOSE_DOWN_SEARCH_1'
+where type = 'FULL_CLOSE_DOWN_SEARCH';
+update history
+set type='KEY_OR_LOCK_2'
+where type = 'KEY_LOCK_INCIDENT';
+update history
+set type='MISCELLANEOUS_1'
+where type = 'MISCELLANEOUS';
+update history
+set type='RADIO_COMPROMISE_1'
+where type = 'RADIO_COMPROMISE';
+update history
+set type='RELEASE_IN_ERROR_1'
+where type = 'RELEASED_IN_ERROR';
+update history
+set type='SELF_HARM_1'
+where type = 'SELF_HARM';
+update history
+set type='TEMPORARY_RELEASE_FAILURE_4'
+where type = 'TEMPORARY_RELEASE_FAILURE';
+update history
+set type='TOOL_LOSS_1'
+where type = 'TOOL_LOSS';
+update history
+set type='DAMAGE_1'
+where type = 'DAMAGE';
+update history
+set type='ASSAULT_1'
+where type = 'OLD_ASSAULT';
+update history
+set type='ASSAULT_2'
+where type = 'OLD_ASSAULT1';
+update history
+set type='ASSAULT_3'
+where type = 'OLD_ASSAULT2';
+update history
+set type='ASSAULT_4'
+where type = 'OLD_ASSAULT3';
+update history
+set type='BARRICADE_1'
+where type = 'OLD_BARRICADE';
+update history
+set type='CONCERTED_INDISCIPLINE_1'
+where type = 'OLD_CONCERTED_INDISCIPLINE';
+update history
+set type='DISORDER_1'
+where type = 'OLD_DISORDER';
+update history
+set type='DRONE_SIGHTING_1'
+where type = 'OLD_DRONE_SIGHTING';
+update history
+set type='DRONE_SIGHTING_2'
+where type = 'OLD_DRONE_SIGHTING1';
+update history
+set type='DRUGS_1'
+where type = 'OLD_DRUGS';
+update history
+set type='FIND_1'
+where type = 'OLD_FINDS';
+update history
+set type='FIND_2'
+where type = 'OLD_FINDS1';
+update history
+set type='FIND_3'
+where type = 'OLD_FINDS2';
+update history
+set type='FIND_4'
+where type = 'OLD_FINDS3';
+update history
+set type='FIND_5'
+where type = 'OLD_FINDS4';
+update history
+set type='FIREARM_1'
+where type = 'OLD_FIREARM_ETC';
+update history
+set type='HOSTAGE_1'
+where type = 'OLD_HOSTAGE';
+update history
+set type='KEY_OR_LOCK_1'
+where type = 'OLD_KEY_LOCK_INCIDENT';
+update history
+set type='MOBILE_PHONE_1'
+where type = 'OLD_MOBILES';
+update history
+set type='INCIDENT_AT_HEIGHT_1'
+where type = 'OLD_ROOF_CLIMB';
+update history
+set type='TEMPORARY_RELEASE_FAILURE_1'
+where type = 'OLD_TEMPORARY_RELEASE_FAILURE';
+update history
+set type='TEMPORARY_RELEASE_FAILURE_2'
+where type = 'OLD_TEMPORARY_RELEASE_FAILURE1';
+update history
+set type='TEMPORARY_RELEASE_FAILURE_3'
+where type = 'OLD_TEMPORARY_RELEASE_FAILURE2';
+
+alter table constant_type
+  alter column family_code set not null;
+
+update constant_status
+set description='Information amended'
+where code = 'INFORMATION_AMENDED';
diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/helper/ReportBuilder.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/helper/ReportBuilder.kt
index 05253ac3..6a52b952 100644
--- a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/helper/ReportBuilder.kt
+++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/helper/ReportBuilder.kt
@@ -16,7 +16,7 @@ fun buildReport(
   location: String = "MDI",
   source: InformationSource = InformationSource.DPS,
   status: Status = Status.DRAFT,
-  type: Type = Type.FINDS,
+  type: Type = Type.FIND_6,
   reportingUsername: String = "USER1",
   // all related entities apart from event are optionally generated:
   generateStaffInvolvement: Int = 0,
@@ -36,7 +36,7 @@ fun buildReport(
     status = status,
     type = type,
     title = "Incident Report $reportReference",
-    description = "A new incident created in the new service of type ${type.description}",
+    description = "A new incident created in the new service of type ${type.description.lowercase()}",
     reportedAt = reportTime,
     createdAt = reportTime,
     modifiedAt = reportTime,
@@ -104,7 +104,7 @@ fun buildReport(
 
   (1..generateHistory).forEach { historyIndex ->
     val history = report.addHistory(
-      type = Type.entries.elementAtWrapped(historyIndex),
+      type = Type.entries.filter { it.active }.elementAtWrapped(historyIndex),
       changedAt = reportTime.minusMinutes((generateHistory - historyIndex).toLong()),
       changedBy = "some-past-user",
     )
diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/jpa/ConstantsTableTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/jpa/ConstantsTableTest.kt
index 615af985..43a84b48 100644
--- a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/jpa/ConstantsTableTest.kt
+++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/jpa/ConstantsTableTest.kt
@@ -13,6 +13,7 @@ import uk.gov.justice.digital.hmpps.incidentreporting.constants.PrisonerRole
 import uk.gov.justice.digital.hmpps.incidentreporting.constants.StaffRole
 import uk.gov.justice.digital.hmpps.incidentreporting.constants.Status
 import uk.gov.justice.digital.hmpps.incidentreporting.constants.Type
+import uk.gov.justice.digital.hmpps.incidentreporting.constants.TypeFamily
 import uk.gov.justice.digital.hmpps.incidentreporting.integration.IntegrationTestBase
 
 @DisplayName("Compare the result of migrations to in-built constant enumerations")
@@ -99,6 +100,7 @@ class ConstantsTableTest : IntegrationTestBase() {
   fun `type table`() {
     val expected = Type.entries.map {
       mapOf(
+        "family_code" to it.typeFamily.name,
         "code" to it.name,
         "description" to it.description,
         "active" to it.active,
@@ -107,7 +109,24 @@ class ConstantsTableTest : IntegrationTestBase() {
     val actual = listAllConstants(
       // language=postgresql
       """
-      SELECT code, description, active FROM constant_type
+      SELECT family_code, code, description, active FROM constant_type
+      """,
+    )
+    assertThat(actual).isEqualTo(expected)
+  }
+
+  @Test
+  fun `type family table`() {
+    val expected = TypeFamily.entries.map {
+      mapOf(
+        "code" to it.name,
+        "description" to it.description,
+      )
+    }
+    val actual = listAllConstants(
+      // language=postgresql
+      """
+      SELECT code, description FROM constant_type_family
       """,
     )
     assertThat(actual).isEqualTo(expected)
diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/jpa/ReportRepositoryTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/jpa/ReportRepositoryTest.kt
index 6cc3970b..fedaa6c3 100644
--- a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/jpa/ReportRepositoryTest.kt
+++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/jpa/ReportRepositoryTest.kt
@@ -81,7 +81,7 @@ class ReportRepositoryTest : IntegrationTestBase() {
         filterByLocations("MDI"),
         filterBySource(InformationSource.DPS),
         filterByStatuses(Status.DRAFT),
-        filterByType(Type.FINDS),
+        filterByType(Type.FIND_6),
         filterByIncidentDateFrom(today.minusDays(2)),
         filterByIncidentDateUntil(today),
         filterByReportedDateFrom(today.minusDays(2)),
@@ -100,7 +100,7 @@ class ReportRepositoryTest : IntegrationTestBase() {
         filterByLocations("LEI"),
         filterBySource(InformationSource.NOMIS),
         filterByStatuses(Status.AWAITING_ANALYSIS),
-        filterByType(Type.FOOD_REFUSAL),
+        filterByType(Type.FOOD_REFUSAL_1),
         filterByIncidentDateFrom(today),
         filterByIncidentDateUntil(today.minusDays(2)),
         filterByReportedDateFrom(today),
@@ -126,7 +126,7 @@ class ReportRepositoryTest : IntegrationTestBase() {
             location = "MDI",
             source = InformationSource.DPS,
             status = Status.AWAITING_ANALYSIS,
-            type = Type.ASSAULT,
+            type = Type.ASSAULT_5,
           ),
           buildReport(
             reportReference = "12346",
@@ -134,7 +134,7 @@ class ReportRepositoryTest : IntegrationTestBase() {
             location = "LEI",
             source = InformationSource.DPS,
             status = Status.AWAITING_ANALYSIS,
-            type = Type.FINDS,
+            type = Type.FIND_6,
           ),
           buildReport(
             reportReference = "11124143",
@@ -142,7 +142,7 @@ class ReportRepositoryTest : IntegrationTestBase() {
             location = "MDI",
             source = InformationSource.NOMIS,
             status = Status.DRAFT,
-            type = Type.FINDS,
+            type = Type.FIND_6,
           ),
         ),
       ).map { it.id!! }
@@ -191,27 +191,27 @@ class ReportRepositoryTest : IntegrationTestBase() {
       assertSpecificationReturnsReports(
         filterByStatuses(Status.AWAITING_ANALYSIS)
           .and(filterBySource(InformationSource.DPS))
-          .and(filterByType(Type.FINDS)),
+          .and(filterByType(Type.FIND_6)),
         listOf(report2Id),
       )
       assertSpecificationReturnsReports(
         filterByStatuses(Status.AWAITING_ANALYSIS)
           .and(filterByLocations("LEI"))
           .and(filterBySource(InformationSource.DPS))
-          .and(filterByType(Type.FINDS)),
+          .and(filterByType(Type.FIND_6)),
         listOf(report2Id),
       )
       assertSpecificationReturnsReports(
         filterByStatuses(Status.AWAITING_ANALYSIS)
           .and(filterBySource(InformationSource.DPS))
           .and(filterByLocations("MDI"))
-          .and(filterByType(Type.FINDS)),
+          .and(filterByType(Type.FIND_6)),
         emptyList(),
       )
       assertSpecificationReturnsReports(
         filterByIncidentDateFrom(today.minusDays(3))
           .and(filterByIncidentDateUntil(today.minusDays(3)))
-          .and(filterByType(Type.ASSAULT)),
+          .and(filterByType(Type.ASSAULT_5)),
         listOf(report1Id),
       )
       assertSpecificationReturnsReports(
@@ -240,7 +240,7 @@ class ReportRepositoryTest : IntegrationTestBase() {
           reportReference = reportRepository.generateReportReference(),
           incidentDateAndTime = hourAgo,
           status = Status.AWAITING_ANALYSIS,
-          type = Type.SELF_HARM,
+          type = Type.SELF_HARM_1,
           title = "A summary",
           description = "An incident occurred",
           reportedBy = "user1",
@@ -298,13 +298,13 @@ class ReportRepositoryTest : IntegrationTestBase() {
       .addResponse("HEAD", null, 0, "Head", "user1", now)
       .addResponse("ARM", null, 1, "Arm", "user1", now)
 
-    report.addHistory(Type.FINDS, halfHourAgo, "user2")
+    report.addHistory(Type.FIND_6, halfHourAgo, "user2")
       .addQuestion("FINDS-Q1", "Finds question 1", 1)
       .addResponse("response1", 0, null, "Some information 1", "user1", halfHourAgo)
       .addResponse("response2", 1, null, "Some information 2", "user1", halfHourAgo)
       .addResponse("response3", 2, null, "Some information 3", "user1", halfHourAgo)
 
-    report.addHistory(Type.ASSAULT, quarterHourAgo, "user1")
+    report.addHistory(Type.ASSAULT_5, quarterHourAgo, "user1")
       .addQuestion("ASSAULT-Q1", "Assault question 1", 1)
       .addResponse("response4", 0, null, "Some information 4", "user1", quarterHourAgo)
       .addResponse("response5", 1, null, "Some information 5", "user1", quarterHourAgo)
@@ -315,7 +315,7 @@ class ReportRepositoryTest : IntegrationTestBase() {
 
     report = reportRepository.findOneEagerlyByReportReference(report.reportReference)
       ?: throw EntityNotFoundException()
-    report.changeType(Type.ASSAULT, now, "user5")
+    report.changeType(Type.ASSAULT_5, now, "user5")
 
     report.addQuestion("SOME_QUESTION", "Another question?", 4)
       .addResponse("YES", null, 0, "Yes", "user1", now)
@@ -330,7 +330,7 @@ class ReportRepositoryTest : IntegrationTestBase() {
     report = reportRepository.findOneEagerlyByReportReference(report.reportReference)
       ?: throw EntityNotFoundException()
     assertThat(report.status).isEqualTo(Status.AWAITING_ANALYSIS)
-    assertThat(report.type).isEqualTo(Type.ASSAULT)
+    assertThat(report.type).isEqualTo(Type.ASSAULT_5)
     assertThat(report.questions).hasSize(1)
     assertThat(report.questions.elementAt(0).code).isEqualTo("SOME_QUESTION")
     assertThat(report.questions.elementAt(0).responses).hasSize(4)
diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/ConstantsResourceTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/ConstantsResourceTest.kt
index 80b46142..4bab3647 100644
--- a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/ConstantsResourceTest.kt
+++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/ConstantsResourceTest.kt
@@ -12,6 +12,7 @@ import uk.gov.justice.digital.hmpps.incidentreporting.constants.PrisonerRole
 import uk.gov.justice.digital.hmpps.incidentreporting.constants.StaffRole
 import uk.gov.justice.digital.hmpps.incidentreporting.constants.Status
 import uk.gov.justice.digital.hmpps.incidentreporting.constants.Type
+import uk.gov.justice.digital.hmpps.incidentreporting.constants.TypeFamily
 import uk.gov.justice.digital.hmpps.incidentreporting.integration.SqsIntegrationTestBase
 
 @DisplayName("Constants resource")
@@ -130,19 +131,35 @@ class ConstantsResourceTest : SqsIntegrationTestBase() {
       Type.entries.size,
       listOf(
         mapOf(
-          "code" to "DISORDER",
+          "familyCode" to "DISORDER",
+          "code" to "DISORDER_2",
           "description" to "Disorder",
           "active" to true,
           "nomisCode" to "DISORDER1",
         ),
         mapOf(
-          "code" to "OLD_ASSAULT2",
-          "description" to "Assault (from April 2017)",
+          "familyCode" to "ASSAULT",
+          "code" to "ASSAULT_3",
+          "description" to "Assault",
           "active" to false,
           "nomisCode" to "ASSAULTS1",
         ),
       ),
     ),
+    ConstantsTestCase(
+      "type-families",
+      TypeFamily.entries.size,
+      listOf(
+        mapOf(
+          "code" to "DISORDER",
+          "description" to "Disorder",
+        ),
+        mapOf(
+          "code" to "KEY_OR_LOCK",
+          "description" to "Key or lock compromise",
+        ),
+      ),
+    ),
   ).map { (endpoint, expectedCount, expectedSamples) ->
     DynamicTest.dynamicTest("exposes $endpoint constants") {
       webTestClient.get().uri("/constants/$endpoint")
diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/DprReportingIntegrationTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/DprReportingIntegrationTest.kt
index ba5c9eb8..6afb782a 100644
--- a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/DprReportingIntegrationTest.kt
+++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/DprReportingIntegrationTest.kt
@@ -167,8 +167,9 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
           .expectStatus().isOk
           .expectBody()
           .json(
+            // language=json
             """
-              {
+            {
               "id": "incident-report",
               "name": "Incident report summary",
               "description": "List of all incidents filtered by dates, types, status and locations (INC0009)",
@@ -180,7 +181,7 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
                 "printable": true
               }
             }
-            """.trimIndent(),
+            """,
           )
       }
     }
@@ -229,36 +230,37 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
             .expectStatus().isOk
             .expectBody()
             .json(
+              // language=json
               """
               [
                 {
-                  "type": "FINDS",
-                  "type_description": "Finds",
+                  "type": "FIND_6",
+                  "type_description": "Find of illicit items",
                   "status": "DRAFT",
                   "status_description": "Draft",
                   "incident_date_and_time": "05/12/2023 11:34",
                   "reported_at": "05/12/2023",
                   "reported_by": "USER1",
                   "title": "Incident Report 11124143",
-                  "description": "A new incident created in the new service of type Finds",
+                  "description": "A new incident created in the new service of type find of illicit items",
                   "location": "MDI",
                   "modified_at": "05/12/2023 12:34"
                 },
                 {
-                  "type": "FINDS",
-                  "type_description": "Finds",
+                  "type": "FIND_6",
+                  "type_description": "Find of illicit items",
                   "status": "DRAFT",
                   "status_description": "Draft",
                   "incident_date_and_time": "05/12/2023 11:34",
                   "reported_at": "05/12/2023",
                   "reported_by": "USER1",
                   "title": "Incident Report 11124199",
-                  "description": "A new incident created in the new service of type Finds",
+                  "description": "A new incident created in the new service of type find of illicit items",
                   "location": "MDI",
                   "modified_at": "05/12/2023 12:34"
                 }
               ]
-              """.trimIndent(),
+              """,
             )
         }
 
@@ -329,26 +331,27 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
             .expectStatus().isOk()
             .expectBody().jsonPath("$.length()").isEqualTo(1)
             .json(
+              // language=json
               """
-                [
-                  {
-                    "id": "${pecsReport.id}",
-                    "report_reference": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports/${pecsReport.id}' target=\"_blank\">${pecsReport.reportReference}</a>",
-                    "type": "${pecsReport.type.name}",
-                    "type_description": "${pecsReport.type.description}",
-                    "status": "${pecsReport.status.name}",
-                    "status_description": "${pecsReport.status.description}",
-                    "incident_date_and_time": "05/12/2023 11:34",
-                    "reported_at": "05/12/2023",
-                    "reported_by": "USER1",
-                    "title": "${pecsReport.title}",
-                    "description": "${pecsReport.description}",
-                    "location": "${pecsReport.location}",
-                    "pecs_region": "National Operations Unit",
-                    "modified_at": "05/12/2023 12:34"
-                  }
-                ]
-              """.trimIndent(),
+              [
+                {
+                  "id": "${pecsReport.id}",
+                  "report_reference": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports/${pecsReport.id}' target=\"_blank\">${pecsReport.reportReference}</a>",
+                  "type": "${pecsReport.type.name}",
+                  "type_description": "${pecsReport.type.description}",
+                  "status": "${pecsReport.status.name}",
+                  "status_description": "${pecsReport.status.description}",
+                  "incident_date_and_time": "05/12/2023 11:34",
+                  "reported_at": "05/12/2023",
+                  "reported_by": "USER1",
+                  "title": "${pecsReport.title}",
+                  "description": "${pecsReport.description}",
+                  "location": "${pecsReport.location}",
+                  "pecs_region": "National Operations Unit",
+                  "modified_at": "05/12/2023 12:34"
+                }
+              ]
+              """,
             )
         }
       }
@@ -383,14 +386,15 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
             .expectStatus().isOk
             .expectBody()
             .json(
+              // language=json
               """
               [
                 {
                   "id": "${existingReport1.id}",
                   "report_reference": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports/${existingReport1.id}' target=\"_blank\">11124143</a>",
                   "location": "MDI",
-                  "type": "FINDS",
-                  "type_description": "Finds",
+                  "type": "FIND_6",
+                  "type_description": "Find of illicit items",
                   "status": "DRAFT",
                   "status_description": "Draft",
                   "incident_date_and_time": "05/12/2023 11:34",
@@ -404,8 +408,8 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
                   "id": "${existingReport1.id}",
                   "report_reference": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports/${existingReport1.id}' target=\"_blank\">11124143</a>",
                   "location": "MDI",
-                  "type": "FINDS",
-                  "type_description": "Finds",
+                  "type": "FIND_6",
+                  "type_description": "Find of illicit items",
                   "status": "DRAFT",
                   "status_description": "Draft",
                   "incident_date_and_time": "05/12/2023 11:34",
@@ -419,8 +423,8 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
                   "id": "${existingReport1.id}",
                   "report_reference": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports/${existingReport1.id}' target=\"_blank\">11124143</a>",
                   "location": "MDI",
-                  "type": "FINDS",
-                  "type_description": "Finds",
+                  "type": "FIND_6",
+                  "type_description": "Find of illicit items",
                   "status": "DRAFT",
                   "status_description": "Draft",
                   "incident_date_and_time": "05/12/2023 11:34",
@@ -434,8 +438,8 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
                   "id": "${existingReport2.id}",
                   "report_reference": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports/${existingReport2.id}' target=\"_blank\">11124199</a>",
                   "location": "MDI",
-                  "type": "FINDS",
-                  "type_description": "Finds",
+                  "type": "FIND_6",
+                  "type_description": "Find of illicit items",
                   "status": "DRAFT",
                   "status_description": "Draft",
                   "incident_date_and_time": "05/12/2023 11:34",
@@ -446,7 +450,7 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
                   "comment": "Comment #1"
                 }
               ]
-              """.trimIndent(),
+              """,
             )
         }
 
@@ -459,14 +463,15 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
             .expectStatus().isOk
             .expectBody()
             .json(
+              // language=json
               """
               [
                 {
                   "id": "${existingReport1.id}",
                   "report_reference": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports/${existingReport1.id}' target=\"_blank\">11124143</a>",
                   "location": "MDI",
-                  "type": "FINDS",
-                  "type_description": "Finds",
+                  "type": "FIND_6",
+                  "type_description": "Find of illicit items",
                   "status": "DRAFT",
                   "status_description": "Draft",
                   "incident_date_and_time": "05/12/2023 11:34",
@@ -480,8 +485,8 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
                   "id": "${existingReport1.id}",
                   "report_reference": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports/${existingReport1.id}' target=\"_blank\">11124143</a>",
                   "location": "MDI",
-                  "type": "FINDS",
-                  "type_description": "Finds",
+                  "type": "FIND_6",
+                  "type_description": "Find of illicit items",
                   "status": "DRAFT",
                   "status_description": "Draft",
                   "incident_date_and_time": "05/12/2023 11:34",
@@ -495,8 +500,8 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
                   "id": "${existingReport2.id}",
                   "report_reference": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports/${existingReport2.id}' target=\"_blank\">11124199</a>",
                   "location": "MDI",
-                  "type": "FINDS",
-                  "type_description": "Finds",
+                  "type": "FIND_6",
+                  "type_description": "Find of illicit items",
                   "status": "DRAFT",
                   "status_description": "Draft",
                   "incident_date_and_time": "05/12/2023 11:34",
@@ -507,7 +512,7 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
                   "comment": "Comment #1"
                 }
               ]
-              """.trimIndent(),
+              """,
             )
         }
       }
@@ -542,18 +547,19 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
             .expectStatus().isOk
             .expectBody()
             .json(
+              // language=json
               """
               [
                 {
                   "start_date": "05/12/2023",
                   "filter_date": "05/12/2023",
                   "location": "MDI",
-                  "type": "FINDS",
-                  "type_description": "Finds",
-                  "num_of_incidents": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports?fromDate=05/12/2023&toDate=05/12/2023&location=MDI&incidentType=FINDS' target=\"_blank\">2</a>"
+                  "type": "FIND_6",
+                  "type_description": "Find of illicit items",
+                  "num_of_incidents": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports?fromDate=05/12/2023&toDate=05/12/2023&location=MDI&incidentType=FIND_6' target=\"_blank\">2</a>"
                 }
               ]
-              """.trimIndent(),
+              """,
             )
         }
 
@@ -566,6 +572,7 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
             .expectStatus().isOk
             .expectBody()
             .json(
+              // language=json
               """
               [
                 {
@@ -573,12 +580,12 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
                   "min_date": "05/12/2023",
                   "max_date": "05/12/2023",
                   "location": "MDI",
-                  "type": "FINDS",
-                  "type_description": "Finds",
-                  "num_of_incidents": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports?fromDate=05/12/2023&toDate=05/12/2023&location=MDI&incidentType=FINDS' target=\"_blank\">2</a>"
+                  "type": "FIND_6",
+                  "type_description": "Find of illicit items",
+                  "num_of_incidents": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports?fromDate=05/12/2023&toDate=05/12/2023&location=MDI&incidentType=FIND_6' target=\"_blank\">2</a>"
                 }
               ]
-              """.trimIndent(),
+              """,
             )
         }
 
@@ -591,19 +598,20 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
             .expectStatus().isOk
             .expectBody()
             .json(
+              // language=json
               """
-          [
-            {
-              "start_date": "Dec-2023",
-              "min_date": "05/12/2023",
-              "max_date": "05/12/2023",
-              "location": "MDI",
-              "type": "FINDS",
-              "type_description": "Finds",
-              "num_of_incidents": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports?fromDate=05/12/2023&toDate=05/12/2023&location=MDI&incidentType=FINDS' target=\"_blank\">2</a>"
-            }
-          ]
-              """.trimIndent(),
+              [
+                {
+                  "start_date": "Dec-2023",
+                  "min_date": "05/12/2023",
+                  "max_date": "05/12/2023",
+                  "location": "MDI",
+                  "type": "FIND_6",
+                  "type_description": "Find of illicit items",
+                  "num_of_incidents": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports?fromDate=05/12/2023&toDate=05/12/2023&location=MDI&incidentType=FIND_6' target=\"_blank\">2</a>"
+                }
+              ]
+              """,
             )
         }
       }
@@ -651,6 +659,7 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
             .expectStatus().isOk
             .expectBody()
             .json(
+              // language=json
               """
               [
                 {
@@ -660,10 +669,10 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
                   "prisoner_number": "<a href='https://prisoner.digital.prison.service.justice.gov.uk/prisoner/A0002AA' target=\"_blank\">A0002AA</a>",
                   "first_name": "First 2",
                   "last_name": "Last 2, First 2",
-                  "type": "FINDS",
-                  "type_description": "Finds",
+                  "type": "FIND_6",
+                  "type_description": "Find of illicit items",
                   "location": "MDI",
-                  "num_of_incidents": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports?searchID=A0002AA&fromDate=05/12/2023&toDate=05/12/2023&location=MDI&incidentType=FINDS' target=\"_blank\">1</a>"
+                  "num_of_incidents": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports?searchID=A0002AA&fromDate=05/12/2023&toDate=05/12/2023&location=MDI&incidentType=FIND_6' target=\"_blank\">1</a>"
                 },
                 {
                   "month_year": "Dec-2023",
@@ -672,13 +681,13 @@ class DprReportingIntegrationTest : SqsIntegrationTestBase() {
                   "prisoner_number": "<a href='https://prisoner.digital.prison.service.justice.gov.uk/prisoner/A0001AA' target=\"_blank\">A0001AA</a>",
                   "first_name": "First 1",
                   "last_name": "Last 1, First 1",
-                  "type": "FINDS",
-                  "type_description": "Finds",
+                  "type": "FIND_6",
+                  "type_description": "Find of illicit items",
                   "location": "MDI",
-                  "num_of_incidents": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports?searchID=A0001AA&fromDate=05/12/2023&toDate=05/12/2023&location=MDI&incidentType=FINDS' target=\"_blank\">2</a>"
+                  "num_of_incidents": "<a href='https://incident-reporting.hmpps.service.justice.gov.uk/reports?searchID=A0001AA&fromDate=05/12/2023&toDate=05/12/2023&location=MDI&incidentType=FIND_6' target=\"_blank\">2</a>"
                 }
               ]
-              """.trimIndent(),
+              """,
             )
         }
       }
diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/EventResourceTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/EventResourceTest.kt
index 2e607333..d994abb7 100644
--- a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/EventResourceTest.kt
+++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/EventResourceTest.kt
@@ -424,7 +424,7 @@ class EventResourceTest : SqsIntegrationTestBase() {
           .expectStatus().isOk
           .expectBody().json(
             // language=json
-            """ 
+            """
             {
               "id": "${existingReport.event.id}",
               "eventReference": "11124143",
@@ -436,12 +436,12 @@ class EventResourceTest : SqsIntegrationTestBase() {
               "reports": [{
                 "id": "${existingReport.id}",
                 "reportReference": "11124143",
-                "type": "FINDS",
+                "type": "FIND_6",
                 "incidentDateAndTime": "2023-12-05T11:34:56",
                 "location": "MDI",
                 "prisonId": "MDI",
                 "title": "Incident Report 11124143",
-                "description": "A new incident created in the new service of type Finds",
+                "description": "A new incident created in the new service of type find of illicit items",
                 "reportedBy": "USER1",
                 "reportedAt": "2023-12-05T12:34:56",
                 "status": "DRAFT",
@@ -467,7 +467,7 @@ class EventResourceTest : SqsIntegrationTestBase() {
           buildReport(
             reportReference = "11017203",
             reportTime = now.plusMinutes(10),
-            type = Type.MISCELLANEOUS,
+            type = Type.MISCELLANEOUS_1,
           ),
         )
         anotherReport.event = existingReport.event
@@ -480,7 +480,7 @@ class EventResourceTest : SqsIntegrationTestBase() {
           .expectStatus().isOk
           .expectBody().json(
             // language=json
-            """ 
+            """
             {
               "id": "${existingReport.event.id}",
               "eventReference": "11124143",
@@ -493,12 +493,12 @@ class EventResourceTest : SqsIntegrationTestBase() {
                 {
                   "id": "${existingReport.id}",
                   "reportReference": "11124143",
-                  "type": "FINDS",
+                  "type": "FIND_6",
                   "incidentDateAndTime": "2023-12-05T11:34:56",
                   "location": "MDI",
                   "prisonId": "MDI",
                   "title": "Incident Report 11124143",
-                  "description": "A new incident created in the new service of type Finds",
+                  "description": "A new incident created in the new service of type find of illicit items",
                   "reportedBy": "USER1",
                   "reportedAt": "2023-12-05T12:34:56",
                   "status": "DRAFT",
@@ -512,12 +512,12 @@ class EventResourceTest : SqsIntegrationTestBase() {
                 {
                   "id": "${anotherReport.id}",
                   "reportReference": "11017203",
-                  "type": "MISCELLANEOUS",
+                  "type": "MISCELLANEOUS_1",
                   "incidentDateAndTime": "2023-12-05T11:44:56",
                   "location": "MDI",
                   "prisonId": "MDI",
                   "title": "Incident Report 11017203",
-                  "description": "A new incident created in the new service of type Miscellaneous",
+                  "description": "A new incident created in the new service of type miscellaneous",
                   "reportedBy": "USER1",
                   "reportedAt": "2023-12-05T12:44:56",
                   "status": "DRAFT",
@@ -593,7 +593,7 @@ class EventResourceTest : SqsIntegrationTestBase() {
           .expectStatus().isOk
           .expectBody().json(
             // language=json
-            """ 
+            """
             {
               "id": "${existingReport.event.id}",
               "eventReference": "11124143",
@@ -605,12 +605,12 @@ class EventResourceTest : SqsIntegrationTestBase() {
               "reports": [{
                 "id": "${existingReport.id}",
                 "reportReference": "11124143",
-                "type": "FINDS",
+                "type": "FIND_6",
                 "incidentDateAndTime": "2023-12-05T11:34:56",
                 "location": "MDI",
                 "prisonId": "MDI",
                 "title": "Incident Report 11124143",
-                "description": "A new incident created in the new service of type Finds",
+                "description": "A new incident created in the new service of type find of illicit items",
                 "reportedBy": "USER1",
                 "reportedAt": "2023-12-05T12:34:56",
                 "status": "DRAFT",
@@ -636,7 +636,7 @@ class EventResourceTest : SqsIntegrationTestBase() {
           buildReport(
             reportReference = "11017203",
             reportTime = now.plusMinutes(10),
-            type = Type.MISCELLANEOUS,
+            type = Type.MISCELLANEOUS_1,
           ),
         )
         anotherReport.event = existingReport.event
@@ -649,7 +649,7 @@ class EventResourceTest : SqsIntegrationTestBase() {
           .expectStatus().isOk
           .expectBody().json(
             // language=json
-            """ 
+            """
             {
               "id": "${existingReport.event.id}",
               "eventReference": "11124143",
@@ -662,12 +662,12 @@ class EventResourceTest : SqsIntegrationTestBase() {
                 {
                   "id": "${existingReport.id}",
                   "reportReference": "11124143",
-                  "type": "FINDS",
+                  "type": "FIND_6",
                   "incidentDateAndTime": "2023-12-05T11:34:56",
                   "location": "MDI",
                   "prisonId": "MDI",
                   "title": "Incident Report 11124143",
-                  "description": "A new incident created in the new service of type Finds",
+                  "description": "A new incident created in the new service of type find of illicit items",
                   "reportedBy": "USER1",
                   "reportedAt": "2023-12-05T12:34:56",
                   "status": "DRAFT",
@@ -681,12 +681,12 @@ class EventResourceTest : SqsIntegrationTestBase() {
                 {
                   "id": "${anotherReport.id}",
                   "reportReference": "11017203",
-                  "type": "MISCELLANEOUS",
+                  "type": "MISCELLANEOUS_1",
                   "incidentDateAndTime": "2023-12-05T11:44:56",
                   "location": "MDI",
                   "prisonId": "MDI",
                   "title": "Incident Report 11017203",
-                  "description": "A new incident created in the new service of type Miscellaneous",
+                  "description": "A new incident created in the new service of type miscellaneous",
                   "reportedBy": "USER1",
                   "reportedAt": "2023-12-05T12:44:56",
                   "status": "DRAFT",
diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/NomisSyncResourceTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/NomisSyncResourceTest.kt
index bf39443d..29bf4875 100644
--- a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/NomisSyncResourceTest.kt
+++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/NomisSyncResourceTest.kt
@@ -471,7 +471,7 @@ class NomisSyncResourceTest : SqsIntegrationTestBase() {
               """
               {
                 "reportReference": "112414666",
-                "type": "SELF_HARM",
+                "type": "SELF_HARM_1",
                 "nomisType": "SELF_HARM",
                 "incidentDateAndTime": "2023-12-05T11:34:56",
                 "location": "MDI",
@@ -584,7 +584,7 @@ class NomisSyncResourceTest : SqsIntegrationTestBase() {
                 ],
                 "history": [
                   {
-                    "type": "DAMAGE",
+                    "type": "DAMAGE_1",
                     "nomisType": "DAMAGE",
                     "changedAt": "2023-12-05T12:34:56",
                     "changedBy": "user2",
@@ -762,7 +762,7 @@ class NomisSyncResourceTest : SqsIntegrationTestBase() {
               """
               {
                 "reportReference": "$newIncidentId",
-                "type": "ASSAULT",
+                "type": "ASSAULT_5",
                 "nomisType": "ASSAULTS3",
                 "incidentDateAndTime": "2023-12-05T11:34:56",
                 "location": "MDI",
@@ -875,7 +875,7 @@ class NomisSyncResourceTest : SqsIntegrationTestBase() {
                 ],
                 "history": [
                   {
-                    "type": "DAMAGE",
+                    "type": "DAMAGE_1",
                     "nomisType": "DAMAGE",
                     "changedAt": "2023-12-05T12:34:56",
                     "changedBy": "user2",
@@ -1281,7 +1281,7 @@ class NomisSyncResourceTest : SqsIntegrationTestBase() {
               {
                 "id": "${existingNomisReport.id}",
                 "reportReference": "$NOMIS_INCIDENT_NUMBER",
-                "type": "ASSAULT",
+                "type": "ASSAULT_5",
                 "nomisType": "ASSAULTS3",
                 "incidentDateAndTime": "2023-11-25T12:34:56",
                 "location": "FBI",
@@ -1376,7 +1376,7 @@ class NomisSyncResourceTest : SqsIntegrationTestBase() {
                 ],
                 "history": [
                   {
-                    "type": "DAMAGE",
+                    "type": "DAMAGE_1",
                     "nomisType": "DAMAGE",
                     "changedAt": "2023-12-03T12:34:56",
                     "changedBy": "user2",
@@ -1448,7 +1448,7 @@ class NomisSyncResourceTest : SqsIntegrationTestBase() {
                     ]
                   },
                   {
-                    "type": "BOMB_THREAT",
+                    "type": "BOMB_1",
                     "nomisType": "BOMB",
                     "changedAt": "2023-12-04T12:34:56",
                     "changedBy": "user2",
diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/ReportResourceTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/ReportResourceTest.kt
index 19b84d29..3fcff262 100644
--- a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/ReportResourceTest.kt
+++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/resource/ReportResourceTest.kt
@@ -227,7 +227,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
                   location = if (index < 2) "LEI" else "MDI",
                   status = if (fromDps) Status.DRAFT else Status.AWAITING_ANALYSIS,
                   source = if (fromDps) InformationSource.DPS else InformationSource.NOMIS,
-                  type = if (index < 3) Type.FINDS else Type.FIRE,
+                  type = if (index < 3) Type.FIND_6 else Type.FIRE_1,
                   generateStaffInvolvement = index,
                   generatePrisonerInvolvement = index,
                 )
@@ -406,10 +406,10 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             "source=NOMIS                                                | 2",
             "source=NOMIS&location=MDI                                   | 2",
             "source=DPS&location=MDI                                     | 1",
-            "type=ASSAULT                                                | 0",
-            "type=FIRE                                                   | 1",
-            "type=FIRE&location=LEI                                      | 0",
-            "type=FIRE&location=MDI                                      | 1",
+            "type=ASSAULT_5                                              | 0",
+            "type=FIRE_1                                                 | 1",
+            "type=FIRE_1&location=LEI                                    | 0",
+            "type=FIRE_1&location=MDI                                    | 1",
             "incidentDateFrom=2023-12-05                                 | 1",
             "incidentDateFrom=2023-12-04                                 | 2",
             "incidentDateFrom=2023-12-03                                 | 3",
@@ -491,12 +491,12 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             {
               "id": "${existingReport.id}",
               "reportReference": "11124143",
-              "type": "FINDS",
+              "type": "FIND_6",
               "incidentDateAndTime": "2023-12-05T11:34:56",
               "location": "MDI",
               "prisonId": "MDI",
               "title": "Incident Report 11124143",
-              "description": "A new incident created in the new service of type Finds",
+              "description": "A new incident created in the new service of type find of illicit items",
               "reportedBy": "USER1",
               "reportedAt": "2023-12-05T12:34:56",
               "status": "DRAFT",
@@ -564,13 +564,13 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             {
               "id": "${existingReport.id}",
               "reportReference": "11124143",
-              "type": "FINDS",
+              "type": "FIND_6",
               "nomisType": "FIND0422",
               "incidentDateAndTime": "2023-12-05T11:34:56",
               "location": "MDI",
               "prisonId": "MDI",
               "title": "Incident Report 11124143",
-              "description": "A new incident created in the new service of type Finds",
+              "description": "A new incident created in the new service of type find of illicit items",
               "event": {
                 "id": "${existingReport.event.id}",
                 "eventReference": "11124143",
@@ -666,12 +666,12 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             {
               "id": "${existingReport.id}",
               "reportReference": "11124143",
-              "type": "FINDS",
+              "type": "FIND_6",
               "incidentDateAndTime": "2023-12-05T11:34:56",
               "location": "MDI",
               "prisonId": "MDI",
               "title": "Incident Report 11124143",
-              "description": "A new incident created in the new service of type Finds",
+              "description": "A new incident created in the new service of type find of illicit items",
               "reportedBy": "USER1",
               "reportedAt": "2023-12-05T12:34:56",
               "status": "DRAFT",
@@ -739,13 +739,13 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             {
               "id": "${existingReport.id}",
               "reportReference": "11124143",
-              "type": "FINDS",
+              "type": "FIND_6",
               "nomisType": "FIND0422",
               "incidentDateAndTime": "2023-12-05T11:34:56",
               "location": "MDI",
               "prisonId": "MDI",
               "title": "Incident Report 11124143",
-              "description": "A new incident created in the new service of type Finds",
+              "description": "A new incident created in the new service of type find of illicit items",
               "event": {
                 "id": "${existingReport.event.id}",
                 "eventReference": "11124143",
@@ -800,7 +800,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
       incidentDateAndTime = now.minusHours(1),
       title = "An incident occurred",
       description = "Longer explanation of incident",
-      type = Type.SELF_HARM,
+      type = Type.SELF_HARM_1,
       location = "MDI",
       createNewEvent = true,
     )
@@ -894,11 +894,11 @@ class ReportResourceTest : SqsIntegrationTestBase() {
         webTestClient.post().uri(url)
           .headers(setAuthorisation(roles = listOf("ROLE_MAINTAIN_INCIDENT_REPORTS"), scopes = listOf("write")))
           .header("Content-Type", "application/json")
-          .bodyValue(createReportRequest.copy(type = Type.OLD_ASSAULT).toJson())
+          .bodyValue(createReportRequest.copy(type = Type.ASSAULT_1).toJson())
           .exchange()
           .expectStatus().isBadRequest
           .expectBody().jsonPath("developerMessage").value<String> {
-            assertThat(it).contains("Inactive incident type OLD_ASSAULT")
+            assertThat(it).contains("Inactive incident type ASSAULT_1")
           }
 
         assertThatNoDomainEventsWereSent()
@@ -920,7 +920,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             // language=json
             """
             {
-              "type": "SELF_HARM",
+              "type": "SELF_HARM_1",
               "nomisType": "SELF_HARM",
               "incidentDateAndTime": "2023-12-05T11:34:56",
               "location": "MDI",
@@ -987,7 +987,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             // language=json
             """
             {
-              "type": "SELF_HARM",
+              "type": "SELF_HARM_1",
               "nomisType": "SELF_HARM",
               "incidentDateAndTime": "2023-12-05T11:34:56",
               "location": "MDI",
@@ -1149,12 +1149,12 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             {
               "id": "${existingReport.id}",
               "reportReference": "11124143",
-              "type": "FINDS",
+              "type": "FIND_6",
               "incidentDateAndTime": "2023-12-05T11:34:56",
               "location": "MDI",
               "prisonId": "MDI",
               "title": "Incident Report 11124143",
-              "description": "A new incident created in the new service of type Finds",
+              "description": "A new incident created in the new service of type find of illicit items",
               "reportedBy": "USER1",
               "reportedAt": "2023-12-05T12:34:56",
               "status": "DRAFT",
@@ -1178,7 +1178,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
           incidentDateAndTime = now.minusHours(2),
           location = "LEI",
           title = "Updated report 11124143",
-          description = "Updated incident report of type Finds",
+          description = "Updated incident report of type find of illicit items",
         )
         webTestClient.patch().uri(url)
           .headers(setAuthorisation(roles = listOf("ROLE_MAINTAIN_INCIDENT_REPORTS"), scopes = listOf("write")))
@@ -1192,12 +1192,12 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             {
               "id": "${existingReport.id}",
               "reportReference": "11124143",
-              "type": "FINDS",
+              "type": "FIND_6",
               "incidentDateAndTime": "2023-12-05T10:34:56",
               "location": "LEI",
               "prisonId": "LEI",
               "title": "Updated report 11124143",
-              "description": "Updated incident report of type Finds",
+              "description": "Updated incident report of type find of illicit items",
               "reportedBy": "USER1",
               "reportedAt": "2023-12-05T12:34:56",
               "status": "DRAFT",
@@ -1227,7 +1227,11 @@ class ReportResourceTest : SqsIntegrationTestBase() {
           incidentDateAndTime = if (fieldName == "incidentDateAndTime") now.minusHours(2) else null,
           location = if (fieldName == "location") "LEI" else null,
           title = if (fieldName == "title") "Updated report 11124143" else null,
-          description = if (fieldName == "description") "Updated incident report of type Finds" else null,
+          description = if (fieldName == "description") {
+            "Updated incident report of type find of illicit items"
+          } else {
+            null
+          },
         )
         val expectedIncidentDateAndTime = if (fieldName == "incidentDateAndTime") {
           "2023-12-05T10:34:56"
@@ -1245,9 +1249,9 @@ class ReportResourceTest : SqsIntegrationTestBase() {
           "Incident Report 11124143"
         }
         val expectedDescription = if (fieldName == "description") {
-          "Updated incident report of type Finds"
+          "Updated incident report of type find of illicit items"
         } else {
-          "A new incident created in the new service of type Finds"
+          "A new incident created in the new service of type find of illicit items"
         }
         webTestClient.patch().uri(url)
           .headers(setAuthorisation(roles = listOf("ROLE_MAINTAIN_INCIDENT_REPORTS"), scopes = listOf("write")))
@@ -1261,7 +1265,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             {
               "id": "${existingReport.id}",
               "reportReference": "11124143",
-              "type": "FINDS",
+              "type": "FIND_6",
               "incidentDateAndTime": "$expectedIncidentDateAndTime",
               "location": "$expectedLocation",
               "prisonId": "$expectedLocation",
@@ -1352,7 +1356,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
           incidentDateAndTime = now.minusHours(2),
           location = "LEI",
           title = "Updated report 11124143",
-          description = "Updated incident report of type Finds",
+          description = "Updated incident report of type find of illicit items",
 
           updateEvent = updateEvent,
         )
@@ -1375,7 +1379,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
               "location": "LEI",
               "prisonId": "LEI",
               "title": "Updated report 11124143",
-              "description": "Updated incident report of type Finds",
+              "description": "Updated incident report of type find of illicit items",
               "createdAt": "2023-12-05T12:34:56",
               "modifiedAt": "2023-12-05T12:34:56",
               "modifiedBy": "request-user"
@@ -1541,13 +1545,13 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             {
               "id": "${existingReport.id}",
               "reportReference": "11124143",
-              "type": "FINDS",
+              "type": "FIND_6",
               "nomisType": "FIND0422",
               "incidentDateAndTime": "2023-12-05T11:34:56",
               "location": "MDI",
               "prisonId": "MDI",
               "title": "Incident Report 11124143",
-              "description": "A new incident created in the new service of type Finds",
+              "description": "A new incident created in the new service of type find of illicit items",
               "reportedBy": "USER1",
               "reportedAt": "2023-12-05T12:34:56",
               "status": "DRAFT",
@@ -1588,13 +1592,13 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             {
               "id": "${existingReport.id}",
               "reportReference": "11124143",
-              "type": "FINDS",
+              "type": "FIND_6",
               "nomisType": "FIND0422",
               "incidentDateAndTime": "2023-12-05T11:34:56",
               "location": "MDI",
               "prisonId": "MDI",
               "title": "Incident Report 11124143",
-              "description": "A new incident created in the new service of type Finds",
+              "description": "A new incident created in the new service of type find of illicit items",
               "reportedBy": "USER1",
               "reportedAt": "2023-12-05T12:34:56",
               "status": "AWAITING_ANALYSIS",
@@ -1670,7 +1674,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
 
     // language=json
     private val validPayload = """
-      {"newType": "FIRE"}
+      {"newType": "FIRE_1"}
     """
 
     @BeforeEach
@@ -1713,7 +1717,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
           .bodyValue(
             // language=json
             """
-              {"type": "FIRE"}
+              {"type": "FIRE_1"}
             """,
           )
           .exchange()
@@ -1747,13 +1751,13 @@ class ReportResourceTest : SqsIntegrationTestBase() {
           .bodyValue(
             // language=json
             """
-              {"newType": "OLD_ASSAULT3"}
+              {"newType": "ASSAULT_4"}
             """,
           )
           .exchange()
           .expectStatus().isBadRequest
           .expectBody().jsonPath("developerMessage").value<String> {
-            assertThat(it).contains("Inactive incident type OLD_ASSAULT3")
+            assertThat(it).contains("Inactive incident type ASSAULT_4")
           }
 
         assertThatNoDomainEventsWereSent()
@@ -1781,7 +1785,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
           .bodyValue(
             // language=json
             """
-              {"newType": "FINDS"}
+              {"newType": "FIND_6"}
             """,
           )
           .exchange()
@@ -1792,13 +1796,13 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             {
               "id": "${reportWithQuestions.id}",
               "reportReference": "11124146",
-              "type": "FINDS",
+              "type": "FIND_6",
               "nomisType": "FIND0422",
               "incidentDateAndTime": "2023-12-05T11:31:56",
               "location": "MDI",
               "prisonId": "MDI",
               "title": "Incident Report 11124146",
-              "description": "A new incident created in the new service of type Finds",
+              "description": "A new incident created in the new service of type find of illicit items",
               "reportedBy": "USER1",
               "reportedAt": "2023-12-05T12:31:56",
               "status": "AWAITING_ANALYSIS",
@@ -1888,13 +1892,13 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             {
               "id": "${reportWithQuestions.id}",
               "reportReference": "11124146",
-              "type": "FIRE",
+              "type": "FIRE_1",
               "nomisType": "FIRE",
               "incidentDateAndTime": "2023-12-05T11:31:56",
               "location": "MDI",
               "prisonId": "MDI",
               "title": "Incident Report 11124146",
-              "description": "A new incident created in the new service of type Finds",
+              "description": "A new incident created in the new service of type find of illicit items",
               "reportedBy": "USER1",
               "reportedAt": "2023-12-05T12:31:56",
               "status": "AWAITING_ANALYSIS",
@@ -1907,7 +1911,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
               "lastModifiedInNomis": false,
               "history": [
                 {
-                  "type": "FINDS",
+                  "type": "FIND_6",
                   "nomisType": "FIND0422",
                   "changedAt": "2023-12-05T12:34:56",
                   "changedBy": "request-user",
@@ -2016,13 +2020,13 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             {
               "id": "${reportWithQuestionsAndHistory.id}",
               "reportReference": "11124146",
-              "type": "FIRE",
+              "type": "FIRE_1",
               "nomisType": "FIRE",
               "incidentDateAndTime": "2023-12-05T11:31:56",
               "location": "MDI",
               "prisonId": "MDI",
               "title": "Incident Report 11124146",
-              "description": "A new incident created in the new service of type Finds",
+              "description": "A new incident created in the new service of type find of illicit items",
               "reportedBy": "USER1",
               "reportedAt": "2023-12-05T12:31:56",
               "status": "AWAITING_ANALYSIS",
@@ -2035,7 +2039,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
               "lastModifiedInNomis": false,
               "history": [
                 {
-                  "type": "ASSAULT",
+                  "type": "ASSAULT_5",
                   "nomisType": "ASSAULTS3",
                   "changedAt": "2023-12-05T12:31:56",
                   "changedBy": "some-past-user",
@@ -2057,7 +2061,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
                   ]
                 },
                 {
-                  "type": "FINDS",
+                  "type": "FIND_6",
                   "nomisType": "FIND0422",
                   "changedAt": "2023-12-05T12:34:56",
                   "changedBy": "request-user",
@@ -2132,7 +2136,7 @@ class ReportResourceTest : SqsIntegrationTestBase() {
             """
             {
               "id": "${nomisReport.id}",
-              "type": "FIRE",
+              "type": "FIRE_1",
               "createdInNomis": true,
               "lastModifiedInNomis": false
             }
diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/service/NomisSyncServiceTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/service/NomisSyncServiceTest.kt
index 33cf8c75..59d98091 100644
--- a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/service/NomisSyncServiceTest.kt
+++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/service/NomisSyncServiceTest.kt
@@ -158,7 +158,7 @@ class NomisSyncServiceTest {
     id = sampleReportId,
     reportReference = "112414323",
     incidentDateAndTime = whenIncidentHappened,
-    type = Type.SELF_HARM,
+    type = Type.SELF_HARM_1,
     title = "Cutting",
     description = "Offender was found in own cell with a razor",
     location = "MDI",
@@ -249,7 +249,7 @@ class NomisSyncServiceTest {
   private fun assertSampleReportConvertedToDto(report: ReportWithDetails) {
     assertThat(report.id).isEqualTo(sampleReportId)
     assertThat(report.reportReference).isEqualTo("112414323")
-    assertThat(report.type).isEqualTo(Type.SELF_HARM)
+    assertThat(report.type).isEqualTo(Type.SELF_HARM_1)
     assertThat(report.incidentDateAndTime).isEqualTo(whenIncidentHappened)
     assertThat(report.location).isEqualTo("MDI")
     assertThat(report.title).isEqualTo("Cutting")
@@ -407,7 +407,7 @@ class NomisSyncServiceTest {
       id = sampleReportId,
       reportReference = "112414323",
       incidentDateAndTime = whenIncidentHappened,
-      type = Type.SELF_HARM,
+      type = Type.SELF_HARM_1,
       title = "Cutting",
       description = "Offender was found in own cell with a razor",
       location = "MDI",
diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/utils/TypeCodeTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/utils/TypeCodeTest.kt
new file mode 100644
index 00000000..33e572c7
--- /dev/null
+++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/utils/TypeCodeTest.kt
@@ -0,0 +1,33 @@
+package uk.gov.justice.digital.hmpps.incidentreporting.utils
+
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.DisplayName
+import org.junit.jupiter.api.Test
+import uk.gov.justice.digital.hmpps.incidentreporting.constants.Type
+import uk.gov.justice.digital.hmpps.incidentreporting.constants.TypeFamily
+
+@DisplayName("Type codes")
+class TypeCodeTest {
+  @Test
+  fun `codes for incident types start with family code`() {
+    assertThat(Type.entries).allSatisfy { type ->
+      val prefix = "${type.typeFamily.name}_"
+      assertThat(type.name).startsWith(prefix)
+      val suffix = type.name.removePrefix(prefix)
+      suffix.toIntOrNull()
+        ?: throw AssertionError("type code suffix should be an integer")
+    }
+  }
+
+  @Test
+  fun `codes for incident types within a family are numbered successively`() {
+    assertThat(TypeFamily.entries).allSatisfy { family ->
+      val prefix = "${family.name}_"
+      val types = Type.entries.filter { it.typeFamily == family }
+      val suffixes = types.map { it.name.removePrefix(prefix).toInt() }.toSet()
+      assertThat(suffixes).hasSize(types.size)
+      assertThat(suffixes.min()).isEqualTo(1)
+      assertThat(suffixes.max()).isEqualTo(types.size)
+    }
+  }
+}
diff --git a/src/test/resources/entity-mapping/sample-report-basic.json b/src/test/resources/entity-mapping/sample-report-basic.json
index 598dbad2..462d1794 100644
--- a/src/test/resources/entity-mapping/sample-report-basic.json
+++ b/src/test/resources/entity-mapping/sample-report-basic.json
@@ -1,11 +1,11 @@
 {
   "reportReference": "11124143",
-  "type": "FINDS",
+  "type": "FIND_6",
   "incidentDateAndTime": "2023-12-05T11:34:56",
   "location": "MDI",
   "prisonId": "MDI",
   "title": "Incident Report 11124143",
-  "description": "A new incident created in the new service of type Finds",
+  "description": "A new incident created in the new service of type find of illicit items",
   "reportedBy": "USER1",
   "reportedAt": "2023-12-05T12:34:56",
   "status": "DRAFT",
diff --git a/src/test/resources/entity-mapping/sample-report-with-details.json b/src/test/resources/entity-mapping/sample-report-with-details.json
index e99ccf46..9ec7f4ad 100644
--- a/src/test/resources/entity-mapping/sample-report-with-details.json
+++ b/src/test/resources/entity-mapping/sample-report-with-details.json
@@ -1,12 +1,12 @@
 {
   "reportReference": "11124143",
-  "type": "FINDS",
+  "type": "FIND_6",
   "nomisType": "FIND0422",
   "incidentDateAndTime": "2023-12-05T11:34:56",
   "location": "MDI",
   "prisonId": "MDI",
   "title": "Incident Report 11124143",
-  "description": "A new incident created in the new service of type Finds",
+  "description": "A new incident created in the new service of type find of illicit items",
   "reportedBy": "USER1",
   "reportedAt": "2023-12-05T12:34:56",
   "status": "DRAFT",
@@ -103,7 +103,7 @@
   ],
   "history": [
     {
-      "type": "ASSAULT",
+      "type": "ASSAULT_5",
       "nomisType": "ASSAULTS3",
       "changedAt": "2023-12-05T12:33:56",
       "changedBy": "some-past-user",
@@ -183,7 +183,7 @@
       ]
     },
     {
-      "type": "ATTEMPTED_ESCAPE_FROM_CUSTODY",
+      "type": "ATTEMPTED_ESCAPE_FROM_PRISON_1",
       "nomisType": "ATT_ESCAPE",
       "changedAt": "2023-12-05T12:34:56",
       "changedBy": "some-past-user",


Reviews:
user: login: Mjwillis
body: LGTM
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T09:52:19Z
user: login: xoen
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T10:14:18Z
user: login: ushkarev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T10:22:48Z
user: login: xoen
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T10:31:47Z
user: login: ushkarev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T10:35:27Z
user: login: xoen
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T10:37:45Z
user: login: ushkarev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T10:39:19Z
user: login: xoen
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T10:42:14Z
user: login: xoen
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T10:45:10Z
user: login: xoen
body: As mentioned on Slack, etc...I wouldn't have added the grouping/families just yet.
But I don't wanna sound more critical than I am. The code changes themselves are good and the renaming of the codes is sensible and makes the types tidier 👍 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T10:55:25Z

Review comments:
diff_hunk: @@ -46,3 +46,148 @@ values (0, 'ABSCOND', 'Abscond'),
        (28, 'SELF_HARM', 'Self harm'),
        (29, 'TEMPORARY_RELEASE_FAILURE', 'Temporary release failure'),
        (30, 'TOOL_LOSS', 'Tool or implement loss');
+
+alter table constant_type
+  add column family_code varchar(60);
path: src/main/resources/db/migration/V1_16__type_families.sql
user: login: xoen
body: We can make `family_code` `NOT NULL`-able after `UPDATE` statements below.
created_at: 2025-04-02T10:14:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -46,3 +46,148 @@ values (0, 'ABSCOND', 'Abscond'),
        (28, 'SELF_HARM', 'Self harm'),
        (29, 'TEMPORARY_RELEASE_FAILURE', 'Temporary release failure'),
        (30, 'TOOL_LOSS', 'Tool or implement loss');
+
+alter table constant_type
+  add column family_code varchar(60);
path: src/main/resources/db/migration/V1_16__type_families.sql
user: login: ushkarev
body: this is a fair question and i wasn't sure how this would actually work – but it seems to! i think it fills the column with blank strings
created_at: 2025-04-02T10:22:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -46,3 +46,148 @@ values (0, 'ABSCOND', 'Abscond'),
        (28, 'SELF_HARM', 'Self harm'),
        (29, 'TEMPORARY_RELEASE_FAILURE', 'Temporary release failure'),
        (30, 'TOOL_LOSS', 'Tool or implement loss');
+
+alter table constant_type
+  add column family_code varchar(60);
path: src/main/resources/db/migration/V1_16__type_families.sql
user: login: xoen
body: It wasn't a question.

What I meant is that aside from when the column will be added and there will have no way of populating it, I don't think the `family_code` should be `NULL`-able (but of course it can't be `NOT NULL`-able until the `UPDATE` statements have populated this column for all the records)

> i wasn't sure how this would actually work – but it seems to!

The reason why it works is because that `ALTER TABLE constant_type ADD COLUMN family_code ...` statement doesn't specify `NOT NULL` so the new column _**is**_ `NULL`-able (again, this makes sense because there is no sensible way to populate this new column for existing records, hence the `UPDATE` statements below) - but after those statements I think it doesn't make sense for this column to be `NULL`-able (unless I'm missing something of course).
created_at: 2025-04-02T10:31:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -46,3 +46,148 @@ values (0, 'ABSCOND', 'Abscond'),
        (28, 'SELF_HARM', 'Self harm'),
        (29, 'TEMPORARY_RELEASE_FAILURE', 'Temporary release failure'),
        (30, 'TOOL_LOSS', 'Tool or implement loss');
+
+alter table constant_type
+  add column family_code varchar(60);
path: src/main/resources/db/migration/V1_16__type_families.sql
user: login: ushkarev
body: oh!! omg how stupid of me lol
sorted in bf5961b607938fcf40f511e81d5f9957d6f73ded
created_at: 2025-04-02T10:35:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -46,3 +46,148 @@ values (0, 'ABSCOND', 'Abscond'),
        (28, 'SELF_HARM', 'Self harm'),
        (29, 'TEMPORARY_RELEASE_FAILURE', 'Temporary release failure'),
        (30, 'TOOL_LOSS', 'Tool or implement loss');
+
+alter table constant_type
+  add column family_code varchar(60);
path: src/main/resources/db/migration/V1_16__type_families.sql
user: login: xoen
body: I don't think you're stupid or anything, there is nothing wrong with this migration at all, the newly added column **has to be**  `NULL`-able to begin with (unless you also provide a `DEFAULT` which negates the benefit of `NOT NULL` in this case).

But IMHO once we populated this column (as you do), I think it's sensible for it to be `NOT NULL`-able - hope I'm making sense.
created_at: 2025-04-02T10:37:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -46,3 +46,148 @@ values (0, 'ABSCOND', 'Abscond'),
        (28, 'SELF_HARM', 'Self harm'),
        (29, 'TEMPORARY_RELEASE_FAILURE', 'Temporary release failure'),
        (30, 'TOOL_LOSS', 'Tool or implement loss');
+
+alter table constant_type
+  add column family_code varchar(60);
path: src/main/resources/db/migration/V1_16__type_families.sql
user: login: ushkarev
body: stupid in the sense that i had it completely backwards 🙈 
created_at: 2025-04-02T10:39:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -19,7 +19,7 @@ enum class Status(
   AWAITING_ANALYSIS("Awaiting analysis", "AWAN"),
   IN_ANALYSIS("In analysis", "INAN"),
   INFORMATION_REQUIRED("Information required", "INREQ"),
-  INFORMATION_AMENDED("Information amened", "INAME"),
+  INFORMATION_AMENDED("Information amended", "INAME"),
path: src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/constants/Status.kt
user: login: xoen
body: Amen 🙏 
created_at: 2025-04-02T10:42:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,55 +17,55 @@ enum class Type(
   val active: Boolean = true,
 ) {
   // active
-  ABSCONDER(TypeFamily.ABSCOND, "ABSCOND"),
-  ASSAULT(TypeFamily.ASSAULT, "ASSAULTS3"),
-  ATTEMPTED_ESCAPE_FROM_CUSTODY(TypeFamily.ATTEMPTED_ESCAPE_FROM_PRISON, "ATT_ESCAPE"),
-  ATTEMPTED_ESCAPE_FROM_ESCORT(TypeFamily.ATTEMPTED_ESCAPE_FROM_ESCORT, "ATT_ESC_E"),
-  BOMB_THREAT(TypeFamily.BOMB, "BOMB"),
-  BREACH_OF_SECURITY(TypeFamily.BREACH_OF_SECURITY, "BREACH"),
-  DEATH_IN_CUSTODY(TypeFamily.DEATH_PRISONER, "DEATH"),
-  DEATH_OTHER(TypeFamily.DEATH_OTHER, "DEATH_NI"),
-  DISORDER(TypeFamily.DISORDER, "DISORDER1"),
-  DRONE_SIGHTING(TypeFamily.DRONE_SIGHTING, "DRONE2"),
-  ESCAPE_FROM_CUSTODY(TypeFamily.ESCAPE_FROM_PRISON, "ESCAPE_EST"),
-  ESCAPE_FROM_ESCORT(TypeFamily.ESCAPE_FROM_ESCORT, "ESCAPE_ESC"),
-  FINDS(TypeFamily.FIND, "FIND0422"),
-  FIRE(TypeFamily.FIRE, "FIRE"),
-  FOOD_REFUSAL(TypeFamily.FOOD_REFUSAL, "FOOD_REF"),
-  FULL_CLOSE_DOWN_SEARCH(TypeFamily.CLOSE_DOWN_SEARCH, "CLOSE_DOWN"),
-  KEY_LOCK_INCIDENT(TypeFamily.KEY_OR_LOCK, "KEY_LOCKNEW"),
-  MISCELLANEOUS(TypeFamily.MISCELLANEOUS, "MISC"),
-  RADIO_COMPROMISE(TypeFamily.RADIO_COMPROMISE, "RADIO_COMP"),
-  RELEASED_IN_ERROR(TypeFamily.RELEASE_IN_ERROR, "REL_ERROR"),
-  SELF_HARM(TypeFamily.SELF_HARM, "SELF_HARM"),
-  TEMPORARY_RELEASE_FAILURE(TypeFamily.TEMPORARY_RELEASE_FAILURE, "TRF3"),
-  TOOL_LOSS(TypeFamily.TOOL_LOSS, "TOOL_LOSS"),
+  ABSCOND_1(TypeFamily.ABSCOND, "ABSCOND"),
+  ASSAULT_5(TypeFamily.ASSAULT, "ASSAULTS3"),
+  ATTEMPTED_ESCAPE_FROM_PRISON_1(TypeFamily.ATTEMPTED_ESCAPE_FROM_PRISON, "ATT_ESCAPE"),
+  ATTEMPTED_ESCAPE_FROM_ESCORT_1(TypeFamily.ATTEMPTED_ESCAPE_FROM_ESCORT, "ATT_ESC_E"),
+  BOMB_1(TypeFamily.BOMB, "BOMB"),
+  BREACH_OF_SECURITY_1(TypeFamily.BREACH_OF_SECURITY, "BREACH"),
+  DEATH_PRISONER_1(TypeFamily.DEATH_PRISONER, "DEATH"),
+  DEATH_OTHER_1(TypeFamily.DEATH_OTHER, "DEATH_NI"),
+  DISORDER_2(TypeFamily.DISORDER, "DISORDER1"),
+  DRONE_SIGHTING_3(TypeFamily.DRONE_SIGHTING, "DRONE2"),
+  ESCAPE_FROM_PRISON_1(TypeFamily.ESCAPE_FROM_PRISON, "ESCAPE_EST"),
+  ESCAPE_FROM_ESCORT_1(TypeFamily.ESCAPE_FROM_ESCORT, "ESCAPE_ESC"),
+  FIND_6(TypeFamily.FIND, "FIND0422"),
+  FIRE_1(TypeFamily.FIRE, "FIRE"),
+  FOOD_REFUSAL_1(TypeFamily.FOOD_REFUSAL, "FOOD_REF"),
+  CLOSE_DOWN_SEARCH_1(TypeFamily.CLOSE_DOWN_SEARCH, "CLOSE_DOWN"),
+  KEY_OR_LOCK_2(TypeFamily.KEY_OR_LOCK, "KEY_LOCKNEW"),
+  MISCELLANEOUS_1(TypeFamily.MISCELLANEOUS, "MISC"),
+  RADIO_COMPROMISE_1(TypeFamily.RADIO_COMPROMISE, "RADIO_COMP"),
+  RELEASE_IN_ERROR_1(TypeFamily.RELEASE_IN_ERROR, "REL_ERROR"),
+  SELF_HARM_1(TypeFamily.SELF_HARM, "SELF_HARM"),
+  TEMPORARY_RELEASE_FAILURE_4(TypeFamily.TEMPORARY_RELEASE_FAILURE, "TRF3"),
+  TOOL_LOSS_1(TypeFamily.TOOL_LOSS, "TOOL_LOSS"),
 
   // inactive
-  DAMAGE(TypeFamily.DAMAGE, "DAMAGE", active = false),
-  OLD_ASSAULT(TypeFamily.ASSAULT, "ASSAULT", active = false),
-  OLD_ASSAULT1(TypeFamily.ASSAULT, "ASSAULTS", active = false),
-  OLD_ASSAULT2(TypeFamily.ASSAULT, "ASSAULTS1", active = false),
-  OLD_ASSAULT3(TypeFamily.ASSAULT, "ASSAULTS2", active = false),
-  OLD_BARRICADE(TypeFamily.BARRICADE, "BARRICADE", active = false),
-  OLD_CONCERTED_INDISCIPLINE(TypeFamily.CONCERTED_INDISCIPLINE, "CON_INDISC", active = false),
-  OLD_DISORDER(TypeFamily.DISORDER, "DISORDER", active = false),
-  OLD_DRONE_SIGHTING(TypeFamily.DRONE_SIGHTING, "DRONE", active = false),
-  OLD_DRONE_SIGHTING1(TypeFamily.DRONE_SIGHTING, "DRONE1", active = false),
-  OLD_DRUGS(TypeFamily.DRUGS, "DRUGS", active = false),
-  OLD_FINDS(TypeFamily.FIND, "FINDS", active = false),
-  OLD_FINDS1(TypeFamily.FIND, "FIND", active = false),
-  OLD_FINDS2(TypeFamily.FIND, "FIND1", active = false),
-  OLD_FINDS3(TypeFamily.FIND, "FIND0322", active = false),
-  OLD_FINDS4(TypeFamily.FIND, "FINDS1", active = false),
-  OLD_FIREARM_ETC(TypeFamily.FIREARM, "FIREARM_ETC", active = false),
-  OLD_HOSTAGE(TypeFamily.HOSTAGE, "HOSTAGE", active = false),
-  OLD_KEY_LOCK_INCIDENT(TypeFamily.KEY_OR_LOCK, "KEY_LOCK", active = false),
-  OLD_MOBILES(TypeFamily.MOBILE_PHONE, "MOBILES", active = false),
-  OLD_ROOF_CLIMB(TypeFamily.INCIDENT_AT_HEIGHT, "ROOF_CLIMB", active = false),
-  OLD_TEMPORARY_RELEASE_FAILURE(TypeFamily.TEMPORARY_RELEASE_FAILURE, "TRF", active = false),
-  OLD_TEMPORARY_RELEASE_FAILURE1(TypeFamily.TEMPORARY_RELEASE_FAILURE, "TRF1", active = false),
-  OLD_TEMPORARY_RELEASE_FAILURE2(TypeFamily.TEMPORARY_RELEASE_FAILURE, "TRF2", active = false),
+  DAMAGE_1(TypeFamily.DAMAGE, "DAMAGE", active = false),
+  ASSAULT_1(TypeFamily.ASSAULT, "ASSAULT", active = false),
+  ASSAULT_2(TypeFamily.ASSAULT, "ASSAULTS", active = false),
+  ASSAULT_3(TypeFamily.ASSAULT, "ASSAULTS1", active = false),
+  ASSAULT_4(TypeFamily.ASSAULT, "ASSAULTS2", active = false),
+  BARRICADE_1(TypeFamily.BARRICADE, "BARRICADE", active = false),
+  CONCERTED_INDISCIPLINE_1(TypeFamily.CONCERTED_INDISCIPLINE, "CON_INDISC", active = false),
+  DISORDER_1(TypeFamily.DISORDER, "DISORDER", active = false),
+  DRONE_SIGHTING_1(TypeFamily.DRONE_SIGHTING, "DRONE", active = false),
+  DRONE_SIGHTING_2(TypeFamily.DRONE_SIGHTING, "DRONE1", active = false),
+  DRUGS_1(TypeFamily.DRUGS, "DRUGS", active = false),
+  FIND_1(TypeFamily.FIND, "FINDS", active = false),
+  FIND_2(TypeFamily.FIND, "FIND", active = false),
+  FIND_3(TypeFamily.FIND, "FIND1", active = false),
+  FIND_4(TypeFamily.FIND, "FIND0322", active = false),
+  FIND_5(TypeFamily.FIND, "FINDS1", active = false),
path: src/main/kotlin/uk/gov/justice/digital/hmpps/incidentreporting/constants/Type.kt
user: login: xoen
body: This is so much nicer 👍 
created_at: 2025-04-02T10:45:09Z
author_association: CONTRIBUTOR

PR comments:
user: login: xoen
created_at: 2025-04-02T10:56:59Z
body: On a separate note, I imagine deploying this would break filtering on the UI (not a huge deal) until that related PR is deployed too right?
author_association: CONTRIBUTOR
user: login: ushkarev
created_at: 2025-04-02T11:08:41Z
body: yes, filtering in the dashboard will be a bit broken. it'll list all types so there will be like 6 “Assault” in the list and you cannot tell them apart 🤣
i haven't been able to actually test that page in the ui cos it requires the api to be updated first
author_association: CONTRIBUTOR
user: login: xoen
created_at: 2025-04-02T11:19:46Z
body: > yes, filtering in the dashboard will be a bit broken.

Yeah, as I said, not a biggie 👍 

> it'll list all types so there will be like 6 “Assault” in the list and you cannot tell them apart 🤣

Hence my objection for doing the grouping just yet. Maybe a simple enough workaround is to have the code in parenthesis for the time being (e.g. `Find of illicit items (FIND_6)`.
author_association: CONTRIBUTOR