Files
dwroller/AGENTS.md

30 KiB

AGENTS.md - Codex Guide for Deathwatch Roller

This file gives Codex repo-specific context for working on Deathwatch Roller. Prefer the live code and package.json over older docs when they disagree.

Project Snapshot

Deathwatch Roller is a Create React App + Express application for running Deathwatch tabletop RPG sessions. It includes player login and character data, dice rolling, missions, simulations, a requisition shop, bestiary, rules search, weapon references, GM tools, and user-submitted error reports.

The repository is currently a single-root JavaScript app:

  • Frontend: React 18 JSX under src/
  • Backend: Express server under database/
  • Database: MariaDB via mysql2/promise, with some legacy SQLite artifacts
  • Static data: JSON and generated assets under public/, data/, database/, and database/rules/
  • Tests: Jest + React Testing Library for frontend unit tests; separate integration config for root-level integration tests

High-Value Paths

  • src/App.js - main React app, login/session state, tab wiring
  • src/components/ - feature UI components
  • src/components/DeathwatchRoller.jsx - combat dice roller, weapon mapping, attack rolls, damage, hit locations, history, presets, and tracker state
  • src/components/SkillRoller.jsx - sheet-driven skill checks, manual d100 entry, target overrides, and fate-point roll modification
  • src/components/rollHelpers.js - shared frontend d100, degrees of success/failure, ids, and fate application helpers used by roller components
  • src/utils/diceRoller.js - mission-safe dice helpers and MISSION_ROLL_CONTEXT_KEY
  • src/components/MissionTab.jsx - active mission play surface, scene reveal state, check rolling, combat state, initiative, mission roll feed, and GM progress controls
  • src/components/SimulationTab.jsx - simulation report UI, run controls, difficulty/profile selection, roll feed display, and saved reports
  • src/components/MissionSimTab.jsx - older/generated mission simulator UI; check current routing before extending it
  • src/utils/ - shared frontend utilities such as dice and XP logic
  • src/tests/ - Jest unit tests run by the default test config
  • database/server.js - Express backend entrypoint and route mounting
  • database/routes/ - API routes for players, sessions, shop, rules, bestiary, weapons, missions, simulations, staging, and error reports
  • database/routes/missionRoutes.js - mission CRUD, active mission selection, player-safe scene projection, active-scene patching, and shared roll feed APIs
  • database/routes/simulationRoutes.js - backend simulation engine and simulation CRUD/report APIs
  • database/mariadb.js - MariaDB pool, schema creation, schema patching
  • database/sessionModel.js - session storage helpers
  • database/shop-helpers.js - requisition/shop business logic
  • scripts/ - import, cleanup, validation, generation, and local CI helpers
  • docs/ - user/developer docs; some structure docs are stale and still refer to non-existent backend/ and frontend/ directories

Commands

Run commands from the repository root unless noted.

npm install
npm start
npm run server:notest
npm run test:unit
npm test
npm run build

Useful variants:

npm run server        # runs tests first, then database/server.js
npm run test:integration
npm run build:fast    # skips tests, then reloads PM2
./scripts/local-ci.sh

Local ports:

  • Frontend dev server: http://localhost:3000
  • Backend API: http://localhost:5000
  • CRA proxy points frontend API requests at http://localhost:5000

Environment And Runtime Notes

  • Backend loads environment variables with dotenv from the backend process working directory. npm run server:notest runs from database/, so database/.env is relevant.
  • MariaDB connection 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.
  • GM upload auth checks GM_SECRET and falls back to defaultsecret.
  • /api/narrate calls Ollama using OLLAMA_BASE and NARRATOR_MODEL.
  • The backend can serve build/ statically when a production build exists.

Testing Guidance

  • Use npm run test:unit for normal frontend changes.

  • Use targeted Jest commands when iterating, for example:

    npm test -- src/tests/login.test.js
    
  • Use npm run test:integration only when backend/API behavior is touched and PM2/MariaDB are available.

  • npm run build runs unit tests before react-scripts build.

  • If tests fail because MariaDB, PM2, Ollama, or local services are unavailable, report that explicitly rather than masking the failure.

Implementation Conventions

  • The codebase is plain JavaScript/JSX, not TypeScript. Match the surrounding style unless a broader migration is explicitly requested.
  • Prefer existing React hooks/component patterns in src/components/.
  • Keep state persistence keys stable; the app uses localStorage keys such as dw:shop:authedPlayer, dw:shop:sessionId, and dw:shop:playerData.
  • API calls from the frontend generally use relative /api/... URLs through the CRA proxy.
  • Preserve backend response shapes used by existing components and tests.
  • Keep database writes parameterized through mysql2 APIs.
  • Avoid broad refactors in data import scripts unless the task is specifically about those scripts.

Dice Roller, Mission, And Simulation Notes

Dice Roller

  • The main dice roller lives in src/components/DeathwatchRoller.jsx.
  • Shared roller primitives live in two places:
    • src/components/rollHelpers.js for UI roller and skill roller behavior.
    • src/utils/diceRoller.js for mission-safe helpers and the mission roll context storage key.
  • Core roll semantics:
    • d100() returns 1-100.
    • degrees(target, roll) treats roll <= target as success and reports Degree of Success or Degree of Failure in 10-point bands.
    • Skill and combat roll targets should stay clamped to sensible d100 bounds.
  • Combat helpers in DeathwatchRoller.jsx cover dice expressions, weapon normalization, RoF modes, hit counts from DoS, hit location from reversed d100, tearing/proven damage dice, and damage mitigation.
  • Skill rolling is delegated to SkillRoller.jsx, which reads the logged-in player's sheet skills/characteristics, applies training modifiers, supports a manual d100 roll, and can override the target number.
  • Fate handling is shared by FateControls.jsx, useFate.js, and applyFateToTest() in rollHelpers.js. Spending fate persists through POST /api/players/:name/spend-fate.
  • Persistent browser state keys used by the roller include:
    • dw:presets:v3
    • dw:history:v2
    • dw:weapons:v3
    • dw:tracker:v1
    • dw:mission:rollContext

Mission Play

  • MissionTab.jsx is the current mission play implementation used from App.js.
  • GM users are identified in the UI with authedPlayer === 'gm'.
  • GM flow:
    • Load all missions with GET /api/missions.
    • Load the current active mission with GET /api/missions/active/current.
    • Create/update/delete missions through POST/PUT/DELETE /api/missions.
    • Set the active mission with POST /api/missions/:id/active.
    • Save progress and scene edits with PUT /api/missions/:id/progress.
  • Player flow:
    • Load only the player-safe active scene with GET /api/missions/active/player.
    • Hidden scene fields are stripped in playerScene() in database/routes/missionRoutes.js.
    • Player mini rollers patch only the active scene through PUT /api/missions/active/scene so they do not overwrite the full mission.
  • The shared mission roll feed is backed by the mission_rolls table:
    • Read with GET /api/missions/active/rolls/feed?limit=50.
    • Create with POST /api/missions/rolls.
    • Delete with DELETE /api/missions/rolls/:id.
  • Mission scenes may contain checks, objectives, complications, extra challenges, combat state, reveal state, play prompts, enemies, rewards, storyHook, secret, GM notes, and ending text. Preserve unknown scene fields when patching scenes.
  • Generated missions should give every scene a GM-only storyHook that acts as a thread toward the mission's main story. Skill check rewards are the visible triggers that reveal the hook; failures should use fail-forward consequences instead of blocking the clue.
  • Combat state is scene-local and includes round, initiatives, conditions, and fear rating. Initiative rolls use 1d10 + Ag bonus; fear/check rolls use the same d100 degree helpers as the roller.
  • When changing mission roll behavior, verify both GM and player perspectives: the GM sees the full mission, while players only receive the active safe scene.

Simulation

  • SimulationTab.jsx is the current simulation report and control UI.
  • database/routes/simulationRoutes.js contains the backend simulation engine. It loads mission/player data, builds temporary player combat state from tabInfo, runs scenes, and saves reports through simulationHelpers.
  • Simulation endpoints:
    • POST /api/simulations runs and saves a new simulation.
    • GET /api/simulations?limit=50 lists saved simulation summaries.
    • GET /api/simulations/:id returns a full saved report.
    • DELETE /api/simulations/:id removes a saved report.
  • Simulation request inputs include mission_id, player_names, difficulty, per-player combat_profiles, and enemy_profile.
  • Difficulty levels are 1-5 and affect wounds, enemy BS, check modifiers, fear, extra enemies, recovery, and max combat rounds.
  • Combat profiles include auto, balanced, ranged, and melee; they affect weapon choice, grenade use, and enemy attack style.
  • The simulation engine normalizes weapons from player tabInfo.weapons, infers ammo/clip defaults for common Deathwatch weapons, tracks ammo spend, resolves single/semi/full-auto hit counts, rolls initiative each combat round, applies conditions, performs fear tests, spends fate once when useful, and builds roll feeds/player report cards.
  • Saved simulation reports include result, earned XP, total rounds, total rolls, overall success rate, combat success rate, puzzle/check success rate, scene results, player cards, roll feed, story hooks, findings, difficulty_level, and enemy_profile.
  • Because simulations use Math.random() directly and are not seeded, tests and docs should avoid assuming deterministic exact roll output.

App Tabs

Tabs are selected in src/App.js with the tab state. Login/session state is owned by App.js and passed to tabs as authedPlayer and sessionId where needed. Navigation actions are logged with logUserAction().

Mission

  • Component: src/components/MissionTab.jsx
  • Default tab: mission
  • Availability: all logged-in states, but GM and player views differ.
  • Backend: /api/missions, /api/missions/active/current, /api/missions/active/player, /api/missions/active/scene, and mission roll feed endpoints.
  • Notes: GM sees full mission state; players receive only the active safe scene. Preserve unknown scene fields and avoid replacing the full scene array from player-facing actions.

Dice Roller

  • Component: src/components/DeathwatchRoller.jsx
  • Tab key: roller
  • Availability: visible to all users.
  • Backend: player sheet/fate endpoints, bestiary enemy data where loaded, and mission roll context when launched from mission play.
  • Notes: Depends on SkillRoller.jsx, FateControls.jsx, useFate.js, rollHelpers.js, and src/utils/diceRoller.js. Keep d100/DoS behavior aligned across mission, skill, and combat rolls.

Requisition Shop

  • Component: src/components/RequisitionShop.jsx
  • Tab key: shop
  • Availability: visible to all users; GM controls appear for authedPlayer === 'gm'.
  • Backend: GET /api/shop, POST /api/shop/purchase, GET /api/players/:name, GET /api/players, POST /api/players/gm/set-rp, and POST /api/players/gm/set-renown.
  • Data source: public/deathwatch-armoury.json through shopRoutes.js.
  • Notes: Purchases deduct tabInfo.rp and append/update tabInfo.gear. Renown gating uses the local rank order in the component.

Character Sheet

  • Component: src/components/PlayerTab.jsx
  • Tab key: fallback branch when no other tab matches; nav label is Character Sheet.
  • Availability: visible to all users; GM edit tools are enabled only when the logged-in user is gm.
  • Backend: GET /api/players, GET /api/players/:name, PUT /api/players/:name, avatar upload under POST /api/players/:name/avatar if present, and GM player endpoints.
  • Local state: caches player data under dw:shop:players:v1 and also consumes dw:shop:playerData as a fallback.
  • Notes: Character data is stored primarily in tabInfo and includes characteristics, skills, weapons, armour, gear, wounds, fate, XP, XP spent, renown, movement, insanity, corruption, notes, and avatar/picture data. Preserve tabInfo shape because the roller and simulator read it directly.

Rules

  • Component: src/components/RulesTab.jsx
  • Tab key: rules
  • Availability: visible to all users.
  • Backend: GET /api/rules/categories, GET /api/rules/search, GET /api/rules/rule/:id, and GET /api/rules/random.
  • Local state: recent searches are stored as dw:rules:recent.
  • Notes: Results include structured fields such as summary, examples, category, source, page, aliases, tags, and midgame priority. Keep highlighting and compact/full rendering behavior when changing rule result shapes.

Weapons

  • Component: src/components/WeaponsTab.jsx
  • Tab key: weapons
  • Availability: visible to all users.
  • Backend: GET /api/weapons.
  • Data source: MariaDB weapons when available, with fallback to public/deathwatch-armoury.json.
  • Notes: The tab normalizes displayed categories into ranged, melee, grenade, armour, and other. Ranged stat strings may be parsed from semicolon-separated fields in stats.damage.

Error Reports

  • Component: src/components/ErrorReports.jsx
  • Tab key: errors
  • Availability: visible to all logged-in users; GM sees all reports and can resolve/delete.
  • Backend: GET /api/errors, POST /api/errors, PUT /api/errors/:id/resolve, PUT /api/errors/:id/status, and DELETE /api/errors/:id.
  • Auth: requires x-session-id; backend uses requireSession.
  • Notes: Non-GM users should only see their own reports. Form submissions default pageUrl to the current browser path when not provided.

Bestiary

  • Component: src/components/BestiaryTab.jsx
  • Tab key: bestiary
  • Availability: GM-only in App.js; non-GM users see an access denied panel.
  • Backend: GET /api/bestiary/full, POST /api/bestiary/reload, and GET /api/bestiary/enemies for dice roller enemy format.
  • Local state: caches entries under dw:enemies:v1; DB-down warning dismissal uses dw:warning-dismiss-until:v1.
  • Data source: MariaDB bestiary rows with fallback to public/deathwatch-bestiary-extracted.json.
  • Notes: normalizeEntry() handles multiple imported statblock shapes. Keep fallback/cache behavior intact so the tab remains useful when the DB is down.

Player Management

  • Component: src/components/PlayerManagement.jsx
  • Tab key: players
  • Availability: GM-only nav item. The component also expects GM context.
  • Backend: GET /api/players, POST /api/players/gm/add-or-update, POST /api/players/gm/set-xp, POST /api/players/gm/set-xp-spent, POST /api/players/gm/set-rp, POST /api/players/gm/set-renown, POST /api/players/gm/reset-password, and DELETE /api/players/gm/delete/:name.
  • Auth: sends x-session-id and x-gm-secret where available; backend GM secret defaults are defined in playerRoutes.js.
  • Notes: Bulk XP/RP operations update each player in sequence. The component expects player economic and progression values in tabInfo.

GM Kit

  • Component: src/components/GMKit.jsx
  • Tab key: gmkit
  • Availability: GM-only nav item and component-level access check.
  • Backend: none for the current table UI; App.js separately uses GET /api/gmkit/list to choose the players-tab background.
  • Notes: Current GM Kit is static reference tables inside the component: difficulty modifiers, hit locations, combat actions, weapons, armour, critical hits, weapon qualities, cover, renown, and related GM references. There are older GMKit_old.jsx and GMKit_new.jsx files; check routing before editing them.

Simulation Lab

  • Component: src/components/SimulationTab.jsx
  • Tab key: simulation
  • Availability: GM-only nav item in App.js.
  • Backend: /api/simulations endpoints and mission/player data loaded by the backend simulation route.
  • Notes: Reports include roll feeds, scene logs, player report cards, story hooks, findings, difficulty, and enemy profile. Simulation output is non-deterministic.

External Utility Pages

  • public/cards.html is linked from nav as Kort and opens in a new tab. It is a printable card-sheet view and fetches app/static data directly from the served public site.
  • public/print.html is linked from nav as Print and opens in a new tab. It is a printable quick-reference sheet.
  • These are not React tabs. They are static public pages served by CRA/dev server or by Express static serving in production.

Deathwatch RPG Domain Notes

Use these notes when changing the app's roller, mission, simulation, shop, rules, bestiary, and character-sheet flows. They are summarized from the local rules database and app data, not copied from rulebook text. Do not paste long rulebook passages into UI or docs; link/search the local rules DB and summarize mechanics in original wording.

Source Data In This Repo

  • Rules index: database/rules/rules-database.json
    • Categories include actions, combat, damage, conditions, requisition, renown, cohesion, squad_mode, psychic, hordes, gm_tables, mission, skills, talents, and traits.
  • Skill import/reference data: database/deathwatch_skills_p94_107.csv
  • Armoury/shop data: public/deathwatch-armoury.json
    • Major groups include ranged weapons, melee weapons, grenades, other weapons, power armour, helmets, carapace/natural/primitive/xenos armour, shields, and other armour.
  • Bestiary data: public/deathwatch-bestiary-extracted.json and database/deathwatch-bestiary-extracted.json
    • Entries generally include profile, movement, wounds, toughness, skills, talents, traits, armour, book, page, and snippets.
  • Backend may load rules, weapons, and bestiary from MariaDB first, then fall back to the JSON files above.

Core Resolution Model

  • Deathwatch uses percentile tests: roll d100 and succeed when the roll is equal to or under the effective target.
  • Effective target is normally characteristic + skill/training + situational modifiers. The app generally clamps practical targets to d100 bounds.
  • Degrees of Success/Failure are 10-point bands from the target/roll margin. The app's shared helper returns 1 base degree plus one per full 10 points.
  • Basic skills can be attempted broadly; untrained advanced skills should be treated as unavailable or heavily constrained unless the app explicitly allows an override.
  • Common characteristics:
    • WS Weapon Skill for melee attacks and parries.
    • BS Ballistic Skill for ranged attacks.
    • S, T, Ag, Int, Per, Wp, Fel for strength, toughness, agility, intelligence, perception, willpower, and fellowship checks.
  • When making UI for tests, expose the final target, roll, success/failure, DoS/DoF, and the source of modifiers. This is more useful at the table than only showing pass/fail.

Combat Flow

  • Combat is round-based and action-based. Common action concepts include Aim, Charge, Standard Attack, Semi-Auto Burst, Full Auto Burst, Overwatch, Suppressing Fire, Dodge, Parry, Ready, Reload, Move, Run, and Disengage.
  • Initiative is relevant to combat order. In the simulator, each combat round currently rolls player Agility Bonus + d10 and enemy agBonus + d10, then resolves actors from highest to lowest.
  • Attack flow for app purposes:
    • Choose WS or BS based on melee/ranged attack.
    • Apply attack-mode and situation modifiers.
    • Roll d100 against target.
    • On a hit, derive hit count from fire mode and DoS.
    • Determine hit location from the reversed d100 attack roll where needed.
    • Roll damage, apply weapon qualities such as Tearing/Proven where supported.
    • Reduce damage by armour and Toughness Bonus/Penetration handling as the app model supports.
    • Track wounds, critical damage threshold, defeat/downed status, and notable events.
  • Semi-auto and full-auto are not just labels. They change the attack modifier and how additional hits scale with DoS. Keep roller and simulator hit-count formulas aligned.
  • Dodge and Parry are reactions that negate hits when allowed. Dodge is usually the generic ranged/melee avoidance reaction; Parry is melee and WS-based.
  • Suppressing fire and pinning matter in Deathwatch combat. Pinned targets are constrained and should be represented as a condition when simulation or mission play needs battlefield pressure.
  • Righteous Fury is a special high-damage/exploding-damage style event on successful attacks. The simulator currently records "critical hit"/hero moments for high DoS, while the roller has damage dice mechanics; be explicit if changing this behavior to match a stricter table interpretation.

Weapons, Damage, Armour, And Qualities

  • Weapon data often appears as compact strings like range, RoF, damage, penetration, clip, reload, and qualities. Prefer structured parsing helpers over ad hoc display-only splitting when adding behavior.
  • Damage expressions use dice notation such as 1d10+9, 2d10+2, or 3d10+4, with a damage type marker in source data (E, I, R, X, etc.) for energy, impact, rending, explosive, and related categories.
  • Important weapon qualities represented in app data include Tearing, Reliable, Accurate, Blast, Flame, Power Field, Razor Sharp, Unbalanced, Unwieldy, Volatile, Scatter, Storm, Smoke, Toxic, and others.
  • Tearing usually means roll extra damage dice and keep the better result for supported dice. Proven sets a floor on supported damage dice. Only apply qualities that the code explicitly models.
  • Armour can be whole-body or location-based. Bestiary and armoury entries may use different shapes for protection values, so normalize before using in combat calculations.
  • Penetration should reduce armour effectiveness, not Toughness, unless a local helper intentionally abstracts damage.

Missions, Requisition, Renown, And Economy

  • Deathwatch missions are framed around a kill-team selecting mission gear, entering an operation, resolving scenes, earning XP/renown, and returning equipment.
  • Requisition Points are mission equipment budget, not ordinary money. The shop stores purchases as gear and deducts tabInfo.rp.
  • Renown gates access to more prestigious or restricted equipment. The app rank order is None, Respected, Distinguished, Famed, Hero; keep that order consistent in shop and player-management flows.
  • Mission scenes in this app can include objectives, complications, checks, enemies, combat state, reveal state, extra challenges, rewards, and ending text.
  • Good mission UI should separate GM-only information from player-safe scene information. The backend playerScene() projection strips hidden/GM fields.

Skills And Scene Checks

  • Useful scene-check skills from the local skill data include Awareness, Command, Tactics, Tech-Use, Medicae, Scrutiny, Search, Logic, Inquiry, Intimidate, Demolition, Tracking, Survival, Security, Psyniscience, and relevant Lore skills.
  • Tactics is an Intelligence skill and should be used for battlefield doctrine, priority targets, ambush reads, deployments, or tactical approach. It should not automatically grant Squad Mode abilities or Cohesion benefits unless a mission rule explicitly says so.
  • Awareness/Search/Tracking are good for threat and clue discovery.
  • Tech-Use/Security/Demolition are good for machines, locks, traps, explosives, and sabotage.
  • Medicae/Chem-Use are good for injuries, toxins, infection, biology, and field treatment.
  • Command/Charm/Intimidate/Scrutiny/Deceive support social or morale scenes.
  • Puzzle scenes should usually produce multiple checks with assigned best-fit players, visible targets, complications on high DoF, and rewards on success.

Cohesion, Squad Mode, Solo Mode, And Fate

  • Cohesion is the kill-team resource around coordination and Squad Mode. Existing app coverage is mostly reference/rules-search level; do not invent persistent Cohesion behavior without adding explicit state and tests.
  • Squad Mode and Solo Mode are distinct from ordinary skill checks. A successful Tactics or Command roll should not silently toggle Squad Mode.
  • Fate points can be spent to improve outcomes. In this app:
    • The roller exposes re-roll, +10, and +DoS style fate actions.
    • useFate.js reads and persists fate through player endpoints.
    • The simulator may spend fate once on severe failed checks.
  • Keep fate changes persistent and visible because players track remaining fate across scenes/sessions.

Psychic Powers And Warp Risk

  • Psychic rules involve Focus Power tests, Psy Rating, and risk modes such as safer/fettered use versus riskier high-output use.
  • Psychic Phenomena and Perils of the Warp are significant consequences. If adding psychic automation, model risk and output separately and expose the roll trail clearly.
  • Avoid treating psychic powers as generic skills unless the feature explicitly asks for a simplified helper.

Hordes And Large Enemy Groups

  • Hordes are a special combat abstraction for many lesser enemies. They should not be treated as a single normal NPC unless the app is intentionally using a simplified profile.
  • Horde-relevant UI should track magnitude/remaining threat, area effects, full-auto/multi-hit attacks, Blast, Flame, and morale/breaking behavior.
  • Current simulation uses simplified enemy lists and wounds, not a full horde engine.

Bestiary And Enemy Modeling

  • Bestiary entries vary by source and import quality. Normalize names, profile stats, wounds, armour, movement, talents, traits, and attacks before using them in roller/simulation logic.
  • Common enemy themes in app code include Tyranids, Chaos, Xenos/Tau, Orks, and servitor/Imperial profiles.
  • For combat simulation, prefer clear approximations and visible findings over opaque precision. If a stat is missing, surface the fallback rather than pretending it came from the book.

Simulation Design Guidance

  • Simulation is a balance/design tool, not an authoritative rules engine.
  • Keep simulated rolls categorized. Current categories include:
    • combat for attacks and combat-scene tactical checks.
    • puzzle for non-combat scene checks.
    • fear for fear tests.
  • Reports should show overall success %, combat %, puzzle/check %, XP, rounds, roll count, scene results, player report cards, ammo/fate use, conditions, initiative events, and findings.
  • Findings are scripted threshold notes, not AI. They do not use OpenAI/Ollama tokens unless a future feature explicitly calls an AI endpoint.
  • Story hooks are scripted from mission scene data. Mission generation stores a GM-only storyHook/secret on each scene; simulation reports which checks or combat outcomes revealed, partially revealed, or escalated those hooks.
  • Scripted findings should give the GM actionable mission edits grounded in the rules model: reduce or increase enemy pressure, add cover/Aim/Dodge/Parry prompts, add non-attack combat objectives, tune puzzle difficulty, allow assists/alternate skills, prevent single-roll clue bottlenecks, add recovery beats for conditions/fear, and match player roles to checks.
  • Evaluate combat and puzzle/skill pressure separately. A good sim report should make it clear whether the mission is too lethal, too easy, too attack-only, too clue-blocked, or mismatched to the party's stats.
  • Because simulation uses Math.random() and is unseeded, never make exact roll totals a test expectation. Test shape, categories, persistence, and visible report fields instead.
  • The repo contains local summaries, OCR, PDFs, and extracted data for gameplay support. When writing UI/help/docs, use short summaries in original wording.
  • Do not reproduce long rulebook sections, tables, or copyrighted passages in new docs or UI. Prefer links/search into local rule entries and cite source title/page metadata where helpful.
  • If a requested feature needs exact rules text, build a lookup/display path from the existing rules database instead of embedding copied text in code.

API Surface

Mounted API prefixes in database/server.js include:

  • /api/players
  • /api/sessions
  • /api/shop
  • /api/rules
  • /api/weapons
  • /api/bestiary
  • /api/rules/staging
  • /api/missions
  • /api/simulations
  • /api/errors
  • /api/gmkit/list
  • /api/gmkit/upload
  • /api/copy-bestiary
  • /api/narrate

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

Files To Treat Carefully

  • Do not commit or churn runtime logs such as database/backend.log or 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.
  • public/ and database/ contain generated/reference JSON used by the app; update paired files intentionally when scripts require it.
  • There may be unrelated local changes. Inspect git status --short before editing and avoid reverting user work.

Documentation Notes

  • README.md and DEVELOPMENT_GUIDE.md are useful, but several docs under docs/ and CLAUDE.md mention an older split backend/ + frontend/ structure. The current live structure is root CRA frontend plus database/server.js backend.
  • When updating docs, prefer correcting stale structure references rather than propagating them.