# Tasks: Add Combatant **Input**: Design documents from `/specs/002-add-combatant/` **Prerequisites**: plan.md, spec.md, research.md, data-model.md, quickstart.md **Tests**: Included — spec success criteria SC-001 and SC-002 require all acceptance scenarios and invariants to be verified by tests. **Organization**: Single user story (P1). Tasks follow the established `advanceTurn` pattern across all three layers. ## 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) - Include exact file paths in descriptions --- ## Phase 1: Foundational (Domain Event) **Purpose**: Add the CombatantAdded event type that all layers depend on - [x] T001 Add CombatantAdded event interface and extend DomainEvent union in packages/domain/src/events.ts **Checkpoint**: CombatantAdded event type available for import --- ## Phase 2: User Story 1 - Add Combatant to Encounter (Priority: P1) 🎯 MVP **Goal**: A game master can add a new combatant to an existing encounter. The combatant is appended to the end of the initiative list without changing the active turn or round. **Independent Test**: Call `addCombatant` with an Encounter, a CombatantId, and a name. Assert the returned Encounter has the new combatant at the end, activeIndex and roundNumber unchanged, and a CombatantAdded event emitted. ### Domain Layer - [x] T002 [US1] Create addCombatant pure function in packages/domain/src/add-combatant.ts - [x] T003 [US1] Export addCombatant and AddCombatantSuccess from packages/domain/src/index.ts ### Domain Tests - [x] T004 [US1] Create acceptance tests (6 scenarios) and invariant tests (INV-1 through INV-7) in packages/domain/src/__tests__/add-combatant.test.ts ### Application Layer - [x] T005 [P] [US1] Create addCombatantUseCase in packages/application/src/add-combatant-use-case.ts - [x] T006 [US1] Export addCombatantUseCase from packages/application/src/index.ts ### Web Adapter - [x] T007 [US1] Add addCombatant callback to useEncounter hook in apps/web/src/hooks/use-encounter.ts - [x] T008 [US1] Add combatant name input and add button to apps/web/src/App.tsx **Checkpoint**: All 6 acceptance scenarios pass. User can type a name and add a combatant via the UI. `pnpm check` passes. --- ## Phase 3: Polish & Cross-Cutting Concerns - [x] T009 Run pnpm check (format + lint + typecheck + test) and fix any issues - [x] T010 Verify layer boundary compliance (domain has no outer-layer imports) --- ## Dependencies & Execution Order ### Phase Dependencies - **Foundational (Phase 1)**: No dependencies — start immediately - **User Story 1 (Phase 2)**: Depends on T001 (CombatantAdded event type) - **Polish (Phase 3)**: Depends on all Phase 2 tasks ### Within User Story 1 ``` T001 (event type) ├── T002 (domain function) → T003 (domain exports) → T004 (domain tests) └── T005 (use case) ──────→ T006 (app exports) → T007 (hook) → T008 (UI) ``` - T002 depends on T001 (needs CombatantAdded type) - T003 depends on T002 (exports the new function) - T004 depends on T003 (tests import from index) - T005 depends on T003 (use case imports domain function) — can run in parallel with T004 - T006 depends on T005 - T007 depends on T006 - T008 depends on T007 ### Parallel Opportunities - T004 (domain tests) and T005 (use case) can run in parallel after T003 - T009 and T010 can run in parallel --- ## Parallel Example: After T003 ``` # These two tasks touch different packages and can run in parallel: T004: "Acceptance + invariant tests in packages/domain/src/__tests__/add-combatant.test.ts" T005: "Use case in packages/application/src/add-combatant-use-case.ts" ``` --- ## Implementation Strategy ### MVP (This Feature) 1. T001: Add event type (foundation) 2. T002–T003: Domain function + exports 3. T004 + T005 in parallel: Tests + use case 4. T006–T008: Application exports → hook → UI 5. T009–T010: Verify everything passes ### Validation After T004: All 6 acceptance scenarios pass as pure-function tests After T008: UI allows adding combatants by name After T009: `pnpm check` passes clean (merge gate) --- ## Notes - Follow the `advanceTurn` pattern for function signature, result type, and error handling - CombatantId is passed in as input (generated by caller), not created inside domain - Name is trimmed then validated; empty after trim returns DomainError with code "invalid-name" - Commit after each task or logical group - Total: 10 tasks (1 foundational + 7 US1 + 2 polish)