7.9 KiB
Tasks: Combatant Armor Class Display
Input: Design documents from /specs/016-combatant-ac/
Prerequisites: plan.md, spec.md, research.md, data-model.md, quickstart.md
Tests: Domain tests and persistence round-trip tests are included (consistent with existing testing patterns in the codebase).
Organization: Tasks are grouped by user story. US1 (set AC) and US2 (display AC) are both P1 and share foundational work, so they are combined into a single phase. US3 (edit AC) is P2 but is naturally fulfilled by the same inline input from US1/US2.
Format: [ID] [P?] [Story] Description
- [P]: Can run in parallel (different files, no dependencies)
- [Story]: Which user story this task belongs to (e.g., US1, US2, US3)
- Include exact file paths in descriptions
Phase 1: Foundational (Domain + Application Layer)
Purpose: Add AC to the domain model, create the setAc pure function, event type, and application use case. All user stories depend on this phase.
⚠️ CRITICAL: No UI or persistence work can begin until this phase is complete.
- T001 [P] Add
readonly ac?: numbertoCombatantinterface and addAcSetevent interface to theDomainEventunion inpackages/domain/src/types.tsandpackages/domain/src/events.ts - T002 [P] Create
setAcpure function inpackages/domain/src/set-ac.tsfollowing theset-initiative.tspattern: find combatant by ID, validate AC is non-negative integer when defined (>= 0), return updated encounter withAcSetevent orDomainError - T003 Write domain tests in
packages/domain/src/__tests__/set-ac.test.ts: set AC to valid value, set AC to 0, clear AC (undefined), combatant not found error, negative AC error, non-integer AC error, AC unchanged preserves other fields - T004 Export
setAc,SetAcSuccesstype, andAcSetevent type frompackages/domain/src/index.ts - T005 Create
setAcUseCaseinpackages/application/src/set-ac-use-case.tsfollowingset-initiative-use-case.tspattern, and export frompackages/application/src/index.ts
Checkpoint: Domain function and use case ready. Run pnpm test — all tests should pass including new set-ac.test.ts.
Phase 2: User Story 1 + 2 — Set and Display AC (Priority: P1) 🎯 MVP
Goal: Users can set AC for a combatant and see it displayed as a shield icon with the numeric value in the encounter list.
Independent Test: Add a combatant, set its AC via the inline input, and verify the shield icon + value appears next to the name. Verify combatants without AC show no icon.
Implementation
- T006 [P] [US1] Add AC rehydration validation in
apps/web/src/persistence/encounter-storage.ts: validatetypeof entry.ac === "number" && Number.isInteger(entry.ac) && entry.ac >= 0, attach to combatant object during deserialization (follow theinitiativevalidation pattern) - T007 [P] [US1] Add AC round-trip tests in
apps/web/src/persistence/__tests__/encounter-storage.test.ts: persist and load combatant with AC, persist without AC, reject invalid AC values (negative, non-integer, string) - T008 [US1] Add
setAccallback toapps/web/src/hooks/use-encounter.tsfollowing thesetInitiativecallback pattern: callsetAcUseCase, check for domain error, append events - T009 [US1] [US2] Add
onSetAcprop toCombatantRowPropsand implement AC display + inline editable input inapps/web/src/components/combatant-row.tsx: LucideShieldicon + numeric value between name and HP columns, inline input followingMaxHpInputpattern (click to edit, blur/Enter to commit, empty clears), hidden when AC isundefined - T010 [US2] Wire
setAccallback fromuseEncounterhook toCombatantRowonSetAcprop in the encounter list parent component (likelyapps/web/src/components/encounter-tracker.tsxor equivalent)
Checkpoint: US1 + US2 fully functional. Run pnpm check — full merge gate should pass. Manually verify: add combatant, set AC inline, see shield icon + value. Clear AC, icon disappears.
Phase 3: User Story 3 — Edit AC for Existing Combatant (Priority: P2)
Goal: Users can change or remove an existing combatant's AC value.
Independent Test: Set a combatant's AC to 15, then change it to 18 and verify the display updates. Clear the AC field and verify the shield icon disappears.
Note: This story is naturally fulfilled by the inline editable input implemented in Phase 2 (T009). This phase is a verification checkpoint — no additional code tasks are needed unless the inline input from T009 does not yet support editing existing values.
- T011 [US3] Verify and confirm that the inline AC input in
apps/web/src/components/combatant-row.tsxhandles all US3 acceptance scenarios: changing AC from 15 to 18 updates display, clearing AC field removes shield icon, setting AC on a combatant that previously had none shows shield icon
Checkpoint: All user stories functional. Run pnpm check — full merge gate must pass.
Phase 4: Polish & Cross-Cutting Concerns
Purpose: Final validation and cleanup.
- T012 Run
pnpm checkto verify full merge gate passes (knip + format + lint + typecheck + test) - T013 Verify no regression in existing functionality: HP tracking, initiative sorting, turn navigation, combatant add/remove/rename all work with AC field present
Dependencies & Execution Order
Phase Dependencies
- Foundational (Phase 1): No dependencies — can start immediately
- US1+US2 (Phase 2): Depends on Phase 1 completion (domain function + use case must exist)
- US3 (Phase 3): Depends on Phase 2 completion (inline input must exist)
- Polish (Phase 4): Depends on Phase 3 completion
Within Phase 1 (Foundational)
T001 (types + events) ──┐
├──→ T003 (tests) ──→ T004 (exports) ──→ T005 (use case)
T002 (setAc function) ──┘
T001 and T002 can run in parallel (different files). T003 depends on both. T004 depends on T002. T005 depends on T004.
Within Phase 2 (US1+US2)
T006 (persistence) ──┐
T007 (storage tests) ┤
├──→ T009 (combatant row UI) ──→ T010 (wire to parent)
T008 (hook callback) ─┘
T006, T007, T008 can run in parallel (different files). T009 depends on T008. T010 depends on T009.
Parallel Opportunities
- Phase 1: T001 and T002 in parallel (types.ts/events.ts vs set-ac.ts)
- Phase 2: T006, T007, and T008 in parallel (persistence vs tests vs hook)
Parallel Example: Phase 1
# Launch in parallel (different files):
Task: "Add ac field to Combatant interface in packages/domain/src/types.ts and AcSet event in packages/domain/src/events.ts"
Task: "Create setAc pure function in packages/domain/src/set-ac.ts"
# Then sequentially:
Task: "Write domain tests in packages/domain/src/__tests__/set-ac.test.ts"
Task: "Export from packages/domain/src/index.ts"
Task: "Create use case in packages/application/src/set-ac-use-case.ts"
Implementation Strategy
MVP First (US1 + US2)
- Complete Phase 1: Foundational (domain + application)
- Complete Phase 2: US1 + US2 (persistence + hook + UI)
- STOP and VALIDATE: Set AC on combatants, verify shield icon display
- Run
pnpm check— merge gate must pass
Incremental Delivery
- Phase 1 → Domain model and logic ready
- Phase 2 → AC visible and settable in encounter list (MVP!)
- Phase 3 → Verify editing works (should be automatic from Phase 2)
- Phase 4 → Final polish and regression check
Notes
- [P] tasks = different files, no dependencies
- [Story] label maps task to specific user story for traceability
- US1 and US2 are combined because they share all implementation work and are both P1
- US3 requires no new code — the inline input from US1/US2 inherently supports editing
- Follow
set-initiative.tsas the primary pattern reference throughout - Commit after each phase or logical group of tasks