6.2 KiB
Tasks: HP Status Indicators
Input: Design documents from /specs/013-hp-status-indicators/
Prerequisites: plan.md, spec.md, research.md, data-model.md, quickstart.md
Tests: Included -- domain function requires unit tests per project convention.
Organization: Tasks grouped by user story. US1 and US2 share the same domain function (foundational), then diverge at the UI layer.
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: Foundational (Domain Function + Tests)
Purpose: Create the pure domain function that all user stories depend on
CRITICAL: No UI work can begin until this phase is complete
- T001 Create
HpStatustype andderiveHpStatuspure function inpackages/domain/src/hp-status.ts-- returns"healthy" | "bloodied" | "unconscious" | undefinedbased oncurrentHpandmaxHpinputs. CheckcurrentHp <= 0first (unconscious), thencurrentHp < maxHp / 2(bloodied), else healthy. Returnundefinedwhen either input isundefined. - T002 Write unit tests for
deriveHpStatusinpackages/domain/src/__tests__/hp-status.test.tscovering: healthy (currentHp >= maxHp/2), bloodied (0 < currentHp < maxHp/2), unconscious (currentHp <= 0), unconscious with negative HP, undefined when maxHp undefined, undefined when currentHp undefined, undefined when both undefined, edge case maxHp=1 (no bloodied state possible), edge case maxHp=2 (1/2 is healthy not bloodied), odd maxHp=21 (10 is bloodied since 10 < 10.5), currentHp exceeding maxHp (healthy). - T003 Export
deriveHpStatusandHpStatustype frompackages/domain/src/index.ts-- addexport { type HpStatus, deriveHpStatus } from "./hp-status.js";
Checkpoint: pnpm check passes. Domain function is tested and exported.
Phase 2: User Story 1 - Bloodied Indicator (Priority: P1) + User Story 2 - Unconscious/Dead Indicator (Priority: P1)
Goal: Apply visual status indicators to the combatant row -- amber HP text for bloodied, red HP text + muted row for unconscious/dead
Independent Test: Set a combatant's max HP, reduce current HP below half -- verify amber text. Reduce to 0 -- verify red text and muted row. Heal above half -- verify normal appearance restored.
Implementation
- T004 [US1] [US2] Modify
apps/web/src/components/combatant-row.tsxto importderiveHpStatusfrom@initiative/domainand call it with the combatant'scurrentHpandmaxHpat the top of theCombatantRowcomponent. Store result in astatusvariable. - T005 [US1] [US2] In
apps/web/src/components/combatant-row.tsx, apply conditional CSS classes based onstatus: (1) On theCurrentHpInputwrapper/value -- addtext-amber-400when bloodied,text-red-400when unconscious. (2) On the row's outer<div>-- addopacity-50when unconscious. Ensure the active-turn border styling still takes precedence for active combatants. Use the existingcn()utility for conditional class merging.
Checkpoint: pnpm check passes. Bloodied and unconscious indicators are visible in the UI.
Phase 3: User Story 3 - Status Transitions (Priority: P2)
Goal: Ensure visual indicators update in real time as HP changes through quick HP input, direct entry, and +/- controls
Independent Test: Use the quick HP input to deal damage across thresholds (healthy -> bloodied -> unconscious) and heal back. Verify indicator changes instantly with no flicker or delay.
Implementation
- T006 [US3] Verify in
apps/web/src/components/combatant-row.tsxthatderiveHpStatusis called with the current combatant props (not stale state) so status updates in the same render cycle as HP changes. No additional code should be needed if T004-T005 are implemented correctly -- this task is a manual verification and visual QA pass.
Checkpoint: All transitions (healthy->bloodied->unconscious->healthy) work smoothly.
Phase 4: Polish & Cross-Cutting Concerns
- T007 Run
pnpm checkto validate all automated checks pass (knip, format, lint, typecheck, test) - T008 Verify layer boundary test still passes (domain must not import from React/UI) in
packages/domain/src/__tests__/layer-boundaries.test.ts
Dependencies & Execution Order
Phase Dependencies
- Foundational (Phase 1): No dependencies -- can start immediately
- US1+US2 (Phase 2): Depends on Phase 1 completion (needs domain function)
- US3 (Phase 3): Depends on Phase 2 completion (needs UI indicators in place)
- Polish (Phase 4): Depends on all phases complete
Within Phases
- T001 before T002 (need function to test it)
- T002 before T003 (tests pass before exporting)
- T004 before T005 (import before styling)
Parallel Opportunities
- T001 and T002 can be done together if writing tests alongside implementation (TDD)
- US1 and US2 are implemented together in Phase 2 since they modify the same file and component
Parallel Example: Phase 1
# T001 and T002 can be written together (TDD style):
Task: "Create deriveHpStatus in packages/domain/src/hp-status.ts"
Task: "Write tests in packages/domain/src/__tests__/hp-status.test.ts"
Implementation Strategy
MVP First (Phase 1 + Phase 2)
- Complete Phase 1: Domain function + tests + export
- Complete Phase 2: UI styling for both bloodied and unconscious
- STOP and VALIDATE: Manually test all scenarios from spec
- Run
pnpm check
Full Delivery
- Phase 1: Domain function (T001-T003)
- Phase 2: UI indicators (T004-T005)
- Phase 3: Transition verification (T006)
- Phase 4: Final validation (T007-T008)
Notes
- US1 (bloodied) and US2 (unconscious) are combined into Phase 2 because they modify the same component and the domain function handles both statuses. Implementing them separately would require touching the same lines twice.
- T006 is a verification task, not a code change -- if the React component re-renders on prop changes (which it does by default), transitions work automatically.
- Total: 8 tasks across 4 phases. Minimal scope -- no new application layer, no storage changes.