# Implementation Plan: Edit Combatant **Branch**: `004-edit-combatant` | **Date**: 2026-03-03 | **Spec**: [spec.md](./spec.md) **Input**: Feature specification from `/specs/004-edit-combatant/spec.md` ## Summary Add the ability to rename a combatant by id within an encounter. A pure domain function `editCombatant` validates the id and new name, returns the updated encounter with a `CombatantUpdated` event, or a `DomainError`. Wired through an application use case and exposed via the existing `useEncounter` hook to a minimal UI control. ## Technical Context **Language/Version**: TypeScript 5.x (strict mode, verbatimModuleSyntax) **Primary Dependencies**: React 19, Vite **Storage**: In-memory React state (local-first, single-user MVP) **Testing**: Vitest **Target Platform**: Browser (localhost:5173) **Project Type**: Web application (monorepo: domain → application → web) **Performance Goals**: N/A — single-user local state, instant updates **Constraints**: Pure domain logic, no I/O in domain layer **Scale/Scope**: Single-user encounter tracker ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* | Principle | Status | Notes | |-----------|--------|-------| | I. Deterministic Domain Core | PASS | `editCombatant` is a pure function: same encounter + id + name → same result | | II. Layered Architecture | PASS | Domain function → use case → hook/UI. No layer violations. | | III. Agent Boundary | N/A | No agent layer involvement | | IV. Clarification-First | PASS | Spec is complete, no ambiguities remain | | V. Escalation Gates | PASS | All work is within spec scope | | VI. MVP Baseline Language | PASS | Spec uses "MVP baseline does not include" for out-of-scope items | | VII. No Gameplay Rules | PASS | Constitution contains no gameplay logic | ## Project Structure ### Documentation (this feature) ```text specs/004-edit-combatant/ ├── plan.md ├── research.md ├── data-model.md ├── quickstart.md ├── contracts/ └── tasks.md ``` ### Source Code (repository root) ```text packages/domain/src/ ├── edit-combatant.ts # New: pure editCombatant function ├── events.ts # Modified: add CombatantUpdated event ├── types.ts # Unchanged (Combatant, Encounter, DomainError) ├── index.ts # Modified: re-export editCombatant └── __tests__/ └── edit-combatant.test.ts # New: acceptance + invariant tests packages/application/src/ ├── edit-combatant-use-case.ts # New: use case wiring └── index.ts # Modified: re-export use case apps/web/src/ ├── hooks/use-encounter.ts # Modified: add editCombatant action └── App.tsx # Modified: add rename UI control ``` **Structure Decision**: Follows the existing monorepo layout (`packages/domain` → `packages/application` → `apps/web`). Each new file mirrors the pattern established by `add-combatant` and `remove-combatant`.