diff --git a/CLAUDE.md b/CLAUDE.md index 9acfc4a..c899b66 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +**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 @@ -66,16 +66,15 @@ docs/agents/ RPI skill artifacts (research reports, plans) ## 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 (unnecessary type assertions, deprecated APIs, `replaceAll` preference, `String.raw`). Configured in `.oxlintrc.json`. -- **TypeScript strict mode** with `verbatimModuleSyntax`. Use `.js` extensions in relative imports when required by the repo's ESM settings (e.g., `./types.js`). +- **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 and invariants from specs to individual `it()` blocks. -- **Feature specs** live in `specs/NNN-feature-name/` with spec.md (and optionally plan.md, tasks.md for new work). Specs describe features, not individual changes. The project constitution is at `.specify/memory/constitution.md`. -- **Component props** — max 8 explicitly declared props per component interface (enforced by `scripts/check-component-props.mjs` using the TypeScript compiler API). Use React context for shared state; reserve props for per-instance config (data items, layout variants, refs). -- **Export format compatibility** — When changing `Encounter`, `Combatant`, `PlayerCharacter`, or `UndoRedoState` types, verify that previously exported JSON files (version 1) still import correctly. If not, bump the `ExportBundle` version and add migration logic in `validateImportBundle()`. +- **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: @@ -86,21 +85,7 @@ Before finishing a change, consider: ## Speckit Workflow -Speckit (`/speckit.*` skills) manages the spec-driven development pipeline. Specs are **living documents** that describe features, not individual changes. - -### Issue-driven workflow -- `/write-issue` — create a well-structured Gitea issue via interactive interview -- `/integrate-issue ` — fetch an issue, route it to the right spec, and update the spec with the new/changed requirements. Then implement directly. -- `/sync-issue ` — push acceptance criteria from the spec back to the Gitea issue - -### RPI skills (Research → Plan → Implement) -- `rpi-research` — deep codebase research producing a written report in `docs/agents/research/` -- `rpi-plan` — interactive phased implementation plan in `docs/agents/plans/` -- `rpi-implement` — execute a plan file phase by phase with automated + manual verification - -**Research scope**: Research should include a scan for existing patterns similar to what the feature needs (e.g., shared UI primitives, duplicated validation logic, repeated state management patterns). Identify extraction and consolidation opportunities before implementation, not during. - -### Choosing the right workflow by scope +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 | |---|---| @@ -109,24 +94,8 @@ Speckit (`/speckit.*` skills) manages the spec-driven development pipeline. Spec | 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` | -Speckit manages **what** to build (specs as living documents). RPI manages **how** to build it (research, planning, execution). The full speckit pipeline is for new features. For changes to existing features, update the spec via `/integrate-issue`, then use RPI skills if the change is non-trivial. +**Research scope**: Always scan for existing patterns similar to what the feature needs. Identify extraction and consolidation opportunities before implementation, not during. -### Current feature specs -- `specs/001-combatant-management/` — CRUD, persistence, clear, batch add, confirm buttons -- `specs/002-turn-tracking/` — rounds, turn order, advance/retreat, top bar -- `specs/003-combatant-state/` — HP, AC, conditions, concentration, initiative -- `specs/004-bestiary/` — search index, stat blocks, source management, panel UX -- `specs/005-player-characters/` — persistent player character templates (CRUD), search & add to encounters, color/icon visual distinction, `PlayerCharacterStore` port -- `specs/006-undo-redo/` — undo/redo for encounter state mutations -- `specs/007-json-import-export/` — JSON import/export for full encounter state (encounter, undo/redo, player characters) -- `specs/008-encounter-difficulty/` — Live encounter difficulty indicator (5.5e XP budget system), optional PC level field +## Constitution -## 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. **Spec-driven features** — Features are described in living specs; evolve existing specs via `/integrate-issue`, create new ones via `/speckit.specify`. Bug fixes and tooling changes do not require specs. +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. diff --git a/docs/conventions.md b/docs/conventions.md new file mode 100644 index 0000000..de4e001 --- /dev/null +++ b/docs/conventions.md @@ -0,0 +1,20 @@ +# Conventions (detailed) + +These conventions supplement the overview in `CLAUDE.md`. Load this file when working in the relevant areas. + +## Component Props + +Max 8 explicitly declared props per component interface, enforced by `scripts/check-component-props.mjs` (uses the TypeScript compiler API). Run `pnpm check:props` to verify. + +- Use React context for shared state +- Reserve props for per-instance config (data items, layout variants, refs) + +## Export Format Compatibility + +When changing `Encounter`, `Combatant`, `PlayerCharacter`, or `UndoRedoState` types, verify that previously exported JSON files (version 1) still import correctly. If not, bump the `ExportBundle` version and add migration logic in `validateImportBundle()`. + +## Domain Patterns + +- **Branded types** for identity values (e.g., `CombatantId`). Prefer immutability/`readonly` where practical. See [ADR-003](adr/003-branded-types-for-identity.md). +- **Domain events** are plain data objects with a `type` discriminant — no classes. See [ADR-002](adr/002-domain-events-as-plain-data.md). +- **Errors as values** (`DomainError`), never thrown. See [ADR-001](adr/001-errors-as-values.md).