# ApprovalStepAssignee

## Description

ApprovalStepAssignee represents one named approver on a runtime `ApprovalStep`.

## Domain Model Definitions

### Model type

Stateful

#### State Transitions

```mermaid
stateDiagram-v2
    [*] --> Pending: createApprovalRequest
    [*] --> Pending: delegateApprovalStep
    Pending --> Approved: approveApprovalStep
    Pending --> Rejected: rejectApprovalStep
    Pending --> Delegated: delegateApprovalStep
    Approved --> Pending: resubmitApprovalRequest
    Approved --> Pending: sendBackApprovalStep (target..current)
```

`[*] --> Pending: delegateApprovalStep` refers to the delegate's newly inserted row (the substituted approver), not the delegator's seat. The delegator's row follows the `Pending --> Delegated` arrow in the same command, and the new delegate row is created with `required` carried over verbatim from the originating row. `DELEGATED` rows are deliberately not reset by `resubmitApprovalRequest` or step-mode `sendBackApprovalStep` — the delegatee keeps the seat across resubmit and rewind rounds.

| Operation | From | To | Command |
|-----------|------|----|---------|
| approve | PENDING | APPROVED | [approveApprovalStep](../command/ApproveApprovalStep.md) |
| reject | PENDING | REJECTED | [rejectApprovalStep](../command/RejectApprovalStep.md) |
| delegate | PENDING | DELEGATED | [delegateApprovalStep](../command/DelegateApprovalStep.md) |
| reset | APPROVED | PENDING | [resubmitApprovalRequest](../command/ResubmitApprovalRequest.md) (all steps) / [sendBackApprovalStep](../command/SendBackApprovalStep.md) (assignees on target..current steps, step-mode rewind) |

### Command Definitions

- [approveApprovalStep](../command/ApproveApprovalStep.md)
- [rejectApprovalStep](../command/RejectApprovalStep.md)
- [delegateApprovalStep](../command/DelegateApprovalStep.md)
- [sendBackApprovalStep](../command/SendBackApprovalStep.md)
- [resubmitApprovalRequest](../command/ResubmitApprovalRequest.md)

### Query Definitions

- [listApprovalRequestsForApprover](../query/ListApprovalRequestsForApprover.md)

### Models

- ApprovalStepAssignee

### Invariants

- Exactly one of `userId` or `roleId` is set per row (XOR); after the parent step transitions to `IN_PROGRESS`, every row has `userId` set (role-expanded rows additionally retain `roleId` for group identity)
- `required` is meaningful only on user-direct rows (`roleId` is null)
- `roleQuorum` is meaningful only on role-expanded rows (`roleId` is set); on user-direct rows it must be null
- Once the parent step transitions to `IN_PROGRESS`, the assignee set is frozen — later role-membership changes do not alter the materialized rows of a step already in progress
- `resolvedAt` is null while status is `PENDING` and is set when status reaches `APPROVED`, `REJECTED`, or `DELEGATED`
- A user can hold at most one PENDING row per step at a time
- Once the parent `ApprovalRequest.status` becomes terminal via a request-level command (`WITHDRAWN` or `CANCELLED`), or non-terminal `REVISION_REQUESTED` via requester-mode `sendBackApprovalStep`, assignee rows are not updated and the status may stay `PENDING` indefinitely; inbox consumers must join to `ApprovalRequest.status` to determine actionability
- Step-mode `sendBackApprovalStep` (a target step supplied) is the exception that updates assignee rows while the request stays `PENDING`: `APPROVED` rows on the target-through-current steps are reset to `PENDING` (`resolvedAt` cleared), while `DELEGATED` rows are left as-is

### Relationships

- **Belongs To ApprovalStep**: Each assignee row references exactly one parent step
- **References User (cross-module)**: When `userId` is set, references a `User` row in `user-management` whose row is never hard-deleted, so the FK target outlives the assignee row
- **References Role (cross-module)**: When `roleId` is set, references a `Role` row in `user-management`; the `roleId` is also retained for traceability on user-materialized rows after role expansion
- **Created From ApprovalPolicyStepAssignee (optional)**: In policy mode the row is copied from an `ApprovalPolicyStepAssignee` at request creation; in direct mode there is no source policy assignee. Either way the row tracks runtime status independently of the source
- **Originates ApprovalDecision**: Step-level `ApprovalDecision` rows reference the assignee implicitly through `approvalStepId` and `decidedByUserId`
