# CLAUDE.md **Initiative** is a browser-based combat encounter tracker for tabletop RPGs (D&D 5.5e, Pathfinder 2e). It runs entirely client-side — no backend, no accounts — with localStorage and IndexedDB for persistence. ## Commands ```bash pnpm check # Merge gate — must pass before every commit (audit + knip + biome + oxlint + typecheck + test/coverage + jscpd + jsinspect) pnpm oxlint # Type-aware linting (oxlint — complements Biome) pnpm knip # Unused code detection (Knip) pnpm test # Run all tests (Vitest) pnpm test:watch # Tests in watch mode pnpm typecheck # tsc --build (project references) pnpm lint # Biome lint pnpm format # Biome format (writes) pnpm check:props # Component prop count enforcement (max 8) pnpm --filter web dev # Vite dev server (localhost:5173) pnpm --filter web build # Production build ``` Run a single test file: `pnpm vitest run packages/domain/src/__tests__/advance-turn.test.ts` ## Architecture Strict layered architecture with ports/adapters and enforced dependency direction: ``` apps/web (React 19 + Vite) → packages/application (use cases) → packages/domain (pure logic) ``` - **Domain** — Pure functions, no I/O, no framework imports. All state transitions are deterministic. Errors returned as values (`DomainError`), never thrown. Adapters may throw only for programmer errors. - **Application** — Orchestrates domain calls via port interfaces (`EncounterStore`, `BestiarySourceCache`). No business logic here. - **Web** — React adapter. Implements ports using hooks/state. All UI components and user interaction live here. Layer boundaries are enforced by `scripts/check-layer-boundaries.mjs`, which runs as a Vitest test. Domain and application must never import from React, Vite, or upper layers. ### Data & Storage - **localStorage** — encounter persistence (adapter layer, JSON serialization) - **IndexedDB** — bestiary source cache (`apps/web/src/adapters/bestiary-cache.ts`, via `idb` wrapper) - **`data/bestiary/index.json`** — pre-built search index for creature lookup, generated by `scripts/generate-bestiary-index.mjs` ### Project Structure ``` apps/web/ React app — components, hooks, adapters packages/domain/src/ Pure state transitions, types, validation packages/application/src/ Use cases, port interfaces data/bestiary/ Bestiary search index scripts/ Build tooling (layer checks, index generation) specs/NNN-feature-name/ Feature specs (spec.md, plan.md, tasks.md) .specify/ Speckit config (templates, scripts, constitution) docs/agents/ RPI skill artifacts (research reports, plans) .claude/skills/ Agent skills (rpi-research, rpi-plan, rpi-implement) ``` ## Tech Stack - TypeScript 5.8 (strict mode, `verbatimModuleSyntax`) - React 19, Vite 6, Tailwind CSS v4 - Lucide React (icons) - `idb` (IndexedDB wrapper for bestiary cache) - Biome 2.4 (formatting + linting), oxlint (type-aware linting), Knip (unused code), jscpd (copy-paste detection), jsinspect-plus (structural duplication) - Vitest (testing, v8 coverage), Lefthook (pre-commit hooks) ## Conventions - **Biome 2.4** for formatting and linting (no Prettier, no ESLint). Tab indentation, 80-char lines. Imports are auto-organized alphabetically. - **oxlint** for type-aware linting that Biome can't do. Configured in `.oxlintrc.json`. - **TypeScript strict mode** with `verbatimModuleSyntax`. Use `.js` extensions in relative imports. - **Branded types** for identity values (e.g., `CombatantId`). Prefer immutability/`readonly` where practical. - **Domain events** are plain data objects with a `type` discriminant — no classes. - **Tests** live in `packages/*/src/__tests__/*.test.ts`. Test pure functions directly; map acceptance scenarios from specs to individual `it()` blocks. - **Quality gates** are enforced at pre-commit via Lefthook (parallel jobs). No gate may exist only as a CI step or manual process. For component prop rules, export format compatibility, and ADRs, see [`docs/conventions.md`](docs/conventions.md). ## Self-Review Checklist Before finishing a change, consider: - Is this the simplest approach that solves the current problem? - Is there duplication that hurts readability? (But don't abstract prematurely.) - Are errors handled correctly and communicated sensibly to the user? - Does the UI follow modern patterns and feel intuitive to interact with? ## Speckit Workflow Specs are **living documents** in `specs/NNN-feature-name/` that describe features, not individual changes. Use `/speckit.*` and RPI skills (`rpi-research`, `rpi-plan`, `rpi-implement`) to manage them — skill descriptions have full usage details. | Scope | Workflow | |---|---| | Bug fix / CSS tweak | Just fix it, commit | | Small change to existing feature | `/integrate-issue` → implement → commit | | Larger addition to existing feature | `/integrate-issue` → `rpi-research` → `rpi-plan` → `rpi-implement` | | New feature | `/speckit.specify` → `/speckit.clarify` → `/speckit.plan` → `/speckit.tasks` → `/speckit.implement` | **Research scope**: Always scan for existing patterns similar to what the feature needs. Identify extraction and consolidation opportunities before implementation, not during. ## Constitution Project principles governing all feature work are in [`.specify/memory/constitution.md`](.specify/memory/constitution.md). Key rules: deterministic domain core, strict layer boundaries, clarification before assumptions.