Implement the 012-turn-navigation feature that adds a RetreatTurn domain operation and relocates turn controls to a navigation bar at the top of the encounter tracker

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-05 23:11:11 +01:00
parent a0d85a07e3
commit 7d440677be
19 changed files with 946 additions and 13 deletions

View File

@@ -0,0 +1,55 @@
# Domain API Contract: Turn Navigation
## retreatTurn(encounter: Encounter): RetreatTurnSuccess | DomainError
### Input
- `encounter`: Encounter (combatants, activeIndex, roundNumber)
### Success Output
```
{
encounter: Encounter // Updated state
events: DomainEvent[] // TurnRetreated, optionally RoundRetreated
}
```
### Error Cases
| Condition | Error Code | Message |
|-----------|------------|---------|
| combatants.length === 0 | `invalid-encounter` | Cannot retreat turn on an encounter with no combatants |
| roundNumber === 1 && activeIndex === 0 | `no-previous-turn` | Cannot retreat before the start of the encounter |
### Event Contracts
**TurnRetreated** (always emitted on success):
```
{
type: "TurnRetreated"
previousCombatantId: CombatantId // Was active before retreat
newCombatantId: CombatantId // Now active after retreat
roundNumber: number // Round number after retreat
}
```
**RoundRetreated** (emitted when crossing round boundary):
```
{
type: "RoundRetreated"
newRoundNumber: number // Decremented round number
}
```
**Emission order**: TurnRetreated first, then RoundRetreated (when applicable).
## UI Contract: TurnNavigation Component
### Props
- `encounter`: Encounter (current state)
- `onAdvanceTurn`: () => void
- `onRetreatTurn`: () => void
### Behavior
- Previous Turn button: calls onRetreatTurn; disabled when roundNumber === 1 && activeIndex === 0, or combatants.length === 0
- Next Turn button: calls onAdvanceTurn; disabled when combatants.length === 0
- Displays: round number and active combatant name