Implement the 016-combatant-ac feature that adds an optional Armor Class field to combatants with shield icon display and inline editing in the encounter tracker
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
64
specs/016-combatant-ac/quickstart.md
Normal file
64
specs/016-combatant-ac/quickstart.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# 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)
|
||||
```
|
||||
Reference in New Issue
Block a user