Implement the 005-set-initiative feature that adds initiative values to combatants with automatic descending sort and active turn preservation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
63
specs/005-set-initiative/data-model.md
Normal file
63
specs/005-set-initiative/data-model.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user