Implement the 003-remove-combatant feature that adds the possibility to remove a combatant from an encounter

This commit is contained in:
Lukas
2026-03-03 23:46:47 +01:00
parent 9d7b174867
commit aed234de7b
16 changed files with 763 additions and 6 deletions

View File

@@ -0,0 +1,117 @@
# Tasks: Remove Combatant
**Input**: Design documents from `/specs/003-remove-combatant/`
**Prerequisites**: plan.md, spec.md, research.md, data-model.md
**Tests**: Included — spec requires all six acceptance scenarios as automated tests (SC-002).
**Organization**: Tasks grouped by user story for independent implementation and testing.
## Format: `[ID] [P?] [Story] Description`
- **[P]**: Can run in parallel (different files, no dependencies)
- **[Story]**: Which user story this task belongs to (e.g., US1, US2)
- Exact file paths included in descriptions
## Phase 1: Foundational (Event Type)
**Purpose**: Add the CombatantRemoved event type that all subsequent tasks depend on.
- [x] T001 Add `CombatantRemoved` interface and extend `DomainEvent` union in `packages/domain/src/events.ts`
- [x] T002 Export `CombatantRemoved` type from `packages/domain/src/index.ts`
**Checkpoint**: CombatantRemoved event type available for domain function and UI event display.
---
## Phase 2: User Story 1 - Remove Combatant Domain Logic (Priority: P1) MVP
**Goal**: Pure `removeCombatant` domain function that removes a combatant by ID, adjusts activeIndex correctly, preserves roundNumber, and emits CombatantRemoved.
**Independent Test**: Call `removeCombatant` with various encounter states and verify combatant list, activeIndex, roundNumber, events, and error cases.
### Tests for User Story 1
> **NOTE: Write these tests FIRST, ensure they FAIL before implementation**
- [x] T003 [US1] Write acceptance tests for `removeCombatant` in `packages/domain/src/__tests__/remove-combatant.test.ts` covering all 6 spec scenarios: remove after active (AS-1), remove before active (AS-2), remove active combatant mid-list (AS-3), remove active combatant at end/wrap (AS-4), remove only combatant (AS-5), ID not found error (AS-6). Also test: event shape (CombatantRemoved with id+name), roundNumber invariance, and determinism.
### Implementation for User Story 1
- [x] T004 [US1] Implement `removeCombatant` pure function and `RemoveCombatantSuccess` type in `packages/domain/src/remove-combatant.ts` — find combatant by ID, compute new activeIndex per data-model rules, filter combatant list, emit CombatantRemoved event, return DomainError for not-found
- [x] T005 [US1] Export `removeCombatant` and `RemoveCombatantSuccess` from `packages/domain/src/index.ts`
**Checkpoint**: All 6 acceptance tests pass. Domain function is complete and independently testable.
---
## Phase 3: User Story 2 - Application + UI Wiring (Priority: P2)
**Goal**: Wire removeCombatant through application use case and expose via minimal UI with a remove button per combatant.
**Independent Test**: Render encounter UI, click remove on a combatant, verify it disappears from the list and event log updates.
### Implementation for User Story 2
- [x] T006 [P] [US2] Create `removeCombatantUseCase` in `packages/application/src/remove-combatant-use-case.ts` — follows existing pattern: `store.get()``removeCombatant()``store.save()` → return events or DomainError
- [x] T007 [US2] Export `removeCombatantUseCase` from `packages/application/src/index.ts`
- [x] T008 [US2] Add `removeCombatant(id: CombatantId)` callback to `useEncounter` hook in `apps/web/src/hooks/use-encounter.ts` — call use case, append events to log on success
- [x] T009 [US2] Add remove button per combatant and `CombatantRemoved` event display case in `apps/web/src/App.tsx`
**Checkpoint**: Full vertical slice works — GM can remove combatants from UI, initiative order updates correctly, event log shows removal.
---
## Phase 4: Polish & Cross-Cutting Concerns
- [x] T010 Run `pnpm check` (format + lint + typecheck + test) and fix any issues
---
## Dependencies & Execution Order
### Phase Dependencies
- **Phase 1 (Foundational)**: No dependencies — start immediately
- **Phase 2 (US1 Domain)**: Depends on Phase 1 (needs CombatantRemoved type)
- **Phase 3 (US2 App+UI)**: Depends on Phase 2 (needs domain function)
- **Phase 4 (Polish)**: Depends on Phase 3
### Within Each Phase
- T001 → T002 (export after defining)
- T003 (tests first) → T004 (implement) → T005 (export)
- T006 → T007 (export after creating use case file)
- T008 depends on T006+T007 (needs use case)
- T009 depends on T008 (needs hook callback)
### Parallel Opportunities
- Within T003, individual test cases are independent
---
## Implementation Strategy
### MVP First (User Story 1 Only)
1. Complete Phase 1: Event type (T001T002)
2. Complete Phase 2: Domain tests + function (T003T005)
3. **STOP and VALIDATE**: All 6 acceptance tests pass
4. Domain is complete and usable without UI
### Full Feature
1. Phase 1 → Phase 2 → Phase 3 → Phase 4
2. Each phase adds a testable increment
3. Commit after each phase checkpoint
---
## Notes
- [P] tasks = different files, no dependencies
- [Story] label maps task to specific user story
- Tests written first (TDD) per spec requirement SC-002
- Commit after each phase checkpoint
- Total: 10 tasks across 4 phases