diff --git a/.specify/memory/constitution.md b/.specify/memory/constitution.md index c064026..6c1b48d 100644 --- a/.specify/memory/constitution.md +++ b/.specify/memory/constitution.md @@ -1,14 +1,17 @@ # Encounter Console Constitution @@ -29,7 +32,7 @@ be injected at the boundary, never sourced inside the domain layer. ### II. Layered Architecture -The codebase MUST be organized into four layers with strict +The codebase MUST be organized into three layers with strict dependency direction: 1. **Domain** — pure types, state transitions, validation rules. @@ -39,34 +42,21 @@ dependency direction: interfaces that Adapters implement. May import Domain only. 3. **Adapters** — I/O, persistence, UI rendering, external APIs. May import Application and Domain. -4. **Agent** — AI-assisted features (suggestions, analysis). - May import Application and Domain as read-only consumers. A module in an inner layer MUST NOT import from an outer layer. -### III. Agent Boundary - -The agent layer MAY read domain events and current state. The agent -MAY produce suggestions, annotations, or recommendations. The agent -MUST NOT mutate domain state directly. All agent-originated changes -MUST flow through the Application layer as explicit user-confirmed -commands. - -- Agent output MUST be clearly labeled as suggestions. -- No silent or automatic application of agent recommendations. - -### IV. Clarification-First +### III. Clarification-First Before making any non-trivial assumption during specification, -planning, or implementation, the agent MUST surface a clarification +planning, or implementation, Claude Code MUST surface a clarification question to the user. "Non-trivial" means any decision that would alter observable behavior, data model shape, or public API surface. -The agent MUST also ask when multiple valid interpretations exist, +Claude Code MUST also ask when multiple valid interpretations exist, when a choice would affect architectural layering, or when scope -would expand beyond the current spec. The agent MUST NOT silently +would expand beyond the current spec. Claude Code MUST NOT silently choose among valid alternatives. -### V. Escalation Gates +### IV. Escalation Gates Any feature, requirement, or scope change not present in the current spec MUST be rejected at implementation time until the spec is @@ -77,7 +67,7 @@ explicitly updated. The workflow is: 3. Update the spec via `/speckit.specify` or `/speckit.clarify`. 4. Only then proceed with implementation. -### VI. MVP Baseline Language +### V. MVP Baseline Language Constraints in this constitution and in specs MUST use MVP baseline language ("MVP baseline does not include X") rather than permanent @@ -86,7 +76,7 @@ add capabilities in future iterations without constitutional amendment. The current MVP baseline is local-first and single-user; this is a starting scope, not a permanent restriction. -### VII. No Gameplay Rules in Constitution +### VI. No Gameplay Rules in Constitution This constitution MUST NOT contain concrete gameplay mechanics, rule-system specifics, or encounter resolution logic. Such details @@ -96,9 +86,9 @@ architecture, and quality — not product behavior. ## Scope Constraints - The Encounter Console's primary focus is initiative tracking and - encounter state management. Adjacent capabilities (e.g., richer - game-engine features) are not in the MVP baseline but may be - added via spec updates in future iterations. + encounter state management. Adjacent capabilities (e.g., bestiary + integration, richer game-engine features) may be added via spec + updates. - Technology choices, UI framework, and storage mechanism are spec-level decisions, not constitutional mandates. - Testing strategy (unit, integration, contract) is determined per @@ -117,8 +107,14 @@ architecture, and quality — not product behavior. - Commits SHOULD be atomic and map to individual tasks where practical. - Layer boundary compliance MUST be verified by automated import - rules or architectural tests. Agent-assisted or manual review MAY - supplement but not replace automated checks. + rules or architectural tests. +- When a feature adds, removes, or changes user-facing capabilities + described in README.md, the README MUST be updated in the same + change. Features that materially alter what the product does or + how it is set up SHOULD also be reflected in the README. +- When a feature changes the tech stack, project structure, or + architectural patterns documented in CLAUDE.md, the CLAUDE.md + MUST be updated in the same change. ## Governance @@ -142,4 +138,4 @@ MUST comply with its principles. **Compliance review**: Every spec and plan MUST include a Constitution Check section validating adherence to all principles. -**Version**: 1.0.3 | **Ratified**: 2026-03-03 | **Last Amended**: 2026-03-03 +**Version**: 2.2.0 | **Ratified**: 2026-03-03 | **Last Amended**: 2026-03-10 diff --git a/CLAUDE.md b/CLAUDE.md index 98ff9ef..d0cfcf6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,7 +5,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Commands ```bash -pnpm check # Merge gate — must pass before every commit (knip + format + lint + typecheck + test) +pnpm check # Merge gate — must pass before every commit (knip + biome + typecheck + test + jscpd) pnpm knip # Unused code detection (Knip) pnpm test # Run all tests (Vitest) pnpm test:watch # Tests in watch mode @@ -27,11 +27,38 @@ apps/web (React 19 + Vite) → packages/application (use cases) → packages ``` - **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. +- **Application** — Orchestrates domain calls via port interfaces (`EncounterStore`, `BestiarySourceCache`). No business logic here. +- **Web** — React adapter. Implements ports using hooks/state. All UI components, routing, 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// Feature specs (spec.md, plan.md, tasks.md) +.specify/memory/ Project constitution +``` + +## 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.0 (formatting + linting), Knip (unused code), jscpd (copy-paste detection) +- Vitest (testing), Lefthook (pre-commit hooks) + ## Conventions - **Biome 2.0** for formatting and linting (no Prettier, no ESLint). Tab indentation, 80-char lines. Imports are auto-organized alphabetically. @@ -50,40 +77,3 @@ The constitution (`.specify/memory/constitution.md`) governs all feature work: 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 diff --git a/README.md b/README.md index 1d778e7..f1f2077 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,17 @@ -# Initiative Tracker +# Encounter Console -A turn-based initiative tracker for tabletop RPG encounters. Click "Next Turn" to cycle through combatants and advance rounds. +A local-first initiative tracker and encounter manager for tabletop RPGs (D&D 5e / 2024). Runs entirely in the browser — no server, no account, no data leaves your machine. + +## What it does + +- **Initiative tracking** — add combatants, roll initiative (manual or d20), cycle turns and rounds +- **Encounter state** — HP, AC, conditions, concentration tracking with visual status indicators +- **Bestiary integration** — import bestiary JSON sources, search creatures, and view full stat blocks +- **Persistent** — encounters survive page reloads via localStorage; bestiary data cached in IndexedDB ## Prerequisites -- Node.js 22 +- Node.js 22+ - pnpm 10.6+ ## Getting Started @@ -14,9 +21,7 @@ pnpm install pnpm --filter web dev ``` -Open the URL printed in your terminal (typically `http://localhost:5173`). - -The app starts with a 3-combatant demo encounter (Aria, Brak, Cael). Click **Next Turn** to advance through the initiative order. When the last combatant finishes their turn, the round number increments and the cycle restarts. +Open `http://localhost:5173`. ## Scripts @@ -24,5 +29,27 @@ The app starts with a 3-combatant demo encounter (Aria, Brak, Cael). Click **Nex |---------|-------------| | `pnpm --filter web dev` | Start the dev server | | `pnpm --filter web build` | Production build | -| `pnpm test` | Run all tests | -| `pnpm check` | Full merge gate (format, lint, typecheck, test) | +| `pnpm test` | Run all tests (Vitest) | +| `pnpm check` | Full merge gate (knip, biome, typecheck, test, jscpd) | + +## Project Structure + +``` +apps/web/ React 19 + Vite — UI components, hooks, adapters +packages/domain/ Pure functions — state transitions, types, validation +packages/app/ Use cases — orchestrates domain via port interfaces +data/bestiary/ Bestiary index for creature search +scripts/ Build tooling (layer boundary checks, index generation) +specs/ Feature specifications (spec → plan → tasks) +``` + +## Architecture + +Strict layered architecture with enforced dependency direction: + +``` +apps/web (adapters) → packages/application (use cases) → packages/domain (pure logic) +``` + +Domain is pure — no I/O, no randomness, no framework imports. Layer boundaries are enforced by automated import checks that run as part of the test suite. See [CLAUDE.md](./CLAUDE.md) for full conventions. +