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

2.5 KiB

Quickstart: Combatant Armor Class Display

Feature: 016-combatant-ac | Date: 2026-03-06

Overview

This feature adds an optional Armor Class (AC) field to combatants, displayed as a shield icon with the numeric value in the encounter list. It follows the exact patterns established by initiative and HP fields.

Layer-by-Layer Implementation Order

1. Domain Layer (packages/domain/src/)

Pattern to follow: set-initiative.ts (single optional numeric field)

  1. Add readonly ac?: number to Combatant interface in types.ts
  2. Create set-ac.ts with a setAc pure function:
    • Find combatant by ID (error if not found)
    • Validate AC is non-negative integer when defined
    • Return updated encounter + AcSet event
  3. Add AcSet event to events.ts and the DomainEvent union
  4. Export from index.ts
  5. Write tests in __tests__/set-ac.test.ts

2. Application Layer (packages/application/src/)

Pattern to follow: set-initiative-use-case.ts

  1. Create set-ac-use-case.ts: get encounter from store, call setAc, save result
  2. Export from index.ts

3. Web Layer (apps/web/src/)

Pattern to follow: Initiative input in combatant-row.tsx + MaxHpInput editing pattern

  1. Persistence (persistence/encounter-storage.ts): Add AC validation in rehydration logic
  2. Hook (hooks/use-encounter.ts): Add setAc callback using setAcUseCase
  3. UI (components/combatant-row.tsx):
    • Add onSetAc to CombatantRowProps
    • Add AC display: Lucide Shield icon + value, between name and HP
    • Inline editable input (same pattern as MaxHpInput)
    • Hidden when AC is undefined

Key Files to Read First

File Why
packages/domain/src/set-initiative.ts Closest pattern for single optional field
packages/domain/src/events.ts Event type definitions
apps/web/src/components/combatant-row.tsx UI layout and inline input patterns
apps/web/src/persistence/encounter-storage.ts Rehydration validation pattern

Testing Strategy

  • Domain: Pure function tests for setAc (set, clear, validation errors, combatant not found)
  • Persistence: Round-trip test for AC field in localStorage
  • No UI tests: Consistent with existing approach (no component tests in codebase)

Commands

pnpm test                    # Run all tests
pnpm vitest run packages/domain/src/__tests__/set-ac.test.ts  # Run AC domain tests
pnpm check                   # Full merge gate (must pass before commit)