# Implementation Plan: Combatant Armor Class Display **Branch**: `016-combatant-ac` | **Date**: 2026-03-06 | **Spec**: [spec.md](./spec.md) **Input**: Feature specification from `/specs/016-combatant-ac/spec.md` ## Summary Add an optional Armor Class (AC) field to the Combatant domain type and display it in the encounter list as a shield icon with the numeric value next to each combatant's name. The feature follows the exact same patterns as existing optional fields (initiative, maxHp) through all layers: domain pure function, application use case, React UI component, and localStorage persistence. ## Technical Context **Language/Version**: TypeScript 5.8 (strict mode, verbatimModuleSyntax) **Primary Dependencies**: React 19, Vite 6, Tailwind CSS v4, shadcn/ui-style components, Lucide React (icons) **Storage**: Browser localStorage (existing adapter, transparent JSON serialization) **Testing**: Vitest (pure domain function tests + storage round-trip tests) **Target Platform**: Browser (local-first, single-user) **Project Type**: Web application (monorepo: packages/domain, packages/application, apps/web) **Performance Goals**: N/A (trivial data addition, no performance concerns) **Constraints**: Offline-capable (localStorage only), no external dependencies added **Scale/Scope**: Single optional field addition across 3 layers ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* | Principle | Status | Evidence | |-----------|--------|----------| | I. Deterministic Domain Core | PASS | `setAc` is a pure function: same input encounter + combatantId + ac value always produces the same output. No I/O, randomness, or clocks. | | II. Layered Architecture | PASS | Domain defines `setAc` (pure). Application defines `setAcUseCase` (orchestration via EncounterStore port). Web implements UI + persistence adapter. No reverse dependencies. | | III. Agent Boundary | N/A | No agent layer involvement. | | IV. Clarification-First | PASS | No non-trivial assumptions — feature follows established patterns for optional combatant fields. | | V. Escalation Gates | PASS | All functionality is within spec scope (FR-001 through FR-007). | | VI. MVP Baseline Language | PASS | Spec uses "MVP baseline does not include AC-based calculations." | | VII. No Gameplay Rules | PASS | AC is stored and displayed only — no combat resolution logic. | **Gate result**: PASS — no violations. ## Project Structure ### Documentation (this feature) ```text specs/016-combatant-ac/ ├── plan.md ├── research.md ├── data-model.md ├── quickstart.md └── tasks.md # Phase 2 output (/speckit.tasks) ``` ### Source Code (repository root) ```text packages/domain/src/ ├── types.ts # Add optional `ac` field to Combatant interface ├── set-ac.ts # NEW: pure function to set/clear AC ├── events.ts # Add AcSet event type ├── index.ts # Re-export new function + event type └── __tests__/ └── set-ac.test.ts # NEW: domain tests for setAc packages/application/src/ ├── set-ac-use-case.ts # NEW: orchestration use case └── index.ts # Re-export new use case apps/web/src/ ├── components/ │ └── combatant-row.tsx # Add AC display (shield icon + value) + onSetAc callback ├── hooks/ │ └── use-encounter.ts # Add setAc action callback └── persistence/ └── encounter-storage.ts # Add AC validation in loadEncounter rehydration └── __tests__/ └── encounter-storage.test.ts # Add AC round-trip tests ``` **Structure Decision**: Follows the existing monorepo layered architecture (domain → application → web). No new packages or structural changes needed. ## Complexity Tracking > No violations — table not applicable.