Implement the 023-clear-encounter feature that adds a clear encounter button with confirmation dialog to remove all combatants and reset round/turn counters, with the cleared state persisting across page refreshes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
34
specs/023-clear-encounter/checklists/requirements.md
Normal file
34
specs/023-clear-encounter/checklists/requirements.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Specification Quality Checklist: Clear Encounter
|
||||
|
||||
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||
**Created**: 2026-03-09
|
||||
**Feature**: [spec.md](../spec.md)
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [x] No implementation details (languages, frameworks, APIs)
|
||||
- [x] Focused on user value and business needs
|
||||
- [x] Written for non-technical stakeholders
|
||||
- [x] All mandatory sections completed
|
||||
|
||||
## Requirement Completeness
|
||||
|
||||
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||
- [x] Requirements are testable and unambiguous
|
||||
- [x] Success criteria are measurable
|
||||
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||
- [x] All acceptance scenarios are defined
|
||||
- [x] Edge cases are identified
|
||||
- [x] Scope is clearly bounded
|
||||
- [x] Dependencies and assumptions identified
|
||||
|
||||
## Feature Readiness
|
||||
|
||||
- [x] All functional requirements have clear acceptance criteria
|
||||
- [x] User scenarios cover primary flows
|
||||
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||
- [x] No implementation details leak into specification
|
||||
|
||||
## Notes
|
||||
|
||||
- All items pass. Spec is ready for `/speckit.clarify` or `/speckit.plan`.
|
||||
43
specs/023-clear-encounter/data-model.md
Normal file
43
specs/023-clear-encounter/data-model.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Data Model: 023-clear-encounter
|
||||
|
||||
## Entities
|
||||
|
||||
### Encounter (existing — no schema changes)
|
||||
|
||||
The `Encounter` type is unchanged. The clear operation produces an encounter with:
|
||||
|
||||
| Field | Cleared Value | Notes |
|
||||
|---------------|---------------|--------------------------------|
|
||||
| `combatants` | `[]` | Empty readonly array |
|
||||
| `activeIndex` | `0` | Reset to initial |
|
||||
| `roundNumber` | `1` | Reset to initial |
|
||||
|
||||
No new fields or types are added to the data model.
|
||||
|
||||
## Events
|
||||
|
||||
### EncounterCleared (new)
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------------------|----------|--------------------------------------------------|
|
||||
| `type` | `string` | Discriminant: `"EncounterCleared"` |
|
||||
| `combatantCount` | `number` | Number of combatants that were removed |
|
||||
|
||||
Minimal event shape — captures the count of cleared combatants for observability. No need to record the full pre-clear state (MVP baseline does not include undo).
|
||||
|
||||
## State Transitions
|
||||
|
||||
```
|
||||
clearEncounter(encounter: Encounter) → ClearEncounterSuccess | DomainError
|
||||
|
||||
Input: Any Encounter with combatants.length > 0
|
||||
Output: { encounter: { combatants: [], activeIndex: 0, roundNumber: 1 },
|
||||
events: [EncounterCleared] }
|
||||
|
||||
Error: encounter.combatants.length === 0 → DomainError("encounter-already-empty")
|
||||
```
|
||||
|
||||
## Persistence Impact
|
||||
|
||||
- `saveEncounter()`: No changes — already serializes any `Encounter` to JSON.
|
||||
- `loadEncounter()`: Must handle `combatants: []` case by returning the empty encounter directly instead of routing through `createEncounter()` (which rejects empty combatant arrays).
|
||||
75
specs/023-clear-encounter/plan.md
Normal file
75
specs/023-clear-encounter/plan.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Implementation Plan: Clear Encounter
|
||||
|
||||
**Branch**: `023-clear-encounter` | **Date**: 2026-03-09 | **Spec**: [spec.md](./spec.md)
|
||||
**Input**: Feature specification from `/specs/023-clear-encounter/spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
Add a "Clear Encounter" action that removes all combatants and resets round/turn counters with a single confirmed click. Implementation follows the existing domain → application → adapter pattern with a new `clearEncounter` pure function, a use case orchestrator, and a clear button in the TurnNavigation component gated by `window.confirm()`.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: TypeScript 5.8 (strict mode, verbatimModuleSyntax)
|
||||
**Primary Dependencies**: React 19, Tailwind CSS v4, Lucide React (icons)
|
||||
**Storage**: Browser localStorage (existing adapter, updated to handle empty encounters)
|
||||
**Testing**: Vitest
|
||||
**Target Platform**: Web browser (single-page app)
|
||||
**Project Type**: Web application (monorepo: domain/application/web)
|
||||
**Performance Goals**: N/A (single synchronous state reset)
|
||||
**Constraints**: Local-first, single-user MVP
|
||||
**Scale/Scope**: Single encounter tracker screen
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
| Principle | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| I. Deterministic Domain Core | PASS | `clearEncounter` is a pure function — same input always produces same output. No I/O, randomness, or clocks. |
|
||||
| II. Layered Architecture | PASS | Domain function → Application use case → React adapter. No layer violations. |
|
||||
| III. Agent Boundary | N/A | No agent layer involvement. |
|
||||
| IV. Clarification-First | PASS | No non-trivial assumptions — feature is straightforward. Confirmation UI (browser confirm) is a reasonable default documented in research. |
|
||||
| V. Escalation Gates | PASS | All work is within spec scope. |
|
||||
| VI. MVP Baseline Language | PASS | Spec uses "MVP baseline does not include" for undo and encounter history. |
|
||||
| VII. No Gameplay Rules | PASS | No gameplay mechanics in the plan. |
|
||||
|
||||
**Post-Phase 1 re-check**: All gates still pass. The empty encounter bypass of `createEncounter()` is consistent with existing behavior (remove-combatant already produces empty combatant lists). No new layer violations introduced.
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/023-clear-encounter/
|
||||
├── plan.md # This file
|
||||
├── research.md # Phase 0 output
|
||||
├── data-model.md # Phase 1 output
|
||||
├── quickstart.md # Phase 1 output
|
||||
└── tasks.md # Phase 2 output (/speckit.tasks command)
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
packages/domain/src/
|
||||
├── clear-encounter.ts # New: pure clearEncounter function
|
||||
├── events.ts # Modified: add EncounterCleared event
|
||||
├── index.ts # Modified: export clearEncounter + event
|
||||
└── __tests__/
|
||||
└── clear-encounter.test.ts # New: domain function tests
|
||||
|
||||
packages/application/src/
|
||||
├── clear-encounter-use-case.ts # New: use case orchestrator
|
||||
└── index.ts # Modified: export use case
|
||||
|
||||
apps/web/src/
|
||||
├── hooks/
|
||||
│ └── use-encounter.ts # Modified: add clearEncounter callback
|
||||
├── persistence/
|
||||
│ └── encounter-storage.ts # Modified: handle empty encounter in load
|
||||
├── components/
|
||||
│ └── turn-navigation.tsx # Modified: add clear button
|
||||
└── App.tsx # Modified: wire clearEncounter prop
|
||||
```
|
||||
|
||||
**Structure Decision**: Follows existing monorepo layout with strict domain → application → web layering. No new packages or directories needed. Three new files, six modified files.
|
||||
42
specs/023-clear-encounter/quickstart.md
Normal file
42
specs/023-clear-encounter/quickstart.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Quickstart: 023-clear-encounter
|
||||
|
||||
## Overview
|
||||
|
||||
Add a "Clear Encounter" feature that resets the entire encounter state (removes all combatants, resets round to 1, resets turn to 0) with a single confirmed action.
|
||||
|
||||
## Key Files to Create
|
||||
|
||||
| File | Layer | Purpose |
|
||||
|------|-------|---------|
|
||||
| `packages/domain/src/clear-encounter.ts` | Domain | Pure function: clear encounter state |
|
||||
| `packages/application/src/clear-encounter-use-case.ts` | Application | Orchestrates domain call via store |
|
||||
| `packages/domain/src/__tests__/clear-encounter.test.ts` | Test | Domain function tests |
|
||||
|
||||
## Key Files to Modify
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `packages/domain/src/events.ts` | Add `EncounterCleared` event type |
|
||||
| `packages/domain/src/index.ts` | Export `clearEncounter` + event type |
|
||||
| `packages/application/src/index.ts` | Export `clearEncounterUseCase` |
|
||||
| `apps/web/src/hooks/use-encounter.ts` | Add `clearEncounter` callback |
|
||||
| `apps/web/src/persistence/encounter-storage.ts` | Handle empty encounter in `loadEncounter` |
|
||||
| `apps/web/src/components/turn-navigation.tsx` | Add clear button |
|
||||
| `apps/web/src/App.tsx` | Wire `clearEncounter` to TurnNavigation |
|
||||
|
||||
## Architecture Pattern
|
||||
|
||||
Follow the existing remove-combatant pattern:
|
||||
|
||||
1. **Domain** (`clearEncounter`): Pure function, takes `Encounter`, returns `{ encounter, events }` or `DomainError`
|
||||
2. **Application** (`clearEncounterUseCase`): Gets encounter from store, calls domain function, saves result
|
||||
3. **Web hook** (`useEncounter`): Adds `clearEncounter` callback with `window.confirm()` gate
|
||||
4. **UI** (`TurnNavigation`): Renders clear button, calls `onClearEncounter` prop
|
||||
|
||||
## Dev Commands
|
||||
|
||||
```bash
|
||||
pnpm vitest run packages/domain/src/__tests__/clear-encounter.test.ts # Run domain tests
|
||||
pnpm check # Full merge gate
|
||||
pnpm --filter web dev # Dev server
|
||||
```
|
||||
40
specs/023-clear-encounter/research.md
Normal file
40
specs/023-clear-encounter/research.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Research: 023-clear-encounter
|
||||
|
||||
## R1: Empty Encounter State in Domain
|
||||
|
||||
**Decision**: Introduce a `clearEncounter` domain function that returns an empty encounter (`{ combatants: [], activeIndex: 0, roundNumber: 1 }`) directly, bypassing `createEncounter()` which enforces the at-least-one-combatant invariant.
|
||||
|
||||
**Rationale**: The domain already handles empty combatant lists at runtime — `removeCombatant` can produce an encounter with 0 combatants (tested in AS-5 of remove-combatant tests). The `createEncounter` invariant prevents *construction* of empty encounters but the `Encounter` type itself has no minimum constraint. A dedicated `clearEncounter` function makes the intent explicit.
|
||||
|
||||
**Alternatives considered**:
|
||||
- Relax `createEncounter()` to allow empty combatants — rejected because it would weaken the invariant for normal encounter creation flows.
|
||||
- Use a separate `EmptyEncounter` type — rejected as over-engineering; the existing `Encounter` type already supports the shape.
|
||||
|
||||
## R2: Persistence of Cleared State
|
||||
|
||||
**Decision**: Update `loadEncounter()` to handle empty combatant arrays by constructing the empty encounter directly (without routing through `createEncounter()`). The existing `saveEncounter()` serializes the empty encounter as normal JSON.
|
||||
|
||||
**Rationale**: The spec requires cleared state to persist across page refreshes (FR-007). If we remove the localStorage key, `initializeEncounter()` falls back to the demo encounter, which violates the requirement. Storing and loading the empty encounter is the simplest approach.
|
||||
|
||||
**Alternatives considered**:
|
||||
- Remove localStorage key + add a "cleared" flag — rejected as unnecessary complexity.
|
||||
- Store a sentinel JSON value — rejected as non-standard and fragile.
|
||||
|
||||
## R3: Confirmation UI Pattern
|
||||
|
||||
**Decision**: Use browser-native `window.confirm()` for the destructive action confirmation.
|
||||
|
||||
**Rationale**: No dialog component currently exists in the project. Building a custom dialog for a single confirmation prompt would be over-engineering for MVP. Browser `confirm()` is synchronous, universally supported, and clearly communicates the destructive nature of the action. The confirmation logic lives in the adapter layer (React hook or component), keeping the domain pure.
|
||||
|
||||
**Alternatives considered**:
|
||||
- Custom AlertDialog component (shadcn-style) — rejected for MVP; can be added later if a richer confirmation UX is needed.
|
||||
|
||||
## R4: Clear Button Placement
|
||||
|
||||
**Decision**: Add a clear/reset button in the TurnNavigation component, near the existing round/turn controls.
|
||||
|
||||
**Rationale**: The TurnNavigation bar is the encounter-level control area (advance turn, retreat turn, round counter). A "clear encounter" action is encounter-level, so it belongs here. Placing it near the round counter reinforces that it resets the entire encounter state.
|
||||
|
||||
**Alternatives considered**:
|
||||
- ActionBar (bottom) — rejected; ActionBar is for adding combatants, not encounter-level management.
|
||||
- Separate menu/dropdown — rejected as over-engineering for a single action.
|
||||
74
specs/023-clear-encounter/spec.md
Normal file
74
specs/023-clear-encounter/spec.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Feature Specification: Clear Encounter
|
||||
|
||||
**Feature Branch**: `023-clear-encounter`
|
||||
**Created**: 2026-03-09
|
||||
**Status**: Draft
|
||||
**Input**: User description: "I want to have a button to clear the whole encounter and start over. This is helpful because when I end a combat and want to start a new one, I don't want to manually delete every single combatant. Also the round and turn counters should reset."
|
||||
|
||||
## User Scenarios & Testing *(mandatory)*
|
||||
|
||||
### User Story 1 - Clear Encounter to Start Fresh (Priority: P1)
|
||||
|
||||
As a DM who has just finished a combat encounter, I want to clear the entire encounter with a single action so that I can quickly set up a new combat without manually removing each combatant one by one.
|
||||
|
||||
**Why this priority**: This is the core value of the feature. Without it, DMs must tediously remove combatants individually between encounters, which slows down gameplay.
|
||||
|
||||
**Independent Test**: Can be fully tested by adding several combatants, advancing through some rounds, then clearing the encounter and verifying all combatants are removed and counters reset.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** an encounter with multiple combatants at round 3, **When** the user activates the clear encounter action, **Then** all combatants are removed, the round number resets to its initial state, and the active turn index resets.
|
||||
2. **Given** an encounter with a single combatant, **When** the user activates the clear encounter action, **Then** the encounter is fully cleared.
|
||||
3. **Given** an encounter with combatants that have HP values, conditions, concentration, and AC set, **When** the user clears the encounter, **Then** all combatant data is removed completely.
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 - Confirmation Before Clearing (Priority: P1)
|
||||
|
||||
As a DM, I want to be asked for confirmation before the encounter is cleared so that I don't accidentally lose my current combat state.
|
||||
|
||||
**Why this priority**: Accidental data loss would be extremely frustrating during an active combat. This is critical for usability and trust.
|
||||
|
||||
**Independent Test**: Can be tested by clicking the clear button and verifying a confirmation prompt appears, then cancelling and verifying nothing changed.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** an encounter with combatants, **When** the user activates the clear encounter action, **Then** a confirmation prompt is displayed before any data is removed.
|
||||
2. **Given** a confirmation prompt is displayed, **When** the user confirms, **Then** the encounter is cleared.
|
||||
3. **Given** a confirmation prompt is displayed, **When** the user cancels, **Then** the encounter remains unchanged.
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- What happens when the encounter is already empty (no combatants)? The clear action should either be disabled or be a no-op.
|
||||
- What happens to persisted data after clearing? The persisted encounter state should also be cleared so that refreshing the page does not restore the old encounter.
|
||||
|
||||
## Requirements *(mandatory)*
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-001**: System MUST provide a clear encounter action accessible from the encounter tracker interface.
|
||||
- **FR-002**: Activating the clear action MUST remove all combatants from the encounter.
|
||||
- **FR-003**: Activating the clear action MUST reset the round number to its initial value (1).
|
||||
- **FR-004**: Activating the clear action MUST reset the active turn index to its initial value (0).
|
||||
- **FR-005**: System MUST display a confirmation prompt before executing the clear action to prevent accidental data loss.
|
||||
- **FR-006**: Cancelling the confirmation MUST leave the encounter completely unchanged.
|
||||
- **FR-007**: The cleared state MUST be persisted so that a page refresh does not restore the previous encounter.
|
||||
- **FR-008**: The clear action SHOULD be disabled or hidden when the encounter has no combatants.
|
||||
|
||||
## Success Criteria *(mandatory)*
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: Users can clear a full encounter (any number of combatants) and start fresh with a single confirmed action, compared to N individual remove actions previously required.
|
||||
- **SC-002**: After clearing, the encounter tracker displays an empty state with round and turn counters at their initial values.
|
||||
- **SC-003**: Accidental clears are prevented by requiring explicit user confirmation before any data is removed.
|
||||
- **SC-004**: Cleared state persists across page refreshes — no stale encounter data is restored.
|
||||
|
||||
## Assumptions
|
||||
|
||||
- The clear action results in an empty encounter state (no combatants, round 1, active index 0). The user will then add new combatants for the next encounter using the existing add-combatant flow.
|
||||
- The confirmation mechanism will be a simple modal dialog or equivalent inline confirmation pattern consistent with the existing UI style.
|
||||
- MVP baseline does not include undo/restore functionality after clearing. Once confirmed, the clear is final.
|
||||
- MVP baseline does not include encounter history or the ability to save/archive encounters before clearing.
|
||||
146
specs/023-clear-encounter/tasks.md
Normal file
146
specs/023-clear-encounter/tasks.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# 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
|
||||
|
||||
- [x] 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`
|
||||
- [x] 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`
|
||||
- [x] 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
|
||||
|
||||
- [x] 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
|
||||
|
||||
- [x] 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
|
||||
- [x] T006 [US1] Export `clearEncounterUseCase` from `packages/application/src/index.ts`
|
||||
- [x] 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)
|
||||
- [x] 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
|
||||
- [x] 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
|
||||
- [x] 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
|
||||
|
||||
- [x] 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)
|
||||
- [x] 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
|
||||
|
||||
- [x] 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
|
||||
|
||||
```bash
|
||||
# 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 (T001–T003)
|
||||
2. Complete Phase 2: User Story 1 (T004–T010)
|
||||
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 1–2 → 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
|
||||
Reference in New Issue
Block a user