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:
49
specs/012-turn-navigation/research.md
Normal file
49
specs/012-turn-navigation/research.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Research: Turn Navigation
|
||||
|
||||
## R1: RetreatTurn Domain Pattern
|
||||
|
||||
**Decision**: Implement RetreatTurn as a mirror of the existing AdvanceTurn pattern -- a pure function that accepts an Encounter and returns the updated Encounter plus domain events, or a DomainError.
|
||||
|
||||
**Rationale**: The existing AdvanceTurn function (`packages/domain/src/advance-turn.ts`) establishes a clear pattern: pure function, value-based error handling via DomainError, domain events returned as data. RetreatTurn is its exact inverse, so following the same pattern maintains consistency and testability.
|
||||
|
||||
**Alternatives considered**:
|
||||
- Generic "move turn" function with direction parameter: Rejected because AdvanceTurn already exists and changing its signature would break the existing API for no benefit. Two focused functions are simpler than one parameterized function.
|
||||
- Undo/redo stack: Out of scope per spec assumptions. RetreatTurn is a positional operation, not a state-history undo.
|
||||
|
||||
## R2: Boundary Guard (Cannot Retreat Before Start)
|
||||
|
||||
**Decision**: RetreatTurn returns a DomainError when `roundNumber === 1 && activeIndex === 0`. This is the earliest possible encounter state.
|
||||
|
||||
**Rationale**: There is no meaningful "previous turn" before the encounter starts. Allowing retreat past this point would require round 0 or negative rounds, which violates INV-3 (roundNumber >= 1). The spec explicitly requires this guard (FR-003, acceptance scenario 3).
|
||||
|
||||
**Alternatives considered**:
|
||||
- Silently no-op: Rejected because domain operations should be explicit about failures (constitution principle I).
|
||||
- Allow round 0 as "setup" round: Out of scope, would require spec amendment.
|
||||
|
||||
## R3: New Domain Events (TurnRetreated, RoundRetreated)
|
||||
|
||||
**Decision**: Introduce two new domain event types: `TurnRetreated` and `RoundRetreated`, mirroring the existing `TurnAdvanced` and `RoundAdvanced` event shapes.
|
||||
|
||||
**Rationale**: The existing event system uses discriminated unions with a `type` field. Retreat events should be distinct from advance events so consumers can differentiate the direction of navigation. The shapes mirror their forward counterparts for consistency.
|
||||
|
||||
**Alternatives considered**:
|
||||
- Reuse TurnAdvanced/RoundAdvanced with a `direction` field: Rejected because it changes the existing event shape (breaking change) and complicates event consumers that only care about forward progression.
|
||||
|
||||
## R4: UI Placement -- Turn Navigation at Top
|
||||
|
||||
**Decision**: Create a new `TurnNavigation` component positioned between the header and the combatant list. Move the Next Turn button out of ActionBar into this new component. ActionBar retains only the Add Combatant form.
|
||||
|
||||
**Rationale**: The spec requires turn controls at the top of the tracker (FR-007). The current Next Turn button lives in ActionBar at the bottom. Splitting concerns (turn navigation vs. combatant management) improves the component structure and matches the spec's UX intent.
|
||||
|
||||
**Alternatives considered**:
|
||||
- Keep Next Turn in ActionBar and duplicate at top: Rejected -- redundant controls create confusion.
|
||||
- Move entire ActionBar to top: Rejected -- the Add Combatant form is secondary to turn navigation and should not compete for top-of-page prominence.
|
||||
|
||||
## R5: Disabled State for Previous Turn Button
|
||||
|
||||
**Decision**: The Previous Turn button renders in a disabled state (using the existing Button component's `disabled` prop) when the encounter is at round 1, activeIndex 0, or when there are no combatants.
|
||||
|
||||
**Rationale**: The spec requires visual indication that Previous Turn is unavailable (FR-008, FR-009). Using the existing Button disabled styling from shadcn/ui-style components ensures visual consistency.
|
||||
|
||||
**Alternatives considered**:
|
||||
- Hide the button entirely when unavailable: Rejected -- hiding causes layout shifts and makes the control harder to discover.
|
||||
Reference in New Issue
Block a user