6.8 KiB
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
- T001 Add
CombatantUpdatedevent interface and add it to theDomainEventunion inpackages/domain/src/events.ts - T002 Add
EditCombatantSuccessinterface andeditCombatantfunction signature (stub returningDomainError) inpackages/domain/src/edit-combatant.ts - T003 Re-export
editCombatantandEditCombatantSuccessfrompackages/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
- 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 - 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
- T006 [US1] Implement
editCombatantpure function inpackages/domain/src/edit-combatant.ts— find combatant by id, validate name, return updated encounter with mapped combatants list andCombatantUpdatedevent - T007 [US1] Create
editCombatantUseCaseinpackages/application/src/edit-combatant-use-case.tsfollowing the pattern inadd-combatant-use-case.ts(get → call domain → check error → save → return events) - T008 [US1] Re-export
editCombatantUseCasefrompackages/application/src/index.ts - T009 [US1] Add
editCombatant(id: CombatantId, newName: string)action touseEncounterhook inapps/web/src/hooks/use-encounter.ts - 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
- 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
- T012 [US2] Add name validation (empty/whitespace check) to
editCombatantinpackages/domain/src/edit-combatant.ts— returnDomainErrorwith 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
- T013 Run
pnpm check(format + lint + typecheck + test) and fix any issues - T014 Verify layer boundaries pass (
packages/domainhas 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
# 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)
- Complete Phase 1: Setup (T001–T003)
- Complete Phase 2: User Story 1 (T004–T010)
- STOP and VALIDATE:
pnpm checkpasses, rename works in browser - Deploy/demo if ready
Full Feature
- Setup (T001–T003) → Foundation ready
- User Story 1 (T004–T010) → Rename works end-to-end (MVP!)
- User Story 2 (T011–T012) → Error handling complete
- 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