Files
initiative/specs/002-add-combatant/tasks.md

4.5 KiB
Raw Blame History

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

  • 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

  • T002 [US1] Create addCombatant pure function in packages/domain/src/add-combatant.ts
  • T003 [US1] Export addCombatant and AddCombatantSuccess from packages/domain/src/index.ts

Domain Tests

  • 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

  • T005 [P] [US1] Create addCombatantUseCase in packages/application/src/add-combatant-use-case.ts
  • T006 [US1] Export addCombatantUseCase from packages/application/src/index.ts

Web Adapter

  • T007 [US1] Add addCombatant callback to useEncounter hook in apps/web/src/hooks/use-encounter.ts
  • 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

  • T009 Run pnpm check (format + lint + typecheck + test) and fix any issues
  • 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. T002T003: Domain function + exports
  3. T004 + T005 in parallel: Tests + use case
  4. T006T008: Application exports → hook → UI
  5. T009T010: 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)