# Implementation Plan: Encounter Difficulty Indicator **Branch**: `008-encounter-difficulty` | **Date**: 2026-03-27 | **Spec**: [spec.md](spec.md) **Input**: Feature specification from `/specs/008-encounter-difficulty/spec.md` ## Summary Add a live 3-bar encounter difficulty indicator to the top bar, based on the 2024 5.5e XP budget system. The domain layer gains pure lookup tables (CR-to-XP, XP Budget per Character) and a difficulty calculation function. The `PlayerCharacter` type gains an optional `level` field (1-20). The UI renders a compact bar indicator that derives difficulty from encounter combatants, player character levels, and bestiary creature CRs. ## Technical Context **Language/Version**: TypeScript 5.8 (strict mode, `verbatimModuleSyntax`) **Primary Dependencies**: React 19, Vite 6, Tailwind CSS v4, Lucide React (icons) **Storage**: localStorage (encounter + player characters), IndexedDB (bestiary cache) **Testing**: Vitest (v8 coverage) **Target Platform**: Web (mobile-first responsive, desktop side panels) **Project Type**: Web application (monorepo: domain → application → web adapter) **Performance Goals**: Indicator updates within the same render cycle as combatant changes **Constraints**: Offline-capable, local-first, single-user. Max 8 props per component. **Scale/Scope**: Single-page app, ~15 components, 3-layer architecture ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* | Principle | Status | Notes | |-----------|--------|-------| | I. Deterministic Domain Core | PASS | Difficulty calculation is a pure function: `(partyLevels, monsterCRs) → DifficultyResult`. No I/O, no randomness, no clocks. Lookup tables are static data. | | II. Layered Architecture | PASS | New domain module (`encounter-difficulty.ts`) has zero imports from application/adapter. UI component composes data from existing contexts. | | II-A. Context-Based State Flow | PASS | Difficulty indicator reads from existing contexts (encounter, player characters, bestiary). No new props beyond what's needed for the component itself. | | III. Clarification-First | PASS | All design decisions resolved in spec: optional level, 3 tiers, 5.5e rules only, no multipliers, hidden when insufficient data. | | IV. Escalation Gates | PASS | Feature scoped to spec. MVP exclusions documented (no custom CR, no 2014 rules, no XP numbers in UI). | | V. MVP Baseline Language | PASS | Exclusions use "MVP baseline does not include" language. | | VI. No Gameplay Rules in Constitution | PASS | XP tables and difficulty rules live in the feature spec, not the constitution. | No violations. Complexity Tracking section not needed. ## Project Structure ### Documentation (this feature) ```text specs/008-encounter-difficulty/ ├── 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) ``` ### Source Code (repository root) ```text packages/domain/src/ ├── encounter-difficulty.ts # NEW: CR-to-XP, XP budget tables, difficulty calc ├── player-character-types.ts # MODIFY: add optional level field ├── create-player-character.ts # MODIFY: add level validation ├── edit-player-character.ts # MODIFY: add level validation + apply ├── __tests__/ │ ├── encounter-difficulty.test.ts # NEW: unit tests for difficulty calc │ ├── create-player-character.test.ts # MODIFY: add level tests │ └── edit-player-character.test.ts # MODIFY: add level tests └── index.ts # MODIFY: export new functions/types packages/application/src/ ├── create-player-character-use-case.ts # MODIFY: pass level through └── edit-player-character-use-case.ts # MODIFY: pass level through apps/web/src/ ├── components/ │ ├── difficulty-indicator.tsx # NEW: 3-bar indicator component │ ├── turn-navigation.tsx # MODIFY: add indicator to top bar │ ├── create-player-modal.tsx # MODIFY: add level field │ └── player-character-manager.tsx # MODIFY: show level, pass to edit ├── hooks/ │ └── use-difficulty.ts # NEW: hook composing contexts → difficulty result └── contexts/ └── player-characters-context.tsx # MODIFY: pass level to create/edit ``` **Structure Decision**: Follows existing layered architecture. New domain module for difficulty calculation. New UI component + hook at adapter layer. No new contexts needed — the difficulty hook composes existing contexts.