# 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