# CLAUDE.md - Codebase Documentation for Claude Code

This file helps Claude understand the Deathwatch Roller codebase and provide
better assistance. **Prefer the live code and `package.json` over older docs
when they disagree.** For a deep, feature-by-feature guide, see `AGENTS.md` —
it is the most detailed and up-to-date reference in the repo.

## Project Overview

**Deathwatch Roller** is a Create React App + Express application for running
Deathwatch tabletop RPG sessions.

**Main Features:**
- Player login, character sheets, and player management (GM)
- Dice roller (skill checks and combat) with fate-point support
- Mission play (GM/player views) and simulation lab
- Requisition shop (buy gear with RP), gated by renown
- Bestiary, rules search, and weapon references
- GM Kit reference tables and user-submitted error reports

**Tech Stack:**
- Frontend: React 18 (plain JavaScript/JSX, **not** TypeScript) via Create React App
- Backend: Node.js + Express (`database/server.js`)
- Database: MariaDB via `mysql2/promise` (some legacy SQLite artifacts remain)
- Styling: Tailwind CSS + component CSS

## Architecture

The repo is a **single-root JavaScript app** — there is no `backend/` or
`frontend/` directory (older docs that mention them are stale).

```
dwroller/
├── src/             # React frontend (App.js, components/, utils/, tests/)
├── database/        # Express backend (server.js, routes/, mariadb.js, ...)
├── scripts/         # Import, cleanup, validation, generation, CI helpers
├── public/          # Static data/assets + printable pages (cards.html, print.html)
├── data/            # Static data and assets
└── docs/            # Documentation (some structure docs are stale)
```

## Key Files and Locations

### Frontend (`src/`)
- `src/App.js` - main app, login/session state, tab wiring (`tab` state),
  `logUserAction()`
- `src/components/` - feature UI components
- `src/components/DeathwatchRoller.jsx` - combat dice roller (weapon mapping,
  attack/damage, hit locations, history, presets, tracker)
- `src/components/SkillRoller.jsx` - sheet-driven skill checks, manual d100,
  target overrides
- `src/components/rollHelpers.js` - shared d100, DoS/DoF, ids, fate helpers
  (`applyFateToTest()`)
- `src/components/FateControls.jsx`, `src/components/useFate.js` - fate UI and
  persistence (`POST /api/players/:name/spend-fate`)
- `src/components/MissionTab.jsx` - active mission play (GM/player views)
- `src/components/SimulationTab.jsx` - simulation report UI and controls
- `src/components/MissionSimTab.jsx` - older/generated mission simulator;
  check routing before extending
- `src/utils/diceRoller.js` - mission-safe dice helpers, `MISSION_ROLL_CONTEXT_KEY`
- `src/tests/` - Jest unit tests

### Backend (`database/`)
- `database/server.js` - Express entrypoint and route mounting
- `database/routes/` - API routes (players, sessions, shop, rules, bestiary,
  weapons, missions, simulations, staging, errors)
- `database/routes/missionRoutes.js` - mission CRUD, active mission, `playerScene()`
  safe projection, active-scene patching, shared roll feed
- `database/routes/simulationRoutes.js` - backend simulation engine and report CRUD
- `database/mariadb.js` - MariaDB pool, schema creation/patching
- `database/sessionModel.js` - session storage helpers
- `database/shop-helpers.js` - requisition/shop business logic

## Development Workflow

Run commands from the repository root unless noted.

```bash
npm install
npm start            # CRA dev server on http://localhost:3000
npm run server:notest  # backend only (cd database && node server.js) on :5000
npm run test:unit    # frontend Jest unit tests
```

### Scripts (see `package.json` for the full list)
- `npm start` - frontend dev server (CRA proxies /api to :5000)
- `npm run server` - runs unit tests first, then `database/server.js`
- `npm run server:notest` - backend without running tests first
- `npm run build` - runs unit tests, then `react-scripts build`
- `npm run build:fast` - build then `pm2:reload` (skips tests)
- `npm run test:integration` - starts PM2, runs integration Jest, stops PM2
- `npm run pm2:start` / `pm2:reload` / `pm2:stop` - PM2 process control
- `./scripts/local-ci.sh` - local CI helper

Ports: frontend `3000`, backend API `5000` (CRA proxy points at `5000`).

## App Tabs

Tabs are selected in `src/App.js` via the `tab` state (see `AGENTS.md` for the
full per-tab breakdown of components, endpoints, and behavior):

| Tab key | Component | Availability |
|---------|-----------|--------------|
| `mission` (default) | `MissionTab.jsx` | All; GM/player views differ |
| `roller` | `DeathwatchRoller.jsx` | All |
| `shop` | `RequisitionShop.jsx` | All; GM controls for `gm` |
| (fallback) | `PlayerTab.jsx` (Character Sheet) | All; GM edit tools for `gm` |
| `rules` | `RulesTab.jsx` | All |
| `weapons` | `WeaponsTab.jsx` | All |
| `errors` | `ErrorReports.jsx` | All; GM sees/resolves all |
| `bestiary` | `BestiaryTab.jsx` | GM-only |
| `players` | `PlayerManagement.jsx` | GM-only |
| `gmkit` | `GMKit.jsx` | GM-only |
| `simulation` | `SimulationTab.jsx` | GM-only |

External utility pages `public/cards.html` (nav `Kort`) and `public/print.html`
(nav `Print`) are static printable pages, not React tabs.

## API Surface

Mounted prefixes in `database/server.js`:

| Prefix | Description |
|--------|-------------|
| `/api/players` | Players, GM player ops, fate, avatars |
| `/api/sessions` | Session management |
| `/api/shop` | Requisition shop and purchases |
| `/api/rules` | Rules categories/search/random (`/api/rules/staging` for staging) |
| `/api/weapons` | Weapon references |
| `/api/bestiary` | Bestiary full/reload/enemies |
| `/api/missions` | Mission CRUD, active mission, safe scene, roll feed |
| `/api/simulations` | Run and manage simulation reports |
| `/api/errors` | User-submitted error reports |
| `/api/gmkit/list`, `/api/gmkit/upload` | GM kit assets |
| `/api/copy-bestiary` | Bestiary copy utility |
| `/api/narrate` | Ollama narration (`OLLAMA_BASE`, `NARRATOR_MODEL`) |

Static serving includes `/gmkit`, `/weapon-images`, and `/avatars`.

### Authentication
- Sessions use an `x-session-id` header; backend uses `requireSession`
- GM endpoints check `x-gm-secret` / `GM_SECRET` (falls back to `defaultsecret`)
- Some GM upload auth falls back to defaults — see route files

## Environment And Runtime Notes

- Backend loads env via `dotenv` from its working directory. `server:notest`
  runs from `database/`, so `database/.env` is the relevant file.
- MariaDB defaults are hard-coded in `database/mariadb.js`: host `192.168.1.113`,
  user `deathwatch`, database `deathwatch`, port `3307`, password from
  `DB_PASSWORD` or `defaultpassword`.
- Backend may load rules/weapons/bestiary from MariaDB first, then fall back to
  JSON files under `public/` and `database/`.
- The backend can serve `build/` statically when a production build exists.

## Data Sources

- Rules index: `database/rules/rules-database.json`
- Skills: `database/deathwatch_skills_p94_107.csv`
- Armoury/shop: `public/deathwatch-armoury.json`
- Bestiary: `public/deathwatch-bestiary-extracted.json` (+ `database/` copy)

## Code Style & Conventions

- Plain JavaScript/JSX — match surrounding style; do not introduce TypeScript
  unless a migration is explicitly requested.
- Prefer existing React hook/component patterns in `src/components/`.
- Frontend API calls use relative `/api/...` URLs through the CRA proxy.
- Keep `localStorage` keys stable. Known keys include: `dw:shop:authedPlayer`,
  `dw:shop:sessionId`, `dw:shop:playerData`, `dw:shop:players:v1`,
  `dw:presets:v3`, `dw:history:v2`, `dw:weapons:v3`, `dw:tracker:v1`,
  `dw:mission:rollContext`, `dw:enemies:v1`, `dw:rules:recent`,
  `dw:warning-dismiss-until:v1`.
- Keep DB writes parameterized through `mysql2` APIs.
- Preserve backend response shapes and `tabInfo` shape used by components/tests.
- Keep d100/DoS behavior aligned across mission, skill, and combat rolls.

## Testing

- `npm run test:unit` for normal frontend changes; target a file with
  `npm test -- src/tests/login.test.js`.
- `npm run test:integration` only when backend/API behavior changes and
  PM2/MariaDB are available.
- If tests fail because MariaDB, PM2, Ollama, or local services are
  unavailable, report that explicitly rather than masking it.

## Files To Treat Carefully

- Do not commit or churn runtime logs (`database/backend.log`, `database/server.log`).
- Do not overwrite database files, backups, generated JSON, PDFs, OCR output,
  or imported rulebook data unless the user specifically asks for data work.
- Inspect `git status --short` before editing; avoid reverting unrelated local work.
- Do not paste long rulebook passages into UI/docs — summarize in original
  wording and link/search the local rules DB.

## Documentation

Full docs are in `docs/` (note: some structure docs are stale and still refer
to non-existent `backend/`/`frontend/` dirs). See also `README.md`,
`DEVELOPMENT_GUIDE.md`, and especially `AGENTS.md` for the detailed guide.
