Files
initiative/specs/023-clear-encounter/tasks.md

8.2 KiB
Raw Blame History

Tasks: Clear Encounter

Input: Design documents from /specs/023-clear-encounter/ Prerequisites: plan.md, spec.md, research.md, data-model.md, quickstart.md

Tests: Included — domain tests follow existing patterns (pure function assertions).

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: Foundational (Blocking Prerequisites)

Purpose: Domain event type and pure function that all subsequent work depends on

⚠️ CRITICAL: No user story work can begin until this phase is complete

  • T001 Add EncounterCleared event interface (with type: "EncounterCleared" and combatantCount: number fields) and add it to the DomainEvent union type in packages/domain/src/events.ts
  • T002 Create clearEncounter pure function in packages/domain/src/clear-encounter.ts — takes an Encounter, returns ClearEncounterSuccess (with { encounter: { combatants: [], activeIndex: 0, roundNumber: 1 }, events: [EncounterCleared] }) or DomainError (code "encounter-already-empty" when combatants.length === 0). Export ClearEncounterSuccess type. Follow the pattern in packages/domain/src/remove-combatant.ts
  • T003 Export clearEncounter, ClearEncounterSuccess, and EncounterCleared from packages/domain/src/index.ts

Checkpoint: Domain layer complete — clearEncounter is a pure, testable function


Phase 2: User Story 1 — Clear Encounter to Start Fresh (Priority: P1) 🎯 MVP

Goal: Users can clear all combatants and reset round/turn counters with a single action, with the cleared state persisting across page refreshes.

Independent Test: Add several combatants, advance through rounds, clear the encounter, verify all combatants removed and counters reset to round 1 / index 0. Refresh the page and verify empty state persists.

Tests for User Story 1

  • T004 [P] [US1] Write domain tests for clearEncounter in packages/domain/src/__tests__/clear-encounter.test.ts — cover: (1) clearing encounter with multiple combatants at round 3 returns empty encounter with roundNumber 1 and activeIndex 0, (2) clearing encounter with single combatant works, (3) clearing encounter with combatants that have HP/AC/conditions/concentration removes all data, (4) clearing already-empty encounter returns DomainError with code "encounter-already-empty", (5) emits EncounterCleared event with correct combatantCount, (6) determinism — same input always produces same output

Implementation for User Story 1

  • T005 [P] [US1] Create clearEncounterUseCase in packages/application/src/clear-encounter-use-case.ts — follow the pattern in remove-combatant-use-case.ts: get encounter from store, call clearEncounter, check for DomainError, save result, return events
  • T006 [US1] Export clearEncounterUseCase from packages/application/src/index.ts
  • T007 [US1] Update loadEncounter in apps/web/src/persistence/encounter-storage.ts to handle empty combatant arrays — when combatants is a valid empty array, return { combatants: [], activeIndex: 0, roundNumber: 1 } directly instead of routing through createEncounter() (which rejects empty arrays)
  • T008 [US1] Add clearEncounter callback to useEncounter hook in apps/web/src/hooks/use-encounter.ts — call clearEncounterUseCase(makeStore()), check for DomainError, update events. Also reset nextId.current to 0 after clearing. Return clearEncounter from the hook
  • T009 [P] [US1] Add onClearEncounter prop and clear button (using Trash2 icon from Lucide) to the TurnNavigation component in apps/web/src/components/turn-navigation.tsx — place it near the round/turn controls, use variant="ghost" and size="icon" styling consistent with existing buttons
  • T010 [US1] Wire clearEncounter from useEncounter hook to TurnNavigation as onClearEncounter prop in apps/web/src/App.tsx

Checkpoint: User Story 1 is fully functional — clearing works end-to-end and persists across refreshes


Phase 3: User Story 2 — Confirmation Before Clearing (Priority: P1)

Goal: Users are prompted for confirmation before the encounter is cleared, preventing accidental data loss. The clear button is disabled when there are no combatants.

Independent Test: Click the clear button, verify a confirmation prompt appears, cancel and verify encounter unchanged, confirm and verify encounter cleared. Verify the button is disabled when the encounter is already empty.

Implementation for User Story 2

  • T011 [US2] Add window.confirm("Clear the entire encounter? This cannot be undone.") gate before executing clearEncounterUseCase in the clearEncounter callback in apps/web/src/hooks/use-encounter.ts — if user cancels, return early without modifying state (FR-006)
  • T012 [US2] Disable the clear encounter button when encounter.combatants.length === 0 in apps/web/src/components/turn-navigation.tsx — pass combatant count or a disabled flag via props (FR-008)

Checkpoint: User Story 2 complete — accidental clears are prevented, button disabled when empty


Phase 4: Polish & Cross-Cutting Concerns

Purpose: Verify all checks pass and feature is merge-ready

  • T013 Run pnpm check (knip + format + lint + typecheck + test) to verify merge gate passes

Dependencies & Execution Order

Phase Dependencies

  • Foundational (Phase 1): No dependencies — can start immediately
  • User Story 1 (Phase 2): Depends on Phase 1 completion
  • User Story 2 (Phase 3): Depends on Phase 2 completion (US2 modifies files created/modified in US1)
  • Polish (Phase 4): Depends on all phases complete

User Story Dependencies

  • User Story 1 (P1): Can start after Foundational — no dependencies on other stories
  • User Story 2 (P1): Depends on User Story 1 — adds confirmation gate to the clearEncounter callback and disabled state to the button created in US1

Within Each Phase

  • Tasks marked [P] can run in parallel (different files)
  • Sequential tasks depend on prior tasks in the same phase

Parallel Opportunities

  • Phase 1: T001 must complete before T002 (T002 imports EncounterCleared). T003 depends on both.
  • Phase 2: T004 (tests) and T005 (use case) can run in parallel [P]. T009 (UI) can run in parallel with T005T008 [P]. T010 depends on T008 and T009.
  • Phase 3: T011 and T012 modify different files but T012 depends on the prop interface from T009, so they should run sequentially.

Parallel Example: User Story 1

# After Phase 1 completes, launch in parallel:
Task T004: "Write domain tests in packages/domain/src/__tests__/clear-encounter.test.ts"
Task T005: "Create clearEncounterUseCase in packages/application/src/clear-encounter-use-case.ts"
Task T009: "Add clear button to apps/web/src/components/turn-navigation.tsx"

# Then sequentially:
Task T006: "Export use case from packages/application/src/index.ts"
Task T007: "Update loadEncounter in apps/web/src/persistence/encounter-storage.ts"
Task T008: "Add clearEncounter to apps/web/src/hooks/use-encounter.ts"
Task T010: "Wire clearEncounter in apps/web/src/App.tsx"

Implementation Strategy

MVP First (User Story 1 Only)

  1. Complete Phase 1: Foundational (T001T003)
  2. Complete Phase 2: User Story 1 (T004T010)
  3. STOP and VALIDATE: Test clearing end-to-end, verify persistence
  4. Feature is usable at this point (clearing works, just no confirmation gate)

Full Delivery

  1. Complete Phases 12 → Clear encounter works
  2. Complete Phase 3 → Confirmation prevents accidental clears
  3. Complete Phase 4 → Merge gate verified
  4. Feature is complete and merge-ready

Notes

  • [P] tasks = different files, no dependencies
  • [Story] label maps task to specific user story for traceability
  • US2 is intentionally sequenced after US1 because it modifies the same files (hook, component)
  • The window.confirm() approach keeps the domain pure — confirmation logic stays in the adapter layer
  • Commit after each phase for clean git history