Implement the 004-edit-combatant feature that adds the possibility to change a combatants name
This commit is contained in:
147
specs/004-edit-combatant/tasks.md
Normal file
147
specs/004-edit-combatant/tasks.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# Tasks: Edit Combatant
|
||||
|
||||
**Input**: Design documents from `/specs/004-edit-combatant/`
|
||||
**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/
|
||||
|
||||
**Tests**: Tests are included as this project follows test-driven patterns established by prior features.
|
||||
|
||||
**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
|
||||
|
||||
## 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)
|
||||
- Include exact file paths in descriptions
|
||||
|
||||
## Phase 1: Setup (Shared Infrastructure)
|
||||
|
||||
**Purpose**: Add the `CombatantUpdated` event type shared by all user stories
|
||||
|
||||
- [x] T001 Add `CombatantUpdated` event interface and add it to the `DomainEvent` union in `packages/domain/src/events.ts`
|
||||
- [x] T002 Add `EditCombatantSuccess` interface and `editCombatant` function signature (stub returning `DomainError`) in `packages/domain/src/edit-combatant.ts`
|
||||
- [x] T003 Re-export `editCombatant` and `EditCombatantSuccess` from `packages/domain/src/index.ts`
|
||||
|
||||
**Checkpoint**: Domain types compile, `editCombatant` exists as a stub
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: User Story 1 - Rename a Combatant (Priority: P1) 🎯 MVP
|
||||
|
||||
**Goal**: A user can rename an existing combatant by id. The encounter state is updated in-place with a `CombatantUpdated` event emitted. Turn order and round number are preserved.
|
||||
|
||||
**Independent Test**: Create an encounter with combatants, edit one name, verify updated name + unchanged activeIndex/roundNumber + correct event.
|
||||
|
||||
### Tests for User Story 1
|
||||
|
||||
> **NOTE: Write these tests FIRST, ensure they FAIL before implementation**
|
||||
|
||||
- [x] T004 [US1] Write acceptance scenario tests in `packages/domain/src/__tests__/edit-combatant.test.ts`: (1) rename succeeds with correct event containing combatantId, oldName, newName; (2) activeIndex and roundNumber preserved when renaming the active combatant; (3) combatant list order preserved; (4) renaming to same name still emits event
|
||||
- [x] T005 [US1] Write invariant tests in `packages/domain/src/__tests__/edit-combatant.test.ts`: (INV-1) determinism — same inputs produce same outputs; (INV-2) exactly one event emitted on success; (INV-3) original encounter is not mutated
|
||||
|
||||
### Implementation for User Story 1
|
||||
|
||||
- [x] T006 [US1] Implement `editCombatant` pure function in `packages/domain/src/edit-combatant.ts` — find combatant by id, validate name, return updated encounter with mapped combatants list and `CombatantUpdated` event
|
||||
- [x] T007 [US1] Create `editCombatantUseCase` in `packages/application/src/edit-combatant-use-case.ts` following the pattern in `add-combatant-use-case.ts` (get → call domain → check error → save → return events)
|
||||
- [x] T008 [US1] Re-export `editCombatantUseCase` from `packages/application/src/index.ts`
|
||||
- [x] T009 [US1] Add `editCombatant(id: CombatantId, newName: string)` action to `useEncounter` hook in `apps/web/src/hooks/use-encounter.ts`
|
||||
- [x] T010 [US1] Add inline name editing UI for each combatant in `apps/web/src/App.tsx` — click name to edit via input field, confirm on Enter or blur
|
||||
|
||||
**Checkpoint**: User Story 1 fully functional — renaming works end-to-end, all tests pass
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 2 - Error Feedback on Invalid Edit (Priority: P2)
|
||||
|
||||
**Goal**: Invalid edit attempts (non-existent id, empty/whitespace name) return clear errors with no side effects on encounter state.
|
||||
|
||||
**Independent Test**: Attempt to edit a non-existent combatant id and an empty name, verify error returned and encounter unchanged.
|
||||
|
||||
### Tests for User Story 2
|
||||
|
||||
- [x] T011 [US2] Write error scenario tests in `packages/domain/src/__tests__/edit-combatant.test.ts`: (1) non-existent id returns `"combatant-not-found"` error; (2) empty name returns `"invalid-name"` error; (3) whitespace-only name returns `"invalid-name"` error; (4) empty encounter returns `"combatant-not-found"` for any id
|
||||
|
||||
### Implementation for User Story 2
|
||||
|
||||
- [x] T012 [US2] Add name validation (empty/whitespace check) to `editCombatant` in `packages/domain/src/edit-combatant.ts` — return `DomainError` with code `"invalid-name"` (should already be partially covered by T006; this task ensures the guard is correct and tested)
|
||||
|
||||
**Checkpoint**: Error paths fully tested, `pnpm check` passes
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Polish & Cross-Cutting Concerns
|
||||
|
||||
**Purpose**: Final validation across all stories
|
||||
|
||||
- [x] T013 Run `pnpm check` (format + lint + typecheck + test) and fix any issues
|
||||
- [x] T014 Verify layer boundaries pass (`packages/domain` has no application/web imports)
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
|
||||
- **Setup (Phase 1)**: No dependencies — can start immediately
|
||||
- **User Story 1 (Phase 2)**: Depends on Setup (T001–T003)
|
||||
- **User Story 2 (Phase 3)**: Depends on Setup (T001–T003); can run in parallel with US1 for tests, but implementation builds on T006
|
||||
- **Polish (Phase 4)**: Depends on all user stories being complete
|
||||
|
||||
### User Story Dependencies
|
||||
|
||||
- **User Story 1 (P1)**: Can start after Setup — no dependencies on other stories
|
||||
- **User Story 2 (P2)**: Error handling is part of the same domain function as US1; tests can be written in parallel, but implementation in T012 refines the function created in T006
|
||||
|
||||
### Within Each User Story
|
||||
|
||||
- Tests MUST be written and FAIL before implementation
|
||||
- Domain function before use case
|
||||
- Use case before hook
|
||||
- Hook before UI
|
||||
|
||||
### Parallel Opportunities
|
||||
|
||||
- T004 and T005 (US1 tests) target the same file — execute sequentially
|
||||
- T007 and T008 (use case + export) are sequential but fast
|
||||
- T011 (US2 tests) can be written in parallel with US1 implementation (T006–T010)
|
||||
- T013 and T014 (polish) can run in parallel
|
||||
|
||||
---
|
||||
|
||||
## Parallel Example: User Story 1
|
||||
|
||||
```bash
|
||||
# Write both test groups in parallel:
|
||||
Task T004: "Acceptance scenario tests in packages/domain/src/__tests__/edit-combatant.test.ts"
|
||||
Task T005: "Invariant tests in packages/domain/src/__tests__/edit-combatant.test.ts"
|
||||
|
||||
# Then implement sequentially (each depends on prior):
|
||||
Task T006: Domain function → T007: Use case → T008: Export → T009: Hook → T010: UI
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### MVP First (User Story 1 Only)
|
||||
|
||||
1. Complete Phase 1: Setup (T001–T003)
|
||||
2. Complete Phase 2: User Story 1 (T004–T010)
|
||||
3. **STOP and VALIDATE**: `pnpm check` passes, rename works in browser
|
||||
4. Deploy/demo if ready
|
||||
|
||||
### Full Feature
|
||||
|
||||
1. Setup (T001–T003) → Foundation ready
|
||||
2. User Story 1 (T004–T010) → Rename works end-to-end (MVP!)
|
||||
3. User Story 2 (T011–T012) → Error handling complete
|
||||
4. Polish (T013–T014) → Final validation
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- [P] tasks = different files, no dependencies
|
||||
- [Story] label maps task to specific user story for traceability
|
||||
- T004 and T005 both write to the same test file — execute sequentially
|
||||
- Commit after each phase or logical group
|
||||
- Stop at any checkpoint to validate story independently
|
||||
Reference in New Issue
Block a user