Extract the skill-check UI/logic into a standalone SkillRoller component
and move shared dice helpers (d100, degrees, uid) into rollHelpers.js.
DeathwatchRoller now hosts the shared roll history + player sheet and a
Combat/Skill tab toggle; the skill-only characteristic/training helpers
moved into SkillRoller. Behaviour unchanged, including first-skill
preselect (now done inside SkillRoller) and the manual target override.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Allow setting an explicit target number to roll against instead of the
computed Per+training+mod. Works with no sheet skill selected (e.g. a
GM-set 'roll vs 43'). Empty field keeps the auto-computed behaviour.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Delete 17 one-off migration/admin scripts that imported the removed
./sqlite-db module. None are referenced by the app, package.json, or CI;
they broke at the SQLite->MariaDB migration. No live code imports
sqlite-db anymore.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sessions are served from MariaDB via sessionModel.js + routes/sessionRoutes.
These files were orphaned by the SQLite->MariaDB migration:
- requireSession.js: unused (no importers), still required ./sqlite-db
- healthcheck.js: required removed ./sqlite-db; would crash every run
- server-healthcheck.yml: scheduled the broken healthcheck every 15 min
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
healthcheck.js only opens the local committed SQLite DB on the runner
and never contacts the deployed server, so the every-15-min schedule
validated nothing and consumed Actions minutes. Keep workflow_dispatch
for manual runs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Dedicated 'Run unit tests' step (npm run test:unit) so unit tests are
visible and guaranteed in CI rather than implied by the build script.
- Build now calls react-scripts build directly, avoiding a redundant
second Jest run (CI=true still fails the build on lint warnings).
- Bump Node 18 (EOL) -> 20 and enable npm dependency caching.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wrap fetchReports in useCallback so the useEffect dependency array
satisfies react-hooks/exhaustive-deps. CI treats build warnings as
errors (CI=true), so this lint warning was failing the build step.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add 👁/🔒 toggle to combat row in Scene Secrets — players see
combat panel only when GM reveals it (scene.reveal.combat).
Stop flickering: background polls now run silent (no setLoading),
and state updates are skipped when JSON content is unchanged,
preventing unnecessary re-renders every 4s.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add amber banner at top of Mission Tab showing next session date
(Lørdag 28. juni 2026). Create 3-scene Danish demo mission
"Blodprøve: Deathwatch-protokol" (id 10) — Social briefing →
Investigation aboard a dark station → Combat vs Genestealer Patriarch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace single scene reveal toggle with individual unlock buttons for
title, mood, description, objectives, complications, and each check —
GM controls exactly what players see and when.
Add GM manual roll entry (🎲 button per check): select player, type
the physical dice result, submit records it to the mission roll feed
under that player's name.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Mood text field on scenes shown as amber italic read-aloud block
above scene description (gated by revealed flag for players)
- Scene editor includes Mood / Read-aloud textarea
- Scene editor moved full-width below two-column grid
- Mission "The Hunt for Fabius Bile" fully translated to Danish
("Jagten på Fabius Bile") with Danish names, descriptions,
objectives, complications, checks and mood text for all 10 scenes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Edit Scene button in Scene Secrets header opens an inline editor card
below Table State. Lets the GM edit scene title, type, description,
checks (name/skill/char/target/modifier/reward, add/remove), enemies
(name/wounds/BS/WS/damage/AP, add/remove), objectives, and
complications — all saved live via the existing patchScene path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 tiers selectable in Sim Lab config panel. Each tier scales enemy
wounds/BS/AP, adds check penalties, adjusts max combat rounds,
inter-scene healing chance, fear bonus, and adds a 3rd Elite Guard
enemy at Brutal+. Difficulty stored in DB (difficulty_level column)
and shown as a badge in the sidebar and report header.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- Add `simulations` table to MariaDB (mission, players, result, XP,
roll feed, player cards, scene log, findings — all persisted as JSON)
- simulationHelpers: save / getAll / getById / delete
- simulationRoutes.js: POST /api/simulations (run engine + save),
GET /api/simulations (list), GET /:id (full report), DELETE /:id
- Simulation engine: d100 dice, fear tests, combat rounds with
initiative, conditions, damage, fate re-rolls, skill checks,
auto-assign checks to best-stat player, enemy pools from scene data,
per-player report cards with grade A-D, hero moments, shame list
Frontend:
- SimulationTab.jsx: GM-only tab with sidebar list of past runs,
"Run New Simulation" config panel (pick mission + players),
collapsible scene log, player report cards, filterable roll feed,
findings panel
- App.js: import SimulationTab, add "⚙ Sim Lab" nav button (GM only)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Full standalone Node.js simulation of 'The Hunt for Fabius Bile'.
Real d100 rolls vs actual player stats, wound tracking, fear tests,
initiative, conditions, fate spends, and per-player report cards.
Run: node src/tests/playthroughRun2.js
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fetch player RP/renown/gear from GET /api/players/:name (public full data)
instead of the name-only player list, so RP is never 0
- Player status bar: RP (amber), renown badge, collapsible gear inventory
- Purchase flow posts to /api/shop/purchase and reads newRp from response
- Affordable filter, renown filter, item cards show affordability state
- GM panel: all-player RP/renown/gear table, Set RP, Set Renown forms
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On load, each player's character sheet is fetched from /api/players/{name}:
- BS and WS set from tabInfo.characteristics
- Weapon dropdown replaced with only their personal weapons (normalised
from tabInfo.weapons: type→class, ap→pen, default modes by type)
- First weapon pre-selected with all its damage/pen/rof/mode defaults applied
- maxWounds set from tabInfo.wounds; curWounds preserved from localStorage
if the player was already tracking mid-session
Active scene conditions (synced from MissionTab via dw:combat-state) now
apply automatic attack modifiers:
- Pinned: –20 BS
- Stunned: –20 BS/WS
- Knocked Down: –20 BS/WS
- Fatigued: –10 BS/WS
- Grappled: –20 WS, ranged/heavy weapons hidden from weapon dropdown
- Bleeding: banner only (no roll modifier)
Condition banner appears above the roller when any condition is active,
showing each condition label and the net modifier to the attack target.
The status bar target display also shows the condition delta inline.
MissionTab: conditions map now included in dw:combat-state localStorage
write and CustomEvent payload so the Dice Roller receives per-player
condition state in real time.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All 11 issues from 4-player simulation of The Hunt for Fabius Bile:
1. Roll feed visible to all players (no GM auth guard)
2. Scene text gated by revealed flag; GM explicit reveal per scene
3. GM can add NPC/enemy entries to initiative tracker
4. Round counter synced to Dice Roller via localStorage + custom event
5. Check reward text hidden until player has rolled
6. Checks assignable to specific player via Scene Secrets ⚙ options
7. Fear test quick-roll panel with WP input appears when fearRating > 0
8. Decision checks show textarea/Declare instead of d100 roll button
9. Fate point re-roll: one per check per scene, resets on scene advance
10. Player poll reduced from 8s to 4s, combined mission + roll feed poll
11. Mission complete banner with scene stats and GM outcome notes field
4 follow-up findings from second simulation run:
- Finding #1: Activate mission now initialises revealed:false on all scenes
so players never see scene text before the GM narrates
- Finding #2: Fear penalty auto-applied to WP display; button shows
effective (penalised) target rather than raw WP input
- Finding #3: RollFeedRow moved outside component to avoid re-mount on
every render; onDelete passed as prop
- Finding #4: Removed duplicate "Open for Players" quick-button from
Scene Checks left panel — Scene Secrets is the sole entry point
New files:
- src/tests/missionPlaythrough.test.js — full GM+4-player simulation
test suite covering all 11 issues and 4 findings (39 test cases)
- src/tests/missionTab.test.js — player/GM view isolation tests
- src/utils/diceRoller.js — shared d100/degrees/clampTarget utilities
- tests/missionRoutes.test.js — backend mission route unit tests
- tests/playerRoutesLogin.test.js — player login route tests
- tests/sessionRoutes.test.js — session validation tests
Note: React unit tests require jsdom; segfaults on ARM64 (Raspberry Pi)
due to a known jsdom/Node 20 incompatibility on aarch64. Tests pass on
x86 CI. Backend integration tests (tests/) run normally.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace username text input with dropdown selector for player login
- Fix unescaped apostrophe in MissionSimTab.jsx (Emperor's -> double quotes)
- Fix em-dash encoding issues in MissionSimTab.jsx
- Update player password to 1234 in .env
- Tab navigation wraps on small screens (flex-wrap)
- Smaller padding and font on mobile (px-4, text-xl)
- Responsive main content padding (px-4 sm:px-6)
- Tighter spacing on login row (gap-3, mb-3)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Rename MissionTab to MissionSimTab (combat simulation, GM only)
- New MissionTab shows mission history (all users)
- Mission Sim tab added to GM-only navigation
- MissionSimTab.jsx moved to src/components/
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- MariaDB missions table with scenes JSON
- /api/missions CRUD routes
- MissionTab saves completed missions to DB
- Mission history panel in UI
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- New MissionTab component with scene-based combat simulation
- Enemy roster from DeathwatchRoller (12 enemy types)
- Scene progression (4-6 scenes) with threat levels
- Combat log and results tracking
- Added to App.js navigation
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Remove OCR noise, credits, and duplicates from rules-database.json (288→255 rules)
- Add clean_rules.py script for rule cleanup
- Add CLAUDE.md, docs/, and update README with documentation links
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- scripts/generate-weapon-images.js: AI image generation for weapon cards
supporting HuggingFace FLUX.1-schnell, OpenAI DALL-E 3, and Gemini Imagen;
images served from public/weapon-images/ via new static route in server.js
- public/cards.html: load manifest.json and show AI art on weapon cards
- scripts/scrape-rules-from-pdfs.py: PyMuPDF scraper extracting full rule
text from all five rulebook PDFs with bold-span heading detection
- scripts/tag-and-dedup-rules.js: standalone dedup helper (JS-side grouping)
- database/routes/rulesRoutes.js: remove content truncation in search/random,
add admin dedup endpoint (JS-side, fast), fix RulesTab to fetch full content
- src/components/RulesTab.jsx: fetch full rule content on modal open
- public/print.html: expanded quick reference with pre-gen character stat
blocks (Sepheran + Lucian), Astartes traits sheet with correct derived SBs
and TBs, full Critical Hit Tables for all 4 locations × 4 damage types
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both open in a new tab (target=_blank) since they are standalone
HTML pages outside the React app.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Art box reduced from 32mm to 26mm height, icon enlarged to 22pt
- Stats font increased from 6pt to 7pt with more line spacing
- Replaced floating source pip with proper footer bar showing
renown requirement (left) and source book (right)
- Footer bar colour-coded by weapon category to match title bar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Poker-card sized (63×88mm) cards matching the physical Deathwatch card format:
- Req cost badge top-left
- Title bar with weapon name
- Dark art box with category icon and class label
- Type line (Weapon – Melee, Weapon – Ranged, Equipment, etc.)
- Stats block (Dmg, Pen, Range, RoF, Clip, Rld, Special, Renown)
- Source abbreviation bottom-right
9 cards per A4 sheet, cut-ready.
Filter by category (Melee/Ranged/Grenade/Armour/Other).
Data loaded live from /api/weapons.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>