# Data Model: Remove Combatant **Feature**: 003-remove-combatant **Date**: 2026-03-03 ## Existing Entities (no changes) ### Encounter | Field | Type | Description | |-------|------|-------------| | combatants | readonly Combatant[] | Ordered list of participants | | activeIndex | number | Index of the combatant whose turn it is | | roundNumber | number | Current round (≥ 1, never changes on removal) | ### Combatant | Field | Type | Description | |-------|------|-------------| | id | CombatantId (branded string) | Unique identifier | | name | string | Display name | ## New Event Type ### CombatantRemoved | Field | Type | Description | |-------|------|-------------| | type | "CombatantRemoved" (literal) | Event discriminant | | combatantId | CombatantId | ID of the removed combatant | | name | string | Name of the removed combatant | Added to the `DomainEvent` discriminated union alongside `TurnAdvanced`, `RoundAdvanced`, and `CombatantAdded`. ## New Domain Function ### removeCombatant | Parameter | Type | Description | |-----------|------|-------------| | encounter | Encounter | Current encounter state | | id | CombatantId | ID of combatant to remove | **Returns**: `RemoveCombatantSuccess | DomainError` ### RemoveCombatantSuccess | Field | Type | Description | |-------|------|-------------| | encounter | Encounter | Updated encounter after removal | | events | DomainEvent[] | Exactly one CombatantRemoved event | ### DomainError (existing, reused) Returned with code `"combatant-not-found"` when ID does not match any combatant. ## State Transition Rules ### activeIndex Adjustment Given removal of combatant at index `removedIdx` with current `activeIndex`: | Condition | New activeIndex | |-----------|----------------| | removedIdx > activeIndex | activeIndex (unchanged) | | removedIdx < activeIndex | activeIndex - 1 | | removedIdx === activeIndex, not last in list | activeIndex (next slides in) | | removedIdx === activeIndex, last in list | 0 (wrap) | | Only combatant removed (list becomes empty) | 0 |