The addCombatant domain function currently only accepts (encounter, id, name), forcing the hook layer to compose multiple sequential domain calls and direct store.save() mutations to set initial stats when adding from bestiary or player characters. This makes combatant creation non-atomic, bypasses domain validation for some fields, and produces no events for fields set via the bypass. Extending addCombatant to accept an optional CombatantInit bag makes creation atomic and moves all validation into the domain.
Acceptance Criteria
addCombatant domain function accepts an optional CombatantInit parameter (maxHp, ac, initiative, creatureId, color, icon, playerCharacterId)
Initial fields are validated in the domain using the same rules as existing set functions (setHp, setAc, setInitiative)
A single CombatantAdded event is emitted containing the full initial state
addFromBestiary in the hook uses the new atomic addCombatant instead of multi-step composition
addFromPlayerCharacter in the hook uses the new atomic addCombatant instead of multi-step composition
No direct store.save() bypass remains in either compound operation
Existing tests pass; new tests cover addCombatant with CombatantInit
Context
Research: docs/agents/research/2026-03-25-combat-state.md (see "Follow-up Analysis" section)
## Summary
The `addCombatant` domain function currently only accepts `(encounter, id, name)`, forcing the hook layer to compose multiple sequential domain calls and direct `store.save()` mutations to set initial stats when adding from bestiary or player characters. This makes combatant creation non-atomic, bypasses domain validation for some fields, and produces no events for fields set via the bypass. Extending `addCombatant` to accept an optional `CombatantInit` bag makes creation atomic and moves all validation into the domain.
## Acceptance Criteria
- [x] `addCombatant` domain function accepts an optional `CombatantInit` parameter (maxHp, ac, initiative, creatureId, color, icon, playerCharacterId)
- [x] Initial fields are validated in the domain using the same rules as existing set functions (setHp, setAc, setInitiative)
- [x] A single `CombatantAdded` event is emitted containing the full initial state
- [x] `addFromBestiary` in the hook uses the new atomic `addCombatant` instead of multi-step composition
- [x] `addFromPlayerCharacter` in the hook uses the new atomic `addCombatant` instead of multi-step composition
- [x] No direct `store.save()` bypass remains in either compound operation
- [x] Existing tests pass; new tests cover `addCombatant` with `CombatantInit`
## Context
- Research: `docs/agents/research/2026-03-25-combat-state.md` (see "Follow-up Analysis" section)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
The
addCombatantdomain function currently only accepts(encounter, id, name), forcing the hook layer to compose multiple sequential domain calls and directstore.save()mutations to set initial stats when adding from bestiary or player characters. This makes combatant creation non-atomic, bypasses domain validation for some fields, and produces no events for fields set via the bypass. ExtendingaddCombatantto accept an optionalCombatantInitbag makes creation atomic and moves all validation into the domain.Acceptance Criteria
addCombatantdomain function accepts an optionalCombatantInitparameter (maxHp, ac, initiative, creatureId, color, icon, playerCharacterId)CombatantAddedevent is emitted containing the full initial stateaddFromBestiaryin the hook uses the new atomicaddCombatantinstead of multi-step compositionaddFromPlayerCharacterin the hook uses the new atomicaddCombatantinstead of multi-step compositionstore.save()bypass remains in either compound operationaddCombatantwithCombatantInitContext
docs/agents/research/2026-03-25-combat-state.md(see "Follow-up Analysis" section)