Files
the-sunken-reach/CLAUDE_BUILD_PROMPT.md
T
2026-09-22 16:27:53 +02:00

198 lines
10 KiB
Markdown

# Build Prompt: D&D Campaign Manager Web App
Hand this whole folder to Claude Code (or paste this file as the initial prompt
with the rest of the folder alongside it) to build the app. Read this file
fully before writing any code, then read `prisma/schema.prisma`,
`docs/GAME_RULES_REFERENCE.md`, and `docs/ROLL_YOUR_CHARACTER.md` — those three
are the authoritative data model and game-logic spec, not just background.
## What we're building
A web app for a Dungeons & Dragons 5e Dungeon Master and their players to run a
campaign together. Users log in and see the campaigns/stories they're active in.
Opening a campaign shows a player their character card (stats, spells,
inventory). A DM can configure campaign basics, add characters, and build full
character sheets through a guided dice-roll flow. Players can also build and
manage their own characters, take notes, and pull up a contextual "quick stats"
panel mid-combat (attack/damage bonus, HP, special abilities). The DM can create
and assign weapons, create monsters/enemies, run encounters, and track damage
live.
## Tech stack (fixed — do not substitute)
- **Backend:** Node.js + Express (or Fastify if you strongly prefer, but default
to Express), JavaScript (not TypeScript, to match the stack the user works in
day to day), Prisma ORM, PostgreSQL.
- **Frontend:** React + Vite + TypeScript.
- **Real-time:** Socket.io over WebSockets, self-hosted on the Express server —
no paid real-time SaaS (no Pusher, Ably, Supabase Realtime, etc.).
- **Auth:** Email + password. Hash passwords with bcrypt. Issue a JWT on login,
store it client-side, verify it as Express middleware. A DM invites players to
a campaign via a short invite code or a shareable `/join/:inviteCode` link
(see `Campaign.inviteCode` in the schema).
- **Repo layout:** monorepo, two top-level folders:
```
/frontend — React + Vite + TypeScript app
/backend — Express + Prisma API, Socket.io server
/prisma — (already provided) schema.prisma — move into /backend/prisma
/seed-data — (already provided) JSON reference data — move into /backend/prisma or /backend/src/data, wire into a seed script
/docs — (already provided) keep as top-level /docs for reference during and after the build
```
Move `prisma/` and `seed-data/` into `/backend` as part of scaffolding (or keep
top-level and point `backend/prisma/schema.prisma`'s location via
`package.json`'s `prisma.schema` field — either is fine, just be consistent).
## First command to run
```
cd backend && npx prisma validate
```
This schema was authored and structurally reviewed (brace/paren balance, no
duplicate models/enums, relation names cross-checked by hand) but could not be
run through `prisma validate` in the environment that generated it — its
sandbox blocked outbound access to Prisma's engine-binary CDN. Run it for real
as your first step before trusting the schema further.
## Database & seeding
1. `prisma migrate dev` from the schema as provided (adjust only if `prisma
validate` surfaces a real error).
2. Write `backend/prisma/seed.js` (or `.ts`) that reads the four JSON files in
`seed-data/` and:
- Does **not** need to load species/classes/skills into the database as rows
— those are fixed game-rule tables, not user data. Instead, port them into
a small constants module the calculation engine imports directly (e.g.
`backend/src/game/data/species.js` etc., generated from the JSON so the
numbers stay traceable to `docs/GAME_RULES_REFERENCE.md`).
- **Does** seed `weapons.json` into the `Weapon` table, but only as part of
a demo/dev campaign seed (weapons are per-campaign in the schema, since a
DM should be able to add homebrew weapons) — don't seed them globally.
3. Build the calculation engine described in `docs/GAME_RULES_REFERENCE.md` as
a single module, fully unit tested (ability modifier, proficiency bonus, HP
by level — including the level-1-uses-max-die rule, AC, save/skill bonuses,
spell DC/attack bonus). Every place in the API that returns a character's
derived stats must call this module — never duplicate the formulas.
## Backend API — suggested route groups
- `POST /auth/register`, `POST /auth/login`, `GET /auth/me`
- `GET /campaigns` (mine, as DM or player), `POST /campaigns` (create, DM
becomes owner), `GET /campaigns/:id`, `PATCH /campaigns/:id` (DM-only,
updates `startingItemCount`/`startingSpellCount`/`startingGold`/etc.)
- `POST /campaigns/:id/invite` (DM regenerates/fetches invite code),
`POST /campaigns/join/:inviteCode` (player joins)
- `GET /campaigns/:id/characters`, `POST /campaigns/:id/characters` (start a
build — DM for anyone, player for themselves)
- `GET /characters/:id`, `PATCH /characters/:id` (stats/notes updates),
`DELETE /characters/:id` (DM only)
- `POST /characters/:id/build/roll` (server-rolls the requested die for the
current build step — never trust a client-submitted roll — see
`docs/ROLL_YOUR_CHARACTER.md`), `POST /characters/:id/build/choose` (submit a
choice for the current step), `POST /characters/:id/build/finalize`
- `GET /characters/:id/quick-stats` (assembles the combat-context panel per
`docs/GAME_RULES_REFERENCE.md`)
- `POST /characters/:id/weapons`, `PATCH /characters/:id/weapons/:weaponId`
(equip/unequip/quantity), weapon CRUD under `/campaigns/:id/weapons` (DM)
- `POST /characters/:id/inventory`, `PATCH
/characters/:id/inventory/:itemId`
- Monster CRUD under `/campaigns/:id/monsters` (DM only)
- `POST /campaigns/:id/encounters`, `GET /encounters/:id`, `POST
/encounters/:id/participants` (add character/monster), `PATCH
/encounters/:id/participants/:participantId` (HP/conditions/initiative
updates — this is the hot path during combat), `POST
/encounters/:id/advance-round`
- `POST /notes`, `GET /campaigns/:id/notes`, `GET /characters/:id/notes`
Authorization rule of thumb: DM (campaign owner / `CampaignMember.role ===
'DM'`) can read/write everything in their campaign; a player can read their own
character fully, can read other players' character cards at whatever visibility
the DM allows (default: full read for party members — keep it simple for v1),
and can only write to their own character and their own notes.
## Real-time events (Socket.io)
Join a Socket.io room per campaign (`campaign:<id>`) on connect. Emit on:
- Character HP/condition changes (`character:updated`)
- Encounter participant HP/condition/initiative changes
(`encounter:participant-updated`)
- Encounter round advances (`encounter:round-advanced`)
- New character added / build finalized (`character:created`)
This is what makes HP changes and combat tracking show up live for everyone
looking at the same campaign/encounter — keep the payloads small (just the
changed record) and let the frontend re-fetch details if needed rather than
pushing full derived-stat blobs over the socket.
## Frontend — key screens
1. **Login / Register**
2. **Campaign list** (post-login home) — campaigns the user is a DM or player
in; a "Join campaign" flow that takes an invite code/link
3. **Campaign home** — DM sees campaign settings + character list + "start
encounter"; player sees their character card front and center
4. **Character card / sheet** — stats, AC, HP (editable inline for
authorized users), skills, saves, spells, inventory, weapons, notes
5. **Character builder** — walks `docs/ROLL_YOUR_CHARACTER.md` step by step:
big "Roll d_N_" buttons, reveal choices, assign ability scores, pick
skills, pick starting equipment, finalize
6. **Quick Stats panel** — a compact overlay/drawer callable from anywhere the
character is on screen, showing attack/damage bonus (per equipped
weapon), HP, AC, and special abilities — this is what a player pulls up
mid-attack, so it must be fast to open (no full page nav) and legible at a
glance
7. **DM encounter/combat tracker** — add characters and monsters to an
encounter, initiative order, per-combatant HP/condition tracking, round
counter, all live via Socket.io
Use React Query (or an equivalent fetch/cache layer) for API state and a
lightweight Socket.io client hook that invalidates/updates the relevant query
cache on each event above, rather than hand-rolling a parallel state store.
## Build phases (do these in order; each should be independently runnable/testable)
1. Monorepo scaffold: `/frontend` (Vite React-TS template) + `/backend`
(Express, `npm init`, Prisma installed, schema in place), root README with
dev-run instructions, `.env.example` in both.
2. Prisma migrate + seed script + the calculation engine module + its unit
tests (this is the foundation everything else depends on — get it fully
correct and tested before building UI on top of it).
3. Auth (register/login/JWT middleware) + campaign CRUD + invite/join flow.
4. Character builder end-to-end (dice-roll flow → finalized character),
backed by the calculation engine.
5. Character sheet view (read) + stat/note editing (write) on the frontend.
6. Quick Stats panel.
7. Weapons (campaign-level CRUD + assign to character) and inventory.
8. Monsters + encounters + combat/damage tracking.
9. Socket.io wiring for all the real-time events above.
10. Polish pass: loading/error states, mobile-usable layout (a DM running combat
from a phone or tablet at the table is a realistic use case), basic
automated tests for the calculation engine and the auth/campaign/character
API routes.
## Non-functional requirements
- Validate all request bodies server-side (e.g. `zod`) — don't trust the client
for anything that affects game state, especially dice rolls.
- Never let a non-DM, non-owning-player mutate a character or campaign they
don't have rights to — enforce this in middleware, not just by hiding UI.
- Keep the calculation engine pure and dependency-free (no DB calls inside it)
so it stays unit-testable in isolation.
- Match the naming and structure in `prisma/schema.prisma` and the JSON files
in `seed-data/` exactly — they're the result of a full review pass and are
meant to be used as-is, not redesigned.
## Reference material in this folder
- `prisma/schema.prisma` — full data model, described above
- `docs/GAME_RULES_REFERENCE.md` — every D&D formula the app needs, verified
against a hand-checked Excel calculator built earlier for this same campaign
- `docs/ROLL_YOUR_CHARACTER.md` — the dice-roll character build flow, step by
step, for the builder UI
- `seed-data/species.json`, `classes.json`, `skills.json`, `weapons.json` —
structured versions of the tables in `GAME_RULES_REFERENCE.md`, ready to
import into code or a seed script