# Implementation Plan: Combatant HP Tracking **Branch**: `009-combatant-hp` | **Date**: 2026-03-05 | **Spec**: [spec.md](./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) ```text 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) ```text 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.