8.2 KiB
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
EncounterClearedevent interface (withtype: "EncounterCleared"andcombatantCount: numberfields) and add it to theDomainEventunion type inpackages/domain/src/events.ts - T002 Create
clearEncounterpure function inpackages/domain/src/clear-encounter.ts— takes anEncounter, returnsClearEncounterSuccess(with{ encounter: { combatants: [], activeIndex: 0, roundNumber: 1 }, events: [EncounterCleared] }) orDomainError(code"encounter-already-empty"whencombatants.length === 0). ExportClearEncounterSuccesstype. Follow the pattern inpackages/domain/src/remove-combatant.ts - T003 Export
clearEncounter,ClearEncounterSuccess, andEncounterClearedfrompackages/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
clearEncounterinpackages/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) emitsEncounterClearedevent with correctcombatantCount, (6) determinism — same input always produces same output
Implementation for User Story 1
- T005 [P] [US1] Create
clearEncounterUseCaseinpackages/application/src/clear-encounter-use-case.ts— follow the pattern inremove-combatant-use-case.ts: get encounter from store, callclearEncounter, check forDomainError, save result, return events - T006 [US1] Export
clearEncounterUseCasefrompackages/application/src/index.ts - T007 [US1] Update
loadEncounterinapps/web/src/persistence/encounter-storage.tsto handle empty combatant arrays — whencombatantsis a valid empty array, return{ combatants: [], activeIndex: 0, roundNumber: 1 }directly instead of routing throughcreateEncounter()(which rejects empty arrays) - T008 [US1] Add
clearEncountercallback touseEncounterhook inapps/web/src/hooks/use-encounter.ts— callclearEncounterUseCase(makeStore()), check forDomainError, update events. Also resetnextId.currentto 0 after clearing. ReturnclearEncounterfrom the hook - T009 [P] [US1] Add
onClearEncounterprop and clear button (using Trash2 icon from Lucide) to theTurnNavigationcomponent inapps/web/src/components/turn-navigation.tsx— place it near the round/turn controls, usevariant="ghost"andsize="icon"styling consistent with existing buttons - T010 [US1] Wire
clearEncounterfromuseEncounterhook toTurnNavigationasonClearEncounterprop inapps/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 executingclearEncounterUseCasein theclearEncountercallback inapps/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 === 0inapps/web/src/components/turn-navigation.tsx— pass combatant count or adisabledflag 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 T005–T008 [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)
- Complete Phase 1: Foundational (T001–T003)
- Complete Phase 2: User Story 1 (T004–T010)
- STOP and VALIDATE: Test clearing end-to-end, verify persistence
- Feature is usable at this point (clearing works, just no confirmation gate)
Full Delivery
- Complete Phases 1–2 → Clear encounter works
- Complete Phase 3 → Confirmation prevents accidental clears
- Complete Phase 4 → Merge gate verified
- 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