Files
initiative/specs/017-combat-conditions/tasks.md

165 lines
8.7 KiB
Markdown

# Tasks: Combat Conditions
**Input**: Design documents from `/specs/017-combat-conditions/`
**Prerequisites**: plan.md (required), spec.md (required), research.md, data-model.md, quickstart.md
**Tests**: Tests are included as this project follows a test-driven domain pattern (all domain operations have corresponding test files).
**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, US3)
- Include exact file paths in descriptions
---
## Phase 1: Setup
**Purpose**: Define condition types, registry, and extend the domain model
- [x] T001 Define `ConditionId` union type, `ConditionDefinition` interface, and `CONDITION_DEFINITIONS` registry array with all 15 conditions (id, label, iconName, color) plus `VALID_CONDITION_IDS` set in `packages/domain/src/conditions.ts`
- [x] T002 Add `readonly conditions?: readonly ConditionId[]` field to `Combatant` interface in `packages/domain/src/types.ts`
- [x] T003 Add `ConditionAdded` and `ConditionRemoved` event interfaces and include them in the `DomainEvent` union in `packages/domain/src/events.ts`
- [x] T004 Re-export new symbols (`ConditionId`, `ConditionDefinition`, `CONDITION_DEFINITIONS`, `VALID_CONDITION_IDS`, `ConditionAdded`, `ConditionRemoved`) from `packages/domain/src/index.ts`
**Checkpoint**: Domain types compile; `pnpm typecheck` passes
---
## Phase 2: Foundational (Domain Operations)
**Purpose**: Implement core domain operations that all user stories depend on
- [x] T005 [P] Implement `toggleCondition(encounter, combatantId, conditionId)` pure function in `packages/domain/src/toggle-condition.ts` — adds condition if absent (emits `ConditionAdded`), removes if present (emits `ConditionRemoved`), maintains sorted definition order, normalizes empty array to `undefined`
- [x] ~~T006~~ Removed — `toggleCondition` handles both add and remove; a separate `removeCondition` was unnecessary
- [x] T007 [P] Write tests for `toggleCondition` in `packages/domain/src/__tests__/toggle-condition.test.ts` — toggle on, toggle off, ordering preserved, duplicate prevention, unknown condition rejected, combatant-not-found error, immutability, empty-to-undefined normalization
- [x] ~~T008~~ Removed — covered by toggle tests
- [x] T009 Re-export `toggleCondition` and its success type from `packages/domain/src/index.ts`
- [x] T010 [P] Implement `toggleConditionUseCase` in `packages/application/src/toggle-condition-use-case.ts` following `setAcUseCase` pattern
- [x] ~~T011~~ Removed — no separate `removeConditionUseCase` needed
- [x] T012 Re-export new use case from `packages/application/src/index.ts`
**Checkpoint**: `pnpm check` passes; domain operations fully tested
---
## Phase 3: User Story 1 — Add a Condition (Priority: P1) + User Story 2 — Remove a Condition (Priority: P1) 🎯 MVP
**Goal**: DM can add conditions via picker and remove them by clicking icon tags in the combat row
**Independent Test**: Click "+" on a combatant, select a condition in the picker, verify icon tag appears. Click the icon tag, verify it disappears. Reload page, verify conditions persist.
### Implementation
- [x] T013 Add conditions rehydration validation to `loadEncounter` in `apps/web/src/persistence/encounter-storage.ts` — filter stored condition values against `VALID_CONDITION_IDS`, normalize empty array to `undefined`
- [x] T014 Add `toggleCondition` callback to `useEncounter` hook in `apps/web/src/hooks/use-encounter.ts` following the `setAc` callback pattern
- [x] T015 Create `ConditionTags` component in `apps/web/src/components/condition-tags.tsx` — renders active condition icon tags (Lucide icons with color classes) in fixed definition order, plus always-visible "+" button; clicking a tag calls `onRemove(conditionId)`; clicking "+" calls `onOpenPicker()`
- [x] T016 Create `ConditionPicker` component in `apps/web/src/components/condition-picker.tsx` — popover/dropdown showing all 15 conditions as icon + label rows; active conditions visually distinguished; clicking toggles on/off via `onToggle(conditionId)`; closes on click-outside
- [x] T017 Integrate conditions into `CombatantRow` in `apps/web/src/components/combatant-row.tsx` — add `onToggleCondition` prop; render `ConditionTags` + `ConditionPicker` below the combatant name; wire picker open/close state; both tag clicks and picker use toggle
**Checkpoint**: Full add/remove flow works; conditions persist across reload; `pnpm check` passes
---
## Phase 4: User Story 3 — Tooltip on Hover (Priority: P2)
**Goal**: Hovering over a condition icon tag shows the condition name in a tooltip
**Independent Test**: Hover over any active condition icon tag, verify tooltip appears with condition name; move cursor away, verify tooltip disappears.
### Implementation
- [x] T018 [US3] Add `title` attribute or tooltip to condition icon buttons in `ConditionTags` component in `apps/web/src/components/condition-tags.tsx` — display `conditionDefinition.label` on hover
**Checkpoint**: Tooltips visible on hover for all condition icons
---
## Phase 5: User Story 4 — Multiple Conditions (Priority: P2)
**Goal**: Multiple conditions on one combatant display compactly with correct ordering
**Independent Test**: Apply 3+ conditions to a combatant in non-alphabetical order, verify they display in fixed definition order; verify row wraps without widening the tracker.
### Implementation
- [x] T019 [US4] Verify wrapping behavior in `ConditionTags` component in `apps/web/src/components/condition-tags.tsx` — ensure `flex-wrap` is applied so multiple conditions wrap within the row width; verify layout does not increase tracker width
**Checkpoint**: Multiple conditions wrap correctly; fixed order maintained regardless of application order
---
## Phase 6: Polish & Cross-Cutting Concerns
**Purpose**: Final validation and cleanup
- [x] T020 Run `pnpm check` (knip + format + lint + typecheck + test) and fix any issues
- [x] T021 Verify layer boundary compliance — domain does not import React/Lucide components; icon name strings only in domain
---
## Dependencies & Execution Order
### Phase Dependencies
- **Setup (Phase 1)**: No dependencies — T001 → T002 → T003 → T004 (sequential, same files)
- **Foundational (Phase 2)**: Depends on Phase 1 — T005/T007 parallel, then T009, then T010, then T012
- **US1+US2 (Phase 3)**: Depends on Phase 2 — T013 → T014 → T015/T016 parallel → T017
- **US3 (Phase 4)**: Depends on Phase 3 (T015 must exist) — T018 standalone
- **US4 (Phase 5)**: Depends on Phase 3 (T015 must exist) — T019 standalone
- **Polish (Phase 6)**: Depends on all previous phases — T020 → T021
### User Story Dependencies
- **US1+US2 (P1)**: Merged into one phase — add and remove are two sides of the same interaction; both require the picker and tags components
- **US3 (P2)**: Depends on US1+US2 (needs condition tags to exist); can run parallel with US4
- **US4 (P2)**: Depends on US1+US2 (needs condition tags to exist); can run parallel with US3
### Parallel Opportunities
- T005, T007 can run in parallel (different files)
- T015, T016 can run in parallel (different files)
- T018, T019 can run in parallel (different stories, different concerns)
---
## Parallel Example: Foundational Phase
```bash
# Launch domain operation and tests together:
Task: "Implement toggleCondition in packages/domain/src/toggle-condition.ts"
Task: "Write toggle-condition tests in packages/domain/src/__tests__/toggle-condition.test.ts"
```
---
## Implementation Strategy
### MVP First (US1 + US2)
1. Complete Phase 1: Setup (types and registry)
2. Complete Phase 2: Foundational (domain operations + use cases)
3. Complete Phase 3: US1+US2 (full add/remove UI flow + persistence)
4. **STOP and VALIDATE**: Add and remove conditions in the browser; reload page; verify persistence
5. Deploy/demo if ready
### Incremental Delivery
1. Setup + Foundational → Domain layer complete
2. US1+US2 → Full condition management flow (MVP!)
3. US3 → Tooltips for discoverability
4. US4 → Layout polish for heavy condition usage
5. Each increment adds value without breaking previous work
---
## Notes
- US1 and US2 are merged into Phase 3 because they share the same components (ConditionTags, ConditionPicker) and cannot be meaningfully separated
- US3 (tooltip) is a single-task phase — just adding `title` attributes to existing icon buttons
- US4 (wrapping) is a single-task phase — verifying and ensuring CSS flex-wrap behavior
- The Lucide icon → component mapping lives in the web layer only; domain uses string icon names
- Commit after each task or logical group