Files
initiative/specs/005-set-initiative/data-model.md

64 lines
2.6 KiB
Markdown

# Data Model: Set Initiative
## Entity Changes
### Combatant (modified)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| id | CombatantId (branded string) | Yes | Unique identifier |
| name | string | Yes | Display name (non-empty, trimmed) |
| initiative | integer | No | Initiative value for turn ordering. Unset means "not yet rolled." |
**Validation rules**:
- `initiative` must be an integer when set (no floats, NaN, or Infinity)
- Zero and negative integers are valid
- Unset (`undefined`) is valid — combatant has not rolled initiative yet
### Encounter (unchanged structure, new ordering behavior)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| combatants | readonly Combatant[] | Yes | Ordered list. Now sorted by initiative descending (unset last, stable sort for ties). |
| activeIndex | number | Yes | Index of the active combatant. Adjusted to follow the active combatant's identity through reorders. |
| roundNumber | number | Yes | Current round (≥ 1). Unchanged by initiative operations. |
**Ordering invariant**: After any `setInitiative` call, `combatants` is sorted such that:
1. Combatants with initiative come first, ordered highest to lowest
2. Combatants without initiative come last
3. Ties within each group preserve relative insertion order (stable sort)
## New Domain Event
### InitiativeSet
Emitted when a combatant's initiative value is set, changed, or cleared.
| Field | Type | Description |
|-------|------|-------------|
| type | "InitiativeSet" | Event discriminant |
| combatantId | CombatantId | The combatant whose initiative changed |
| previousValue | integer or undefined | The initiative value before the change |
| newValue | integer or undefined | The initiative value after the change |
## State Transitions
### setInitiative(encounter, combatantId, value)
**Input**: Current encounter, target combatant id, new initiative value (integer or undefined to clear)
**Output**: Updated encounter with reordered combatants and adjusted activeIndex, plus events
**Error conditions**:
- `combatant-not-found`: No combatant with the given id exists in the encounter
- `invalid-initiative`: Value is not an integer (when defined)
**Transition logic**:
1. Find target combatant by id → error if not found
2. Validate value is integer (when defined) → error if invalid
3. Record the active combatant's id (for preservation)
4. Update the target combatant's initiative value
5. Stable-sort combatants: initiative descending, unset last
6. Find the active combatant's new index in the sorted array
7. Return new encounter + `InitiativeSet` event