# PipelineItemChange

## Description

PipelineItemChange is a unified append-only audit log for all non-stage changes on an item. It consolidates content edits (title/description), scalar field changes (assignee/priority/dueDate), lifecycle transitions (DRAFT/OPEN/CLOSED), and label attach/detach events into a single table. PipelineStage transitions remain in the dedicated [PipelineStageTransition](./PipelineStageTransition.md) table due to its specialized `fromStageId`/`toStageId` relations.

Each entry captures:

- `changeType`: the category of change (`CONTENT`, `FIELD`, `LIFECYCLE`, `LABEL`)
- `fieldName`: the specific field (`TITLE`, `DESCRIPTION`, `ASSIGNEE`, `PRIORITY`, `DUE_DATE`, `LIFECYCLE`, `LABEL`)
- `prevValue` / `newValue`: the before/after values as opaque strings (UUID, enum, ISO date, or free text depending on field)
- `changedByUserId`: the actor
- `createdAt`: when the change occurred (the standard timestamp column doubles as the event time; rows are immutable, so no separate field is needed)

Records are immutable once created.

## Domain Model Definitions

### Model type

AppendOnly

### Command Definitions

- None (entries are created internally by [UpdatePipelineItem](../command/UpdatePipelineItem.md), [OpenPipelineItem](../command/OpenPipelineItem.md), [ClosePipelineItem](../command/ClosePipelineItem.md), [ReopenPipelineItem](../command/ReopenPipelineItem.md), [AttachLabelToPipelineItem](../command/AttachLabelToPipelineItem.md), and [DetachLabelFromPipelineItem](../command/DetachLabelFromPipelineItem.md))

### Query Definitions

- None (read access goes through PipelineItem's `changes` relation in GraphQL)

### Models

- PipelineItemChange

### Invariants

- PipelineItem ID is required and must reference an existing item
- Changed-by user ID is required
- The event timestamp is `createdAt`, set automatically at creation time
- `changeType` is required and must be one of `CONTENT`, `FIELD`, `LIFECYCLE`, `LABEL`
- `fieldName` is required and must be one of `TITLE`, `DESCRIPTION`, `ASSIGNEE`, `PRIORITY`, `DUE_DATE`, `LIFECYCLE`, `LABEL`
- `prevValue` and `newValue` are both optional strings; null means "unset / cleared"
- A change is only recorded when the field's previous and new values actually differ
- One PipelineItemChange row is written per changed field per command invocation
- Records are immutable -- they cannot be updated or deleted after creation, except that DeletePipelineItem cascade-deletes an item's change rows together with the item

### Relationships

- **Belongs To PipelineItem**: Each change belongs to an item via `itemId`
- **References User**: Each change records which user triggered it via `changedByUserId` from the user-management module
