Files
initiative/specs/002-add-combatant/data-model.md

2.0 KiB

Data Model: Add Combatant

Feature: 002-add-combatant Date: 2026-03-03

Entities

Combatant (existing, unchanged)

Field Type Constraints
id CombatantId (branded string) Unique, required
name string Non-empty after trimming, required

Encounter (existing, unchanged)

Field Type Constraints
combatants readonly Combatant[] Ordered list, may be empty
activeIndex number 0 <= activeIndex < combatants.length (or 0 if empty)
roundNumber number Positive integer >= 1, only increases

Domain Events

CombatantAdded (new)

Field Type Description
type "CombatantAdded" (literal) Discriminant for the DomainEvent union
combatantId CombatantId Id of the newly added combatant
name string Name of the newly added combatant
position number Zero-based index where the combatant was placed

State Transitions

AddCombatant

Input: Encounter + CombatantId + name (string)

Preconditions:

  • Name must be non-empty after trimming

Transition:

  • New combatant { id, name: trimmedName } appended to end of combatants list
  • activeIndex unchanged
  • roundNumber unchanged

Postconditions:

  • combatants.length increased by 1
  • New combatant is at index combatants.length - 1
  • All existing combatants preserve their order and index positions
  • INV-2 satisfied (activeIndex still valid for the now-larger list)

Events emitted: Exactly one CombatantAdded

Error cases:

  • Empty or whitespace-only name → DomainError { code: "invalid-name" }

Function Signatures

Domain Layer

addCombatant(encounter, id, name) → { encounter, events } | DomainError

Application Layer

addCombatantUseCase(store, id, name) → DomainEvent[] | DomainError

Validation Rules

Rule Layer Error Code
Name non-empty after trim Domain invalid-name