# 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 ```bash 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) ```