Files
initiative/specs/009-combatant-hp/plan.md

4.2 KiB

Implementation Plan: Combatant HP Tracking

Branch: 009-combatant-hp | Date: 2026-03-05 | Spec: spec.md Input: Feature specification from /specs/009-combatant-hp/spec.md

Summary

Add optional max HP and current HP tracking to combatants. The domain layer gains pure functions for setting max HP and adjusting current HP (clamped to 0..max). The application layer orchestrates via the existing EncounterStore port. The web adapter adds +/- controls and direct numeric entry per combatant row. Persistence extends the existing localStorage serialization to include HP fields.

Technical Context

Language/Version: TypeScript 5.x (strict mode, verbatimModuleSyntax) Primary Dependencies: React 19, Vite 6, Biome 2.0, Vitest Storage: Browser localStorage (adapter layer only) Testing: Vitest (pure function tests in domain, use case tests in application) Target Platform: Modern web browsers (single-user, local-first) Project Type: Web application (monorepo with domain/application/web layers) Performance Goals: Standard web app responsiveness; HP adjustments must feel instant Constraints: Offline-capable, single-user MVP, no server Scale/Scope: Single encounter at a time, typically 5-20 combatants

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

Principle Status Notes
I. Deterministic Domain Core PASS HP set/adjust are pure functions: (Encounter, CombatantId, value) -> (Encounter, Events) or DomainError. No I/O in domain.
II. Layered Architecture PASS Domain: pure HP functions. Application: use cases via EncounterStore port. Web: React adapter with +/- controls. No layer violations.
III. Agent Boundary N/A No agent features in this feature.
IV. Clarification-First PASS Spec has no NEEDS CLARIFICATION markers; all decisions documented in Assumptions.
V. Escalation Gates PASS Implementation stays within spec scope.
VI. MVP Baseline Language PASS Spec uses "not in the MVP baseline" for temp HP, death states, custom damage amounts.
VII. No Gameplay Rules in Constitution PASS HP clamping is spec-level behavior, not constitution-level.

Project Structure

Documentation (this feature)

specs/009-combatant-hp/
├── plan.md              # This file
├── spec.md              # Feature specification
├── research.md          # Phase 0 output
├── data-model.md        # Phase 1 output
├── quickstart.md        # Phase 1 output
└── tasks.md             # Phase 2 output (created by /speckit.tasks)

Source Code (repository root)

packages/domain/src/
├── types.ts                    # Extend Combatant with optional maxHp/currentHp
├── set-hp.ts                   # New: pure function for setting max HP
├── adjust-hp.ts                # New: pure function for adjusting current HP (+/- delta)
├── events.ts                   # New events: MaxHpSet, CurrentHpAdjusted
├── index.ts                    # Re-export new functions
└── __tests__/
    ├── set-hp.test.ts          # Tests for set-hp
    └── adjust-hp.test.ts       # Tests for adjust-hp

packages/application/src/
├── set-hp-use-case.ts          # New: orchestrates set-hp via store
├── adjust-hp-use-case.ts       # New: orchestrates adjust-hp via store
└── index.ts                    # Re-export new use cases

apps/web/src/
├── hooks/use-encounter.ts      # Add setHp and adjustHp callbacks
├── persistence/
│   └── encounter-storage.ts    # Extend validation to include HP fields
└── App.tsx                     # Add HP controls to combatant rows

Structure Decision: Follows existing monorepo layered architecture. New domain functions follow the established one-file-per-operation pattern (matching edit-combatant.ts, set-initiative.ts). Two separate domain functions (set-hp for max HP, adjust-hp for current HP delta) keep concerns separated and make the design extensible for richer damage/heal operations later.

Complexity Tracking

No constitution violations to justify.