Implement the 025-display-initiative feature that adds initiative modifier and passive initiative display to creature stat blocks, calculated as DEX modifier + (proficiency multiplier × proficiency bonus) from bestiary data, shown in MM 2024 format on the AC line

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-10 11:27:46 +01:00
parent c6349928eb
commit 5b0bac880d
14 changed files with 650 additions and 1 deletions

View File

@@ -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 (T001T005)
- **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 (T001T005)
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