# Research: Roll Initiative ## Decision 1: Where to generate random dice rolls **Decision**: Generate random numbers at the adapter (React/browser) layer and pass resolved values into application use cases. **Rationale**: Constitution Principle I (Deterministic Domain Core) requires all domain logic to be pure. Random values must be injected at the boundary. The adapter layer calls `Math.floor(Math.random() * 20) + 1` and passes the result as a parameter. **Alternatives considered**: - Generate in domain layer — violates constitution - Generate in application layer — still impure; application should only orchestrate - Inject a `DiceRoller` port — over-engineered for Math.random(); would need mocking in tests for no real benefit ## Decision 2: New domain function vs. inline calculation **Decision**: Create a minimal `rollInitiative` pure function in the domain that computes `diceRoll + modifier` and returns the final initiative value. The modifier is obtained from the existing `calculateInitiative` function. **Rationale**: Keeps the formula explicit and testable. Even though it's simple arithmetic, having it as a named function documents the intent and makes tests self-describing. **Alternatives considered**: - Inline the addition in the use case — less testable, mixes concerns - Extend `calculateInitiative` to accept a dice roll — conflates two different calculations (passive vs. rolled) ## Decision 3: Reuse existing `setInitiativeUseCase` vs. new use case **Decision**: Create new use cases (`rollInitiativeUseCase` and `rollAllInitiativeUseCase`) that internally call `setInitiativeUseCase` (or the domain `setInitiative` directly) after computing the rolled value. **Rationale**: The roll use cases add the dice-roll-to-modifier step, then delegate to the existing set-initiative pipeline for persistence and sorting. This avoids duplicating sort/persist logic. **Alternatives considered**: - Call `setInitiativeUseCase` directly from the React hook — would leak initiative modifier calculation into the adapter layer ## Decision 4: Event type for rolled initiative **Decision**: Reuse the existing `InitiativeSet` event. No new event type needed. **Rationale**: From the domain's perspective, rolling initiative is indistinguishable from manually setting it — the combatant's initiative field is updated to a new value. The UI doesn't need to distinguish "rolled" from "typed" initiative values for any current feature. **Alternatives considered**: - New `InitiativeRolled` event with dice details — adds complexity with no current consumer; can be added later if roll history/animation is needed ## Decision 5: d20.svg integration approach **Decision**: Move `d20.svg` into `apps/web/src/assets/` and create a thin React component (`D20Icon`) that renders it as an inline SVG, inheriting `currentColor` for theming. **Rationale**: The SVG already uses `stroke="currentColor"` and `fill="none"`, making it theme-compatible. An inline SVG component allows sizing via className props, consistent with how Lucide React icons work in the project. **Alternatives considered**: - Use as `` tag — loses currentColor theming - Import as Vite asset URL — same limitation as img tag - Keep in project root and reference — clutters root, not idiomatic for web assets ## Decision 6: Roll-all button placement **Decision**: Add the "Roll All Initiative" button to the turn navigation bar (top bar), in the right section alongside the existing clear/trash button. **Rationale**: The turn navigation bar is the encounter-level action area. Rolling all initiative is an encounter-level action, not per-combatant. Placing it here follows the existing pattern (clear encounter button is already there). **Alternatives considered**: - Separate toolbar row — adds visual clutter for a single button - Floating action button — inconsistent with existing UI patterns ## Decision 7: Batch roll — multiple setInitiative calls vs. single bulk operation **Decision**: The batch roll use case iterates over eligible combatants and calls `setInitiative` for each one sequentially within a single store transaction (get once, apply all, save once). **Rationale**: Calling `setInitiativeUseCase` per combatant would cause N store reads/writes and N re-sorts. Instead, the batch use case reads the encounter once, applies all initiative values via the domain `setInitiative` function in a loop (each call returns a new encounter), and saves once at the end. This is both more efficient and produces correct final sort order. **Alternatives considered**: - Call `setInitiativeUseCase` N times — N persists and N sorts (wasteful) - New domain `setMultipleInitiatives` function — unnecessary; looping `setInitiative` on the evolving encounter state achieves the same result