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

65 lines
2.5 KiB
Markdown

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