Files
initiative/CLAUDE.md
Lukas c323adc343 Add spec, plan, and tasks for 030-bulk-import-sources feature
Defines the "Bulk Import All Sources" feature for the on-demand bestiary
system: one-click loading of all ~104 bestiary sources with concurrent
fetching, progress feedback, toast notifications, and completion reporting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 22:46:24 +01:00

6.6 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

pnpm check              # Merge gate — must pass before every commit (knip + format + lint + typecheck + test)
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 --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 (e.g., EncounterStore). No business logic here.
  • Web — React adapter. Implements ports using hooks/state.

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.

Conventions

  • Biome 2.0 for formatting and linting (no Prettier, no ESLint). Tab indentation, 80-char lines. Imports are auto-organized alphabetically.
  • TypeScript strict mode with verbatimModuleSyntax. Use .js extensions in relative imports when required by the repo's ESM settings (e.g., ./types.js).
  • 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 and invariants from specs to individual it() blocks.
  • Feature specs live in specs/<feature>/ with spec.md, plan.md, tasks.md. The project constitution is at .specify/memory/constitution.md.

Constitution (key principles)

The constitution (.specify/memory/constitution.md) governs all feature work:

  1. Deterministic Domain Core — Pure state transitions only; no I/O, randomness, or clocks in domain.
  2. Layered Architecture — Domain → Application → Adapters. Never skip layers or reverse dependencies.
  3. Clarification-First — Ask before making non-trivial assumptions.
  4. MVP Baseline — Say "MVP baseline does not include X", never permanent bans.
  5. Every feature begins with a spec — Spec → Plan → Tasks → Implementation.

Active Technologies

  • TypeScript 5.x (strict mode, verbatimModuleSyntax) + React 19, Vite (003-remove-combatant)
  • In-memory React state (local-first, single-user MVP) (003-remove-combatant)
  • TypeScript 5.x (project), Go binary via npm (Lefthook) + lefthook (npm devDependency) (006-pre-commit-gate)
  • TypeScript 5.x (strict mode, verbatimModuleSyntax) + Knip v5 (new), Biome 2.0, Vitest, Vite 6, React 19 (007-add-knip)
  • TypeScript 5.x (strict mode, verbatimModuleSyntax) + React 19, Vite 6, Biome 2.0, existing domain/application packages (008-persist-encounter)
  • Browser localStorage (adapter layer only) (008-persist-encounter)
  • TypeScript 5.x (strict mode, verbatimModuleSyntax) + React 19, Vite 6, Biome 2.0, Vites (009-combatant-hp)
  • TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Vite 6, Tailwind CSS v4, shadcn/ui, Lucide React (icons) (010-ui-baseline)
  • N/A (no storage changes — localStorage persistence unchanged) (010-ui-baseline)
  • TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Vite 6, Tailwind CSS v4, shadcn/ui-style components, Lucide React (icons) (011-quick-hp-input)
  • N/A (no storage changes -- existing localStorage persistence handles HP via adjustHpUseCase) (011-quick-hp-input)
  • N/A (no storage changes -- existing localStorage persistence unchanged) (012-turn-navigation)
  • N/A (no storage changes -- purely derived state, existing localStorage persistence unchanged) (013-hp-status-indicators)
  • TypeScript 5.8 (strict mode, verbatimModuleSyntax) + jscpd (new dev dependency), Lefthook (existing), Biome 2.0 (existing), Knip (existing) (015-add-jscpd-gate)
  • N/A (no storage changes) (015-add-jscpd-gate)
  • Browser localStorage (existing adapter, transparent JSON serialization) (016-combatant-ac)
  • TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Vite 6, Tailwind CSS v4, Lucide React (icons) (017-combat-conditions)
  • N/A (no storage changes — existing localStorage persistence unchanged) (019-combatant-row-declutter)
  • TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Tailwind CSS v4, Lucide React (icons) (020-fix-zero-hp-opacity)
  • Browser localStorage (existing adapter, extended for creatureId) (021-bestiary-statblock)
  • TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Tailwind CSS v4 + React 19, Tailwind CSS v4, Vite 6 (022-fixed-layout-bars)
  • 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)
  • TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Tailwind CSS v4, Lucide React (icons), Vite 6 (026-roll-initiative)
  • N/A (no storage changes — existing localStorage persistence handles initiative via setInitiativeUseCase) (026-roll-initiative)
  • N/A (no storage changes — purely presentational) (027-ui-polish)
  • TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Tailwind CSS v4, Vite 6 + Tailwind CSS v4 (CSS-native @theme theming), Lucide React (icons) (028-semantic-hover-tokens)
  • TypeScript 5.8 (strict mode, verbatimModuleSyntax) + React 19, Vite 6, Tailwind CSS v4, Lucide React (icons), idb (IndexedDB wrapper) (029-on-demand-bestiary)
  • IndexedDB for cached source data (new); localStorage for encounter persistence (existing, unchanged) (029-on-demand-bestiary)
  • IndexedDB for cached source data (existing via bestiary-cache.ts); localStorage for encounter persistence (existing, unchanged) (030-bulk-import-sources)

Recent Changes

  • 003-remove-combatant: Added TypeScript 5.x (strict mode, verbatimModuleSyntax) + React 19, Vite