Files
initiative/specs/004-edit-combatant/tasks.md

148 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 (T001T003)
- **User Story 2 (Phase 3)**: Depends on Setup (T001T003); 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 (T006T010)
- 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 (T001T003)
2. Complete Phase 2: User Story 1 (T004T010)
3. **STOP and VALIDATE**: `pnpm check` passes, rename works in browser
4. Deploy/demo if ready
### Full Feature
1. Setup (T001T003) → Foundation ready
2. User Story 1 (T004T010) → Rename works end-to-end (MVP!)
3. User Story 2 (T011T012) → Error handling complete
4. Polish (T013T014) → 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