Files
initiative/specs/016-combatant-ac/plan.md

3.8 KiB

Implementation Plan: Combatant Armor Class Display

Branch: 016-combatant-ac | Date: 2026-03-06 | Spec: 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)

specs/016-combatant-ac/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
└── tasks.md              # Phase 2 output (/speckit.tasks)

Source Code (repository root)

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.