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

6.8 KiB
Raw Blame History

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 CombatantUpdated event interface and add it to the DomainEvent union in packages/domain/src/events.ts
  • T002 Add EditCombatantSuccess interface and editCombatant function signature (stub returning DomainError) in packages/domain/src/edit-combatant.ts
  • 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

  • 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 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
  • 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)
  • T008 [US1] Re-export editCombatantUseCase from packages/application/src/index.ts
  • T009 [US1] Add editCombatant(id: CombatantId, newName: string) action to useEncounter hook in apps/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 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

  • T013 Run pnpm check (format + lint + typecheck + test) and fix any issues
  • 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

# 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