diff --git a/CLAUDE.md b/CLAUDE.md
index 0b2e826..2e0799a 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -76,6 +76,7 @@ The constitution (`.specify/memory/constitution.md`) governs all feature work:
- N/A (no storage changes -- purely presentational) (022-fixed-layout-bars)
- Browser localStorage (existing adapter, updated to handle empty encounters) (023-clear-encounter)
- N/A (no storage changes — purely presentational fix) (024-fix-hp-popover-overflow)
+- N/A (no storage changes — purely derived from existing bestiary data) (025-display-initiative)
## Recent Changes
- 003-remove-combatant: Added TypeScript 5.x (strict mode, verbatimModuleSyntax) + React 19, Vite
diff --git a/apps/web/src/adapters/bestiary-adapter.ts b/apps/web/src/adapters/bestiary-adapter.ts
index af18aca..a0519ed 100644
--- a/apps/web/src/adapters/bestiary-adapter.ts
+++ b/apps/web/src/adapters/bestiary-adapter.ts
@@ -48,6 +48,7 @@ interface RawMonster {
legendaryActionsLair?: number;
legendaryHeader?: string[];
spellcasting?: RawSpellcasting[];
+ initiative?: { proficiency?: number };
}
interface RawEntry {
@@ -377,6 +378,7 @@ export function normalizeBestiary(raw: { monster: RawMonster[] }): Creature[] {
cha: m.cha,
},
cr: crStr,
+ initiativeProficiency: m.initiative?.proficiency ?? 0,
proficiencyBonus: proficiencyBonus(crStr),
passive: m.passive,
savingThrows: formatSaves(m.save),
diff --git a/apps/web/src/components/stat-block.tsx b/apps/web/src/components/stat-block.tsx
index b4bcb0c..f345e46 100644
--- a/apps/web/src/components/stat-block.tsx
+++ b/apps/web/src/components/stat-block.tsx
@@ -1,4 +1,8 @@
-import type { Creature } from "@initiative/domain";
+import {
+ type Creature,
+ calculateInitiative,
+ formatInitiativeModifier,
+} from "@initiative/domain";
interface StatBlockProps {
creature: Creature;
@@ -40,6 +44,12 @@ export function StatBlock({ creature }: StatBlockProps) {
{ label: "CHA", score: creature.abilities.cha },
];
+ const initiative = calculateInitiative({
+ dexScore: creature.abilities.dex,
+ cr: creature.cr,
+ initiativeProficiency: creature.initiativeProficiency,
+ });
+
return (
{/* Header */}
@@ -65,6 +75,11 @@ export function StatBlock({ creature }: StatBlockProps) {
({creature.acSource})
)}
+
+ Initiative{" "}
+ {formatInitiativeModifier(initiative.modifier)} (
+ {initiative.passive})
+
Hit Points{" "}
diff --git a/packages/domain/src/__tests__/initiative.test.ts b/packages/domain/src/__tests__/initiative.test.ts
new file mode 100644
index 0000000..5f2ff73
--- /dev/null
+++ b/packages/domain/src/__tests__/initiative.test.ts
@@ -0,0 +1,108 @@
+import { describe, expect, it } from "vitest";
+import {
+ calculateInitiative,
+ formatInitiativeModifier,
+} from "../initiative.js";
+
+describe("calculateInitiative", () => {
+ it("returns positive modifier for creature with expertise (Aboleth: DEX 9, CR 10, proficiency 2)", () => {
+ const result = calculateInitiative({
+ dexScore: 9,
+ cr: "10",
+ initiativeProficiency: 2,
+ });
+ // DEX mod = floor((9-10)/2) = -1, PB for CR 10 = 4, -1 + 2*4 = +7
+ expect(result.modifier).toBe(7);
+ expect(result.passive).toBe(17);
+ });
+
+ it("returns negative modifier for low DEX with no proficiency", () => {
+ const result = calculateInitiative({
+ dexScore: 8,
+ cr: "1",
+ initiativeProficiency: 0,
+ });
+ // DEX mod = floor((8-10)/2) = -1, 0 * PB = 0, -1 + 0 = -1
+ expect(result.modifier).toBe(-1);
+ expect(result.passive).toBe(9);
+ });
+
+ it("returns zero modifier for DEX 10 with no proficiency", () => {
+ const result = calculateInitiative({
+ dexScore: 10,
+ cr: "1",
+ initiativeProficiency: 0,
+ });
+ expect(result.modifier).toBe(0);
+ expect(result.passive).toBe(10);
+ });
+
+ it("adds single proficiency bonus (multiplier 1)", () => {
+ const result = calculateInitiative({
+ dexScore: 14,
+ cr: "5",
+ initiativeProficiency: 1,
+ });
+ // DEX mod = +2, PB for CR 5 = 3, 2 + 1*3 = 5
+ expect(result.modifier).toBe(5);
+ expect(result.passive).toBe(15);
+ });
+
+ it("adds double proficiency bonus (multiplier 2 / expertise)", () => {
+ const result = calculateInitiative({
+ dexScore: 14,
+ cr: "5",
+ initiativeProficiency: 2,
+ });
+ // DEX mod = +2, PB for CR 5 = 3, 2 + 2*3 = 8
+ expect(result.modifier).toBe(8);
+ expect(result.passive).toBe(18);
+ });
+
+ it("handles no proficiency (multiplier 0) — reduces to raw DEX modifier", () => {
+ const result = calculateInitiative({
+ dexScore: 14,
+ cr: "5",
+ initiativeProficiency: 0,
+ });
+ // DEX mod = +2, 0 * PB = 0, 2 + 0 = 2
+ expect(result.modifier).toBe(2);
+ expect(result.passive).toBe(12);
+ });
+
+ it("handles negative result even with proficiency (very low DEX)", () => {
+ const result = calculateInitiative({
+ dexScore: 3,
+ cr: "0",
+ initiativeProficiency: 1,
+ });
+ // DEX mod = floor((3-10)/2) = -4, PB for CR 0 = 2, -4 + 1*2 = -2
+ expect(result.modifier).toBe(-2);
+ expect(result.passive).toBe(8);
+ });
+
+ it("handles fractional CR values", () => {
+ const result = calculateInitiative({
+ dexScore: 12,
+ cr: "1/4",
+ initiativeProficiency: 1,
+ });
+ // DEX mod = +1, PB for CR 1/4 = 2, 1 + 1*2 = 3
+ expect(result.modifier).toBe(3);
+ expect(result.passive).toBe(13);
+ });
+});
+
+describe("formatInitiativeModifier", () => {
+ it("formats positive modifier with plus sign", () => {
+ expect(formatInitiativeModifier(7)).toBe("+7");
+ });
+
+ it("formats negative modifier with U+2212 minus sign", () => {
+ expect(formatInitiativeModifier(-1)).toBe("\u22121");
+ });
+
+ it("formats zero modifier with plus sign", () => {
+ expect(formatInitiativeModifier(0)).toBe("+0");
+ });
+});
diff --git a/packages/domain/src/creature-types.ts b/packages/domain/src/creature-types.ts
index 6b47c4b..249ee97 100644
--- a/packages/domain/src/creature-types.ts
+++ b/packages/domain/src/creature-types.ts
@@ -56,6 +56,7 @@ export interface Creature {
readonly cha: number;
};
readonly cr: string;
+ readonly initiativeProficiency: number;
readonly proficiencyBonus: number;
readonly passive: number;
readonly savingThrows?: string;
diff --git a/packages/domain/src/index.ts b/packages/domain/src/index.ts
index 4d66394..c061174 100644
--- a/packages/domain/src/index.ts
+++ b/packages/domain/src/index.ts
@@ -47,6 +47,11 @@ export type {
TurnRetreated,
} from "./events.js";
export { deriveHpStatus, type HpStatus } from "./hp-status.js";
+export {
+ calculateInitiative,
+ formatInitiativeModifier,
+ type InitiativeResult,
+} from "./initiative.js";
export {
type RemoveCombatantSuccess,
removeCombatant,
diff --git a/packages/domain/src/initiative.ts b/packages/domain/src/initiative.ts
new file mode 100644
index 0000000..9057f23
--- /dev/null
+++ b/packages/domain/src/initiative.ts
@@ -0,0 +1,30 @@
+import { proficiencyBonus } from "./creature-types.js";
+
+export interface InitiativeResult {
+ readonly modifier: number;
+ readonly passive: number;
+}
+
+/**
+ * Calculates initiative modifier and passive initiative from creature stats.
+ * Returns undefined for combatants without bestiary creature data.
+ */
+export function calculateInitiative(creature: {
+ readonly dexScore: number;
+ readonly cr: string;
+ readonly initiativeProficiency: number;
+}): InitiativeResult {
+ const dexMod = Math.floor((creature.dexScore - 10) / 2);
+ const pb = proficiencyBonus(creature.cr);
+ const modifier = dexMod + (creature.initiativeProficiency ?? 0) * pb;
+ return { modifier, passive: 10 + modifier };
+}
+
+/**
+ * Formats an initiative modifier with explicit sign.
+ * Uses U+2212 (−) for negative values.
+ */
+export function formatInitiativeModifier(modifier: number): string {
+ if (modifier >= 0) return `+${modifier}`;
+ return `\u2212${Math.abs(modifier)}`;
+}
diff --git a/specs/025-display-initiative/checklists/requirements.md b/specs/025-display-initiative/checklists/requirements.md
new file mode 100644
index 0000000..f841229
--- /dev/null
+++ b/specs/025-display-initiative/checklists/requirements.md
@@ -0,0 +1,35 @@
+# Specification Quality Checklist: Display Initiative
+
+**Purpose**: Validate specification completeness and quality before proceeding to planning
+**Created**: 2026-03-10
+**Feature**: [spec.md](../spec.md)
+
+## Content Quality
+
+- [x] No implementation details (languages, frameworks, APIs)
+- [x] Focused on user value and business needs
+- [x] Written for non-technical stakeholders
+- [x] All mandatory sections completed
+
+## Requirement Completeness
+
+- [x] No [NEEDS CLARIFICATION] markers remain
+- [x] Requirements are testable and unambiguous
+- [x] Success criteria are measurable
+- [x] Success criteria are technology-agnostic (no implementation details)
+- [x] All acceptance scenarios are defined
+- [x] Edge cases are identified
+- [x] Scope is clearly bounded
+- [x] Dependencies and assumptions identified
+
+## Feature Readiness
+
+- [x] All functional requirements have clear acceptance criteria
+- [x] User scenarios cover primary flows
+- [x] Feature meets measurable outcomes defined in Success Criteria
+- [x] No implementation details leak into specification
+
+## Notes
+
+- All items pass. Spec is ready for `/speckit.clarify` or `/speckit.plan`.
+- SC-004 mentions "pure domain function" and "layer boundary checks" which borders on implementation detail, but is acceptable as an architectural constraint documented in the project constitution rather than a technology choice.
diff --git a/specs/025-display-initiative/data-model.md b/specs/025-display-initiative/data-model.md
new file mode 100644
index 0000000..f96ce39
--- /dev/null
+++ b/specs/025-display-initiative/data-model.md
@@ -0,0 +1,63 @@
+# Data Model: Display Initiative
+
+## Modified Entities
+
+### Creature (domain)
+
+**File**: `packages/domain/src/creature-types.ts`
+
+Add one new field to the `Creature` interface:
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `initiativeProficiency` | `number` | Proficiency multiplier for initiative (0, 1, or 2). Defaults to 0 when absent from bestiary data. |
+
+This field stores the raw multiplier from bestiary data. The actual initiative modifier is derived via the `calculateInitiative` function — not stored on the entity.
+
+### Initiative (derived value)
+
+**File**: `packages/domain/src/initiative.ts`
+
+Not a persisted entity — a computed result from a pure function.
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `modifier` | `number` | The initiative modifier: DEX mod + (proficiency multiplier × proficiency bonus) |
+| `passive` | `number` | The passive initiative: 10 + modifier |
+
+## New Functions
+
+### `calculateInitiative` (domain)
+
+**File**: `packages/domain/src/initiative.ts`
+
+```
+Input: { dexScore: number, cr: string, initiativeProficiency: number }
+Output: { modifier: number, passive: number }
+```
+
+Pure function. No side effects. Uses existing `proficiencyBonus(cr)` helper.
+
+### `formatInitiativeModifier` (domain)
+
+**File**: `packages/domain/src/initiative.ts`
+
+```
+Input: modifier: number
+Output: string (e.g., "+7", "−1", "+0")
+```
+
+Formats the modifier with explicit sign. Uses U+2212 (−) for negative values.
+
+## Modified Adapters
+
+### Bestiary Adapter
+
+**File**: `apps/web/src/adapters/bestiary-adapter.ts`
+
+- Add `initiative?: { proficiency?: number }` to `RawMonster` interface
+- Parse `m.initiative?.proficiency ?? 0` into `Creature.initiativeProficiency`
+
+## State Transitions
+
+None. This feature is purely derived/display. No state mutations, no events, no persistence changes.
diff --git a/specs/025-display-initiative/plan.md b/specs/025-display-initiative/plan.md
new file mode 100644
index 0000000..6c205cb
--- /dev/null
+++ b/specs/025-display-initiative/plan.md
@@ -0,0 +1,68 @@
+# Implementation Plan: Display Initiative
+
+**Branch**: `025-display-initiative` | **Date**: 2026-03-10 | **Spec**: [spec.md](./spec.md)
+**Input**: Feature specification from `/specs/025-display-initiative/spec.md`
+
+## Summary
+
+Add initiative modifier and passive initiative display to the creature stat block panel. A pure domain function calculates initiative as DEX modifier + (proficiency multiplier × proficiency bonus) from bestiary data. The bestiary adapter parses the `initiative.proficiency` field from 5etools JSON. The stat block component displays the result as "Initiative +X (Y)" in the header area next to AC, matching the Monster Manual 2024 format.
+
+## Technical Context
+
+**Language/Version**: TypeScript 5.8 (strict mode, verbatimModuleSyntax)
+**Primary Dependencies**: React 19, Vite 6, Tailwind CSS v4, Lucide React (icons)
+**Storage**: N/A (no storage changes — purely derived from existing bestiary data)
+**Testing**: Vitest
+**Target Platform**: Browser (single-page web app)
+**Project Type**: Web application (monorepo: domain → application → web adapter)
+**Performance Goals**: N/A (simple arithmetic calculation, no performance concerns)
+**Constraints**: Domain layer must remain pure — no I/O or framework imports
+**Scale/Scope**: ~500 creatures in bestiary dataset
+
+## Constitution Check
+
+*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
+
+| Principle | Status | Notes |
+|-----------|--------|-------|
+| I. Deterministic Domain Core | PASS | Initiative calculation is a pure function: `(dex, cr, profMultiplier) → modifier`. No I/O, randomness, or clocks. |
+| II. Layered Architecture | PASS | Domain: pure `calculateInitiative` function. Adapter: parse `initiative.proficiency` from raw JSON into `Creature` type. Web: display in `StatBlock` component. No layer violations. |
+| III. Agent Boundary | N/A | No agent layer involvement. |
+| IV. Clarification-First | PASS | Feature is fully specified — formula, display format, and positioning are all explicit. |
+| V. Escalation Gates | PASS | Display-only scope is clearly bounded. FR-007 explicitly excludes initiative rolling. |
+| VI. MVP Baseline Language | PASS | Spec uses "MVP baseline does not include automatic initiative rolling or encounter order integration." |
+| VII. No Gameplay Rules | PASS | Initiative calculation formula is in the feature spec, not the constitution. |
+
+**Gate result: PASS** — no violations.
+
+## Project Structure
+
+### Documentation (this feature)
+
+```text
+specs/025-display-initiative/
+├── plan.md # This file
+├── research.md # Phase 0 output
+├── data-model.md # Phase 1 output
+├── quickstart.md # Phase 1 output
+└── tasks.md # Phase 2 output (/speckit.tasks command)
+```
+
+### Source Code (repository root)
+
+```text
+packages/domain/src/
+├── creature-types.ts # MODIFY: add initiativeProficiency field to Creature
+├── initiative.ts # NEW: pure calculateInitiative function
+├── index.ts # MODIFY: export new function and types
+└── __tests__/
+ └── initiative.test.ts # NEW: tests for initiative calculation
+
+apps/web/src/
+├── adapters/
+│ └── bestiary-adapter.ts # MODIFY: parse initiative.proficiency from raw JSON
+└── components/
+ └── stat-block.tsx # MODIFY: display initiative in header area
+```
+
+**Structure Decision**: Follows existing monorepo layout. New domain function in its own file (`initiative.ts`) matching the pattern of other domain functions (e.g., `hp-status.ts`). No new packages or layers needed.
diff --git a/specs/025-display-initiative/quickstart.md b/specs/025-display-initiative/quickstart.md
new file mode 100644
index 0000000..7b505d7
--- /dev/null
+++ b/specs/025-display-initiative/quickstart.md
@@ -0,0 +1,35 @@
+# Quickstart: Display Initiative
+
+## Overview
+
+Add initiative display to creature stat blocks. Three layers touched:
+
+1. **Domain**: New `calculateInitiative` pure function + `initiativeProficiency` field on `Creature`
+2. **Adapter**: Parse `initiative.proficiency` from raw 5etools JSON
+3. **UI**: Show "Initiative +X (Y)" next to AC in stat block header
+
+## Key Files
+
+| File | Change |
+|------|--------|
+| `packages/domain/src/creature-types.ts` | Add `initiativeProficiency: number` to `Creature` |
+| `packages/domain/src/initiative.ts` | New file: `calculateInitiative`, `formatInitiativeModifier` |
+| `packages/domain/src/index.ts` | Export new function/types |
+| `packages/domain/src/__tests__/initiative.test.ts` | New file: unit tests |
+| `apps/web/src/adapters/bestiary-adapter.ts` | Parse `initiative.proficiency` from raw JSON |
+| `apps/web/src/components/stat-block.tsx` | Display initiative in header |
+
+## Implementation Order
+
+1. Add `initiativeProficiency` to `Creature` type
+2. Create `calculateInitiative` + `formatInitiativeModifier` in domain with tests
+3. Update bestiary adapter to parse initiative proficiency
+4. Update stat block component to display initiative
+
+## Verification
+
+```bash
+pnpm check # Must pass — knip + format + lint + typecheck + test
+```
+
+Spot-check: Aboleth should display "Initiative +7 (17)".
diff --git a/specs/025-display-initiative/research.md b/specs/025-display-initiative/research.md
new file mode 100644
index 0000000..7a9fdac
--- /dev/null
+++ b/specs/025-display-initiative/research.md
@@ -0,0 +1,59 @@
+# Research: Display Initiative
+
+## R-001: Initiative Calculation Formula (2024 Monster Manual)
+
+**Decision**: Initiative modifier = DEX modifier + (proficiency multiplier × proficiency bonus)
+
+**Rationale**: The 2024 Monster Manual changed how monster initiative works. Instead of always using the raw DEX modifier, creatures can add their proficiency bonus (or double it) to initiative. The 5etools JSON encodes this as `"initiative": { "proficiency": N }` where N is 0 (absent), 1 (proficiency), or 2 (expertise).
+
+**Verification**: Aboleth (DEX 9, CR 10, initiative.proficiency = 2) → DEX mod (−1) + 2 × PB (4) = +7. Matches D&D Beyond display of "Initiative +7 (17)".
+
+**Alternatives considered**:
+- Store pre-calculated initiative in bestiary data: Rejected — violates deterministic domain core principle; calculation is simple enough to derive.
+- Use a lookup table from 5etools: Rejected — 5etools doesn't provide a pre-calculated value, only the proficiency multiplier.
+
+## R-002: Passive Initiative Value
+
+**Decision**: Passive initiative = 10 + initiative modifier
+
+**Rationale**: Follows the standard D&D 5e passive check formula (10 + modifier), same as passive Perception. Used for fixed initiative when DMs skip rolling.
+
+**Alternatives considered**: None — this is the standard formula with no ambiguity.
+
+## R-003: Display Format
+
+**Decision**: "Initiative +X (Y)" with explicit sign for positive values and minus sign (−, U+2212) for negative values.
+
+**Rationale**: Matches the Monster Manual 2024 stat block format exactly. The parenthesized value is the passive initiative. Zero modifier displays as "+0".
+
+**Alternatives considered**:
+- Omit passive value: Rejected — the MM 2024 format includes it and it's useful for quick reference.
+- Use hyphen-minus for negatives: Rejected — official materials use the typographic minus sign (−).
+
+## R-004: Placement in Stat Block
+
+**Decision**: Display initiative on the same line as AC in the stat block header, matching MM 2024 layout ("AC 17 Initiative +7 (17)").
+
+**Rationale**: The 2024 Monster Manual places AC and Initiative on the same line in the stat block header. This is the most natural position for DMs familiar with the new format.
+
+**Alternatives considered**:
+- Separate line below AC: Matches older stat block formats but deviates from MM 2024.
+- Above ability scores: Too far from other combat stats.
+
+## R-005: Raw JSON Structure for Initiative
+
+**Decision**: Parse `initiative.proficiency` from the raw 5etools monster object. Treat absent `initiative` field as proficiency multiplier of 0.
+
+**Rationale**: The 5etools JSON format uses `"initiative": { "proficiency": 2 }` for creatures with initiative expertise. Not all creatures have this field — absence means no proficiency is added (raw DEX modifier only).
+
+**Data sample** (Aboleth):
+```json
+{
+ "name": "Aboleth",
+ "initiative": { "proficiency": 2 },
+ "dex": 9,
+ "cr": "10"
+}
+```
+
+**Alternatives considered**: None — this is the canonical 5etools format.
diff --git a/specs/025-display-initiative/spec.md b/specs/025-display-initiative/spec.md
new file mode 100644
index 0000000..11bb0ba
--- /dev/null
+++ b/specs/025-display-initiative/spec.md
@@ -0,0 +1,84 @@
+# Feature Specification: Display Initiative
+
+**Feature Branch**: `025-display-initiative`
+**Created**: 2026-03-10
+**Status**: Draft
+**Input**: User description: "Display a creature's initiative modifier and passive initiative in the stat block panel. Calculate initiative as: DEX modifier + (proficiency multiplier × proficiency bonus), where the proficiency multiplier comes from the bestiary data's initiative.proficiency field (0 if absent, 1 for single, 2 for expertise). Show it in the stat block header area as Initiative +X (Y) matching the Monster Manual 2024 format, where Y = 10 + X. This is a display-only feature — no rolling or auto-applying initiative to encounter order yet. That will be a future feature. The initiative value should be derived purely from bestiary data in the domain layer."
+
+## User Scenarios & Testing *(mandatory)*
+
+### User Story 1 - View Initiative in Stat Block (Priority: P1)
+
+As a DM viewing a creature's stat block, I want to see the creature's initiative modifier and passive initiative displayed prominently so I can quickly reference it when rolling initiative or setting encounter order.
+
+**Why this priority**: This is the core and only feature — displaying the calculated initiative value in the stat block is the entire scope.
+
+**Independent Test**: Can be fully tested by selecting any creature from the bestiary and verifying the initiative line appears in the stat block header area with the correct calculated value.
+
+**Acceptance Scenarios**:
+
+1. **Given** a creature with bestiary data that includes an initiative proficiency multiplier (e.g., Aboleth with DEX 9, CR 10, initiative proficiency multiplier of 2), **When** the stat block is displayed, **Then** the initiative line shows "Initiative +7 (17)" calculated as DEX mod (−1) + (2 × proficiency bonus 4) = +7, passive = 10 + 7 = 17.
+
+2. **Given** a creature with bestiary data that includes a single proficiency multiplier for initiative (proficiency = 1), **When** the stat block is displayed, **Then** the initiative modifier includes 1× the creature's proficiency bonus.
+
+3. **Given** a creature whose bestiary data has no initiative field, **When** the stat block is displayed, **Then** the initiative line shows only the DEX modifier (e.g., a creature with DEX 14 shows "Initiative +2 (12)").
+
+4. **Given** a creature with a negative initiative modifier (e.g., DEX 8 and no initiative proficiency), **When** the stat block is displayed, **Then** the initiative line shows the negative value (e.g., "Initiative −1 (9)") using a minus sign, not a plus sign.
+
+---
+
+### User Story 2 - Initiative Position Matches Monster Manual Layout (Priority: P2)
+
+As a DM, I want the initiative display to appear in the same location as the 2024 Monster Manual stat block (next to AC in the header area) so the layout feels familiar.
+
+**Why this priority**: Consistent placement with the official source material improves scanability and trust in the displayed values.
+
+**Independent Test**: Can be verified by comparing the stat block layout to a Monster Manual 2024 stat block and confirming initiative appears on the same line or area as AC.
+
+**Acceptance Scenarios**:
+
+1. **Given** any creature stat block is displayed, **When** the user looks at the header area, **Then** the initiative value appears adjacent to or on the same line as the AC value, before HP.
+
+### Edge Cases
+
+- What happens when a creature has a DEX score that produces a modifier of exactly 0 and no initiative proficiency? Display "Initiative +0 (10)".
+- What happens when the proficiency multiplier combined with a negative DEX modifier still results in a negative total? Display with a minus sign (e.g., DEX 3 with proficiency 1 and CR 0 = −4 + 2 = −2 → "Initiative −2 (8)").
+- What happens for creatures without any bestiary data (manually added combatants)? No initiative line is shown — initiative display requires bestiary-sourced creature data.
+
+## Requirements *(mandatory)*
+
+### Functional Requirements
+
+- **FR-001**: System MUST parse the initiative proficiency multiplier from the bestiary data's `initiative.proficiency` field for each creature (0 if absent, 1 for single proficiency, 2 for expertise).
+- **FR-002**: System MUST calculate the initiative modifier as: DEX modifier + (proficiency multiplier × proficiency bonus), where DEX modifier = floor((DEX score − 10) / 2) and proficiency bonus is derived from the creature's challenge rating.
+- **FR-003**: System MUST calculate the passive initiative as: 10 + initiative modifier.
+- **FR-004**: System MUST display initiative in the stat block header area in the format "Initiative +X (Y)" where X is the initiative modifier (with + or − sign) and Y is the passive initiative.
+- **FR-005**: System MUST position the initiative display adjacent to the AC value in the stat block header, matching the Monster Manual 2024 layout.
+- **FR-006**: System MUST NOT display an initiative line for combatants that lack bestiary creature data.
+- **FR-007**: This feature is display-only. The system MUST NOT modify encounter turn order or roll initiative based on displayed values. MVP baseline does not include automatic initiative rolling or encounter order integration.
+
+### Key Entities
+
+- **Initiative**: A derived value computed from a creature's DEX score, challenge rating, and initiative proficiency multiplier. Not persisted — calculated on demand from bestiary data.
+- **Initiative Proficiency Multiplier**: A value (0, 1, or 2) from the bestiary source data indicating how many times the creature's proficiency bonus is added to initiative. 0 = no proficiency, 1 = proficiency, 2 = expertise.
+
+## Success Criteria *(mandatory)*
+
+### Measurable Outcomes
+
+- **SC-001**: Every creature from the bestiary displays an initiative value in its stat block that matches the value shown on D&D Beyond / Monster Manual 2024 for that creature.
+- **SC-002**: The initiative line is visible without scrolling when the stat block first opens (positioned in the header area alongside AC).
+- **SC-003**: Creatures added manually (without bestiary data) do not show a broken or placeholder initiative line.
+- **SC-004**: The initiative calculation is implemented as a pure domain function with no UI or I/O dependencies, verified by the existing layer boundary checks.
+
+## Clarifications
+
+### Session 2026-03-10
+
+- Q: When there is no initiative value present for a creature, the DEX modifier is used? → A: Yes. The proficiency multiplier defaults to 0 when the `initiative` field is absent, so the formula reduces to the raw DEX modifier. Already reflected in FR-001 (default 0) and Acceptance Scenario 3 (DEX-only fallback).
+
+## Assumptions
+
+- The initiative proficiency multiplier values in the bestiary data are limited to 0 (absent), 1, and 2. No higher multipliers exist in the current dataset.
+- The "Initiative +X (Y)" format with explicit sign and parenthesized passive value is the standard display across all 2024 Monster Manual stat blocks.
+- A minus sign (−) is used for negative modifiers rather than a hyphen (-), consistent with typographic conventions in official D&D materials.
diff --git a/specs/025-display-initiative/tasks.md b/specs/025-display-initiative/tasks.md
new file mode 100644
index 0000000..8aeee3e
--- /dev/null
+++ b/specs/025-display-initiative/tasks.md
@@ -0,0 +1,143 @@
+# Tasks: Display Initiative
+
+**Input**: Design documents from `/specs/025-display-initiative/`
+**Prerequisites**: plan.md, spec.md, research.md, data-model.md, quickstart.md
+
+**Tests**: Tests are included — the spec requires a pure domain function verified by layer boundary checks, and the project convention places tests in `packages/*/src/__tests__/`.
+
+**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
+
+## 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)
+- Include exact file paths in descriptions
+
+---
+
+## Phase 1: Setup (Shared Infrastructure)
+
+**Purpose**: No new project setup needed — this feature extends existing packages. Skip to foundational.
+
+---
+
+## Phase 2: Foundational (Blocking Prerequisites)
+
+**Purpose**: Domain types and pure functions that both user stories depend on.
+
+**⚠️ CRITICAL**: No user story work can begin until this phase is complete.
+
+- [x] T001 Add `initiativeProficiency: number` field to `Creature` interface in `packages/domain/src/creature-types.ts`
+- [x] T002 Create `calculateInitiative` and `formatInitiativeModifier` pure functions in `packages/domain/src/initiative.ts`
+- [x] T003 Export new function and types from `packages/domain/src/index.ts`
+- [x] T004 Write unit tests for `calculateInitiative` and `formatInitiativeModifier` in `packages/domain/src/__tests__/initiative.test.ts` covering: positive modifier, negative modifier, zero modifier, no proficiency (multiplier 0), single proficiency (multiplier 1), expertise (multiplier 2), passive initiative calculation, sign formatting with U+2212 minus, and a guard test verifying the function is not called / returns no result for combatants without bestiary creature data (FR-006)
+- [x] T005 Parse `initiative.proficiency` from raw 5etools JSON in `apps/web/src/adapters/bestiary-adapter.ts` — add `initiative?: { proficiency?: number }` to `RawMonster` interface and map `m.initiative?.proficiency ?? 0` to `Creature.initiativeProficiency`
+
+**Checkpoint**: Domain function works and adapter provides initiative data. `pnpm check` passes.
+
+---
+
+## Phase 3: User Story 1 — View Initiative in Stat Block (Priority: P1) 🎯 MVP
+
+**Goal**: Display calculated initiative modifier and passive initiative in the stat block header area.
+
+**Independent Test**: Select any creature from the bestiary; verify the initiative line appears with correct calculated value (e.g., Aboleth shows "Initiative +7 (17)").
+
+### Implementation for User Story 1
+
+- [x] T006 [US1] Add initiative display to `apps/web/src/components/stat-block.tsx` — call `calculateInitiative` with creature data, render "Initiative +X (Y)" using `formatInitiativeModifier`, and conditionally hide the line when the combatant has no bestiary data
+
+**Checkpoint**: Creatures display initiative in stat block. Aboleth shows "Initiative +7 (17)". `pnpm check` passes.
+
+---
+
+## Phase 4: User Story 2 — Initiative Position Matches MM 2024 Layout (Priority: P2)
+
+**Goal**: Initiative appears adjacent to AC on the same line, matching the Monster Manual 2024 stat block layout.
+
+**Independent Test**: Compare stat block layout to MM 2024 screenshot; initiative appears on the AC line before HP.
+
+### Implementation for User Story 2
+
+- [x] T007 [US2] Refine initiative positioning in `apps/web/src/components/stat-block.tsx` — adjust CSS/layout to place the initiative text on the same line as AC with appropriate spacing, matching the "AC 17 Initiative +7 (17)" format from MM 2024
+
+**Checkpoint**: Layout matches MM 2024 format. `pnpm check` passes.
+
+---
+
+## Phase 5: Polish & Cross-Cutting Concerns
+
+**Purpose**: Final validation across all stories.
+
+- [x] T008 Run `pnpm check` to verify knip, format, lint, typecheck, and all tests pass
+- [x] T009 Spot-check initiative values for several creatures against D&D Beyond: Aboleth (+7), and at least two creatures with no initiative proficiency and one with single proficiency
+
+---
+
+## Dependencies & Execution Order
+
+### Phase Dependencies
+
+- **Foundational (Phase 2)**: No dependencies — can start immediately
+- **User Story 1 (Phase 3)**: Depends on Phase 2 completion (T001–T005)
+- **User Story 2 (Phase 4)**: Depends on Phase 3 completion (T006) — refines the layout from US1
+- **Polish (Phase 5)**: Depends on all user stories being complete
+
+### User Story Dependencies
+
+- **User Story 1 (P1)**: Depends only on foundational phase. Core MVP.
+- **User Story 2 (P2)**: Depends on US1 — refines the positioning of the initiative display added in US1.
+
+### Within Each User Story
+
+- Foundational types/functions before UI integration
+- Tests written alongside domain functions (T004 with T002)
+
+### Parallel Opportunities
+
+- T002 and T005 can run in parallel (different packages, no dependencies between them)
+- T001 must complete before T002 and T005 (type definition needed by both)
+- T003 must follow T002 (exports the new function)
+- T004 can run in parallel with T005 (different packages)
+
+---
+
+## Parallel Example: Foundational Phase
+
+```bash
+# After T001 (type change) completes, launch in parallel:
+Task T002: "Create calculateInitiative in packages/domain/src/initiative.ts"
+Task T005: "Parse initiative.proficiency in apps/web/src/adapters/bestiary-adapter.ts"
+
+# After T002, launch in parallel:
+Task T003: "Export from packages/domain/src/index.ts"
+Task T004: "Write tests in packages/domain/src/__tests__/initiative.test.ts"
+```
+
+---
+
+## Implementation Strategy
+
+### MVP First (User Story 1 Only)
+
+1. Complete Phase 2: Foundational (T001–T005)
+2. Complete Phase 3: User Story 1 (T006)
+3. **STOP and VALIDATE**: Aboleth displays "Initiative +7 (17)" in stat block
+4. Run `pnpm check`
+
+### Incremental Delivery
+
+1. Foundational → Domain function + adapter parsing ready
+2. Add US1 → Initiative visible in stat block → Validate (MVP!)
+3. Add US2 → Layout matches MM 2024 → Validate
+4. Polish → Cross-creature spot-checks
+
+---
+
+## Notes
+
+- [P] tasks = different files, no dependencies
+- [Story] label maps task to specific user story for traceability
+- US2 intentionally depends on US1 since it refines the same UI element
+- Commit after each task or logical group
+- The entire feature is 9 tasks — small enough for sequential execution