A local chess coaching AI that pulls your games from Chess.com and Lichess, runs deep Stockfish analysis on every move, and uses reasoning LLMs to generate personalized, age-appropriate coaching insights — with pattern tracking over time and a live web dashboard.
Dedicated to my wife Yuying Deng and our three children — Eleanor, Evan, and Estella — whose chess journey inspired every line of this project.
My kids play chess. After every game, they'd ask: "How did I do? What should I practice?"
Chess engines can tell you what went wrong — move 23 was a blunder, you lost 300 centipawns. But they can't tell you why it matters, what pattern caused it, or how to fix it next time. A list of computer evaluations isn't coaching.
Online platforms like Chess.com and Lichess offer basic game review, but nothing that adapts to a child's age, connects lessons across multiple games, or builds on what was taught last week. Real coaching requires context — and context is what's missing.
Arrakis Engine bridges that gap. It pairs Stockfish's analytical depth with reasoning LLMs (Claude, GPT, Gemini, and 5 more) to produce coaching that:
- Explains positions in language a 9-year-old can understand
- Remembers what was taught in the last 5 games and builds on it
- Detects game types (tactical battle, comeback, opening disaster) and adjusts advice accordingly
- Tracks patterns over weeks and months — not just single games
- Writes a personal letter to the player after each game with 3 specific things to practice
It supports 8 LLM providers (including Ollama for free local coaching) and runs entirely on your own machine. Your data stays yours.
The name comes from Frank Herbert's Dune — on Arrakis, the spice must flow. In chess, good coaching must flow.
- Why Arrakis Engine?
- Screenshots
- How a Chess Parent Uses This
- Quick Start for Chess Parents
- Architecture
- Full Installation Guide
- CLI Commands
- Typical Workflow
- How Analysis Works
- Web Dashboard
- Project Structure
- Running Tests
- Troubleshooting
- Acknowledgements
- License
Where you bleed ELO and what to keep using. Each row shows total games, W/L/D split, and links to the most recent game so you can study it.
Recurring named opening traps in your games — backed by the Lichess CC0 chess-openings library. Click any row to see the trap unfold on a board, jump to the games where it happened, and study the line on Lichess.
Look up an opponent's public games on chess.com or lichess. The targeted-prep view shows openings the opponent loses (your hunting targets) and openings they win (lines to avoid).
Click any opening row to see how the opponent actually played the line — step-through mini-board, "Game N of M" controls flipping through up to 5 representative games, annotated move list with deviations from book theory, and a "Study this position on Lichess" deep link.
You don't need to understand Stockfish, centipawns, or LLMs to use Arrakis Engine. Here's what the experience looks like:
Add your child's Chess.com or Lichess username to the config file. Arrakis fetches every game from the last 6 months automatically — rapid, blitz, bullet, and daily. It deduplicates, so you can run it as often as you like without worrying about double-counting.
Games that aren't online — over-the-board tournament games played in person — can be brought in too: paste or upload their PGN on the Import page (a whole multi-game tournament file works). Turn on "Over-the-board / competition game", pick the game type (Classical / Rapid / Blitz), and each game is analyzed and coached exactly like an online one, tagged with a 🏆 Competition badge. Ratings, the date/time, and the category are all editable per game afterwards — and if you mistyped a move off the scoresheet, Edit moves on the game's detail page lets you paste a corrected PGN, which re-validates the moves (pointing at the exact illegal move if any) and re-runs analysis and coaching in place (v1.32.0). The competition's name and venue are never stored, for privacy.
Every game gets deep engine analysis: each move is evaluated, blunders and mistakes are highlighted, and a win-probability chart shows exactly where the game turned. You'll see which moves were excellent, which were inaccurate, and which were outright blunders — color-coded and interactive on the dashboard (see Games List screenshot above).
This is where Arrakis is different. An AI reads the full analysis and writes a coaching brief for each game:
- A game story — "You played the Italian Game and got a great position out of the opening, but after move 18 you started rushing..."
- A key lesson — "When you're winning, slow down and look for your opponent's best reply before moving."
- A personal letter — 3 specific, actionable tips written in an encouraging tone appropriate for the player's age.
- Coach notes — a technical summary for the parent or coach to use in lesson planning.
The AI remembers what it taught in previous games, so advice builds over time rather than repeating the same tips.
After a few weeks of games, the Patterns page comes alive (see Patterns screenshots above). You'll see:
- Whether accuracy and consistency are improving week over week
- Which openings are working and which need attention
- Danger zones — the move ranges where errors cluster
- How well your child converts winning positions in endgames
- Time management patterns — do they blunder more when the clock is low?
All of this runs locally on your computer. Your data stays yours.
A simplified path to get up and running. You'll need macOS or Linux, Python 3.11+, and Node.js 18+.
# 1. Clone the repository
git clone git@github.com:bleongcw/Arrakis_Engine.git
cd Arrakis_Engine
# 2. Install Stockfish (the chess engine)
brew install stockfish
# 3. Set up Python
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# 4. Configure your players
cp config.yaml.example config.yaml
# Edit config.yaml — replace "your_chess_com_username" with your child's usernameFor LLM coaching, create a .env file with at least one API key (see Configure API keys). Or use Ollama for free local coaching — no API key needed.
# 5. Run the full pipeline (harvest → analyze → coach → patterns)
python main.py run-all# 6. Install frontend dependencies (one time only)
cd frontend && pnpm install && cd ..
# 7. Start both servers with a single command
python main.py serveYou'll see a unified banner like this:
🏰 Arrakis Engine running
📡 Frontend UI: http://localhost:3000 ← open this
🔌 API backend: http://localhost:8000
📊 Live data from: data/chess_coach.db
🕒 Auto-updates: disabled (every 6h)
Press Ctrl+C to stop both servers.
Open http://localhost:3000 in your browser — that's the dashboard. Hit Ctrl+C in the terminal to stop both servers cleanly.
What
servedoes under the hood. The Python API backend runs on port 8000, bound to127.0.0.1(loopback) by default (v1.29.0) — the API has no authentication, so it is not exposed to your network unless you opt in with--host 0.0.0.0. The Next.js frontend is launched as a child process on port 3000 (with output prefixed[frontend]so it stays scannable) and reaches the API same-origin via a Next.js rewrite. Both are stopped together when you Ctrl+C. The--installflag will runpnpm installfor you if you skipped step 6.
If you want hot-reload visibility on each server independently — useful when developing — you can still run the two halves manually:
| What runs | Port | Start with | |
|---|---|---|---|
| Terminal 1 | Python API backend (SQLite, Stockfish, LLM coaching) | 8000 |
python main.py dashboard |
| Terminal 2 | Next.js frontend (the dashboard UI) | 3000 |
cd frontend && pnpm dev |
Why two servers? The backend is a minimal Python
http.serverthat exposes the database over REST. The frontend is a Next.js app that provides the rich UI. Splitting them lets the frontend hot-reload while you develop, and keeps the backend dependency-light.
For advanced options (compile Stockfish from source, configure multiple providers, Ollama setup), see the Full Installation Guide below.
The pipeline is layered:
- Stockfish engine evaluation — objective, per-move centipawn analysis with clock time extraction
- LLM coaching interpretation — transforms raw engine output into human-readable insights
- Pattern aggregation — tracks trends across games over weeks and months
- LLM trend summaries — interprets cross-game patterns into coaching narratives
- Time pressure analysis — per-move clock data reveals time management patterns and pressure-induced blunders
- Tactical-motif detection (v1.14.0–v1.17.0) — 12 detectors tag each critical move with the themes it executes or misses (fork, pin, skewer, …, zugzwang), aggregated cross-game with per-phase concentration and surfaced in coaching + the Tactical Themes Patterns card
- Journal (v1.10.0–v1.12.0) — a chronological diary of coaching artifacts: LLM-generated Recent Form Reviews + manual Parent Notes, in a threaded feed
- Recurring weakness escalation (v1.19.0) — when a missed motif PERSISTS across games (distinct-game spread + recency streak), coaching escalates from "watch for forks" to a prescribed drill, the Tactical Themes card shows a 🔴/🟠/🟡 badge, and a fire-once "Priority Weakness" Journal alert is filed
- Hunter Mode Deep Scan (v1.20.0) — opt-in Stockfish + motif analysis of an opponent's games surfaces the tactical themes they miss ("Tactical Blind Spots" — the patterns to bait them into)
- Tournament Prep (v1.21.0) — a saved, named roster of opponents with a combined cross-opponent analysis: which openings to play (the field loses to them) / avoid (the field wins with them) + a field-wide tactical blind-spots panel
The frontend is a mobile-responsive Next.js 16 + React 19 dashboard with player-scoped URLs keyed on a stable slug (v1.16.x) — /<slug>/games, /<slug>/patterns, /<slug>/journal, /<slug>/hunt, /<slug>/tournament, /<slug>/reports — so chess.com renames never break bookmarks.
Looking for a faster path? See Quick Start for Chess Parents above.
| Requirement | Version | Notes |
|---|---|---|
| Python | 3.11+ | Tested on 3.12 |
| Stockfish | 16+ | Apple Silicon recommended |
| macOS / Linux | Any | Developed on macOS (Apple Silicon) |
- Anthropic — console.anthropic.com → API Keys
- OpenAI — platform.openai.com → API Keys
- Google — aistudio.google.com → API Keys
- xAI — console.x.ai → API Keys
- Mistral — console.mistral.ai → API Keys
- DeepSeek — platform.deepseek.com → API Keys
- Qwen (DashScope) — dashscope.aliyun.com → API Keys
- Ollama — No API key needed. Install from ollama.com, then
ollama pull deepseek-r1:8b
The harvester and Stockfish analyzer work without API keys. You only need at least one provider key (or Ollama) for the LLM coaching step.
git clone git@github.com:bleongcw/Arrakis_Engine.git
cd Arrakis_EngineOption A — Compile from source (recommended, ~2x faster):
git clone https://github.com/official-stockfish/Stockfish.git
cd Stockfish/src
make -j profile-build COMP=clang ARCH=apple-silicon
sudo cp stockfish /usr/local/bin/stockfish
cd ../..
rm -rf StockfishOption B — Homebrew (simpler):
brew install stockfishVerify the installation:
stockfish <<< "uci" | head -1
# Should print: Stockfish 18 by the Stockfish developers (see AUTHORS file)python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtCreate a .env file in the project root:
# .env (gitignored — never committed)
ARRAKIS_ANTHROPIC_API_KEY=sk-ant-your-key-here
ARRAKIS_OPENAI_API_KEY=sk-your-key-here
ARRAKIS_GOOGLE_API_KEY=your-google-api-key # Optional — for Gemini
ARRAKIS_XAI_API_KEY=xai-your-key-here # Optional — for Grok
ARRAKIS_MISTRAL_API_KEY=your-mistral-key # Optional — for Mistral
ARRAKIS_DEEPSEEK_API_KEY=sk-your-deepseek-key # Optional — for DeepSeek
ARRAKIS_QWEN_API_KEY=sk-your-qwen-key # Optional — for Qwen
# Ollama needs no API key — just `ollama serve` running locallyIf you want to use local models instead of (or alongside) cloud APIs:
# Install Ollama
brew install ollama
# Pull the default model (~5GB download)
ollama pull deepseek-r1:8b
# Start the Ollama server (keep running in a separate terminal)
ollama serveAvailable local models:
| Model | Pull Command | RAM | Quality |
|---|---|---|---|
| DeepSeek-R1 8B | ollama pull deepseek-r1:8b |
~5GB | Good for testing |
| DeepSeek-R1 14B | ollama pull deepseek-r1:14b |
~9GB | Moderate coaching |
| DeepSeek-R1 32B | ollama pull deepseek-r1:32b |
~20GB | Strong coaching |
| Qwen3 8B | ollama pull qwen3:8b |
~5GB | Good JSON reliability |
Copy the example template and fill in your details:
cp config.yaml.example config.yamlEdit config.yaml to match your setup (this file is gitignored — your personal config stays local):
players:
- username: your_chess_com_username # Chess.com username (required)
lichess_username: your_lichess_id # Lichess username (optional)
fide_id: null # FIDE player ID (optional, e.g., 1234567)
display_name: Player 1
age: null
rating: null
- username: another_chess_com_username
display_name: Player 2
age: null
rating: null
stockfish:
path: /opt/homebrew/bin/stockfish # or /usr/local/bin/stockfish
depth: 22
threads: 6
hash_mb: 512
analysis:
months_lookback: 6
coaching:
default_provider: claude # claude | openai | gemini | grok | mistral | deepseek | qwen | ollama
anthropic_model: claude-opus-5
openai_model: gpt-5.6-sol
gemini_model: gemini-3.5-flash # optional — requires ARRAKIS_GOOGLE_API_KEY
grok_model: grok-4.5 # optional — requires ARRAKIS_XAI_API_KEY
mistral_model: mistral-medium-latest # optional — requires ARRAKIS_MISTRAL_API_KEY
deepseek_model: deepseek-v4-pro # optional — requires ARRAKIS_DEEPSEEK_API_KEY
qwen_model: qwen3.7-max # optional — requires ARRAKIS_QWEN_API_KEY
ollama_model: deepseek-r1:8b # optional — requires `ollama serve` running
ollama_base_url: http://localhost:11434
reasoning_effort: medium # low | medium | high | xhigh | max — applied to
# providers with a granular effort scale (Claude,
# ChatGPT, Mistral); others reason by default
# How many recent coached games to inject into each new coaching prompt
# so the AI avoids repeating prior advice. Default 5, range 1-20.
# See "Coaching history depth" below for token-cost guidance.
coaching_history_count: 5
database:
path: data/chess_coach.dbStockfish path: Defaults to /opt/homebrew/bin/stockfish (Homebrew). Use /usr/local/bin/stockfish if compiled from source. Run which stockfish to verify.
Threads: Set to your CPU core count minus 2 (e.g., 6 for an 8-core M-series chip) to leave headroom for other processes.
| Command | Description |
|---|---|
python main.py harvest |
Fetch games from Chess.com and Lichess for all configured players |
python main.py analyze |
Run Stockfish analysis on all pending games |
python main.py coach |
Generate LLM coaching insights (supports --limit, --provider, --history) |
python main.py patterns |
Compute cross-game pattern statistics (incl. motif aggregation) |
python main.py trend |
(v1.15.2) Regenerate the LLM trend summary (Coaching Summary card) |
python main.py review |
(v1.9.0) Generate a Recent Form Review across the last N coached games |
python main.py note |
(v1.12.0) Add a Parent Note to the Journal |
python main.py rescan-motifs |
(v1.14.0) Backfill tactical-motif tags on existing games (no Stockfish) |
python main.py hunt-scan |
(v1.20.0) Deep-scan an opponent's recent games for tactical blind spots (--opponent, --platform, --games) |
python main.py tournament-prep |
(v1.21.0) Warm a tournament roster's profiles + print the top opening targets (--id) |
python main.py report |
Generate Markdown coaching reports |
python main.py serve |
(v1.5.0) Launch backend + frontend together (recommended for end users; supports --port, --host, --frontend-port, --install) |
python main.py dashboard |
Launch only the API backend (use serve for the full app) |
python main.py fide-update |
Update a player's FIDE rating |
python main.py backfill-acpl / backfill-clocks |
Backfill capped ACPL / clock data on existing games |
python main.py run-all |
Run the full pipeline end-to-end |
--player accepts the player slug (v1.16.4), e.g. --player evanleong.
Harvest games:
# All configured players from all platforms
python main.py harvest
# Specific player only
python main.py harvest --player your_chess_com_username
# Filter by platform
python main.py harvest --platform chess.com
python main.py harvest --platform lichess
# Combine filters
python main.py harvest --player your_chess_com_username --platform lichessMulti-platform: Games are fetched from Chess.com (via monthly archives API) and Lichess (via PGN export API). Add
lichess_usernameto your player config to enable Lichess harvesting.Incremental by design: The harvester deduplicates by
game_url— it only fetches new games since your last harvest. Safe to run repeatedly without duplicating data. The dashboard shows which platform each game came from (♜ Chess.com / ♞ Lichess).
Analyze with Stockfish:
# Analyze all pending games (uses settings from config.yaml)
python main.py analyzeEach move has a 10-second time limit to prevent hanging on complex positions. Analysis takes ~5–10 min per game with Homebrew Stockfish or ~3–5 min per game with a source-compiled binary. For large backlogs (400+ games), run overnight.
Generate coaching insights:
# Use default provider from config
python main.py coach
# Use a specific cloud provider
python main.py coach --provider openai
python main.py coach --provider gemini
python main.py coach --provider grok
python main.py coach --provider deepseek
# Use Ollama for free local coaching (requires `ollama serve` running)
python main.py coach --provider ollama --limit 5
# Limit batch size (recommended for rate limits)
python main.py coach --limit 5
# Combine provider and limit
python main.py coach --provider openai --limit 5LLM Cost Warning: Each coaching call sends a detailed prompt (~3,000–7,000 tokens) and receives a structured response (~2,000–4,000 tokens). At current API pricing, coaching a single game costs approximately $0.03–0.10 with Claude and $0.02–0.08 with GPT-5.6. For a backlog of 400+ games, this can add up to $15–40 or more. Start with
--limit 5to verify quality and estimate your costs before running large batches. Ollama is free — it runs locally with no API costs.
Rate limits: Cloud providers have tokens-per-minute caps (e.g., OpenAI's
gpt-5.6-solat ~10,000 TPM on free tiers). Use--limit 5per batch to avoid 429 errors. Claude typically has higher throughput —--limit 10-20is safe. Ollama has no rate limits but is slower (~30–90s per game depending on model size).
Dashboard coaching: You can also coach individual games directly from the dashboard — select any provider from the dropdown on a game's detail page and click Coach Game. The pipeline panel also supports all 8 providers with Cloud/Local grouping. Results auto-refresh when complete.
Update FIDE rating:
# Update FIDE rating for a player
python main.py fide-update --player your_username --rating 1600
# Set FIDE ID and rating together
python main.py fide-update --player your_username --fide-id 12345678 --rating 1600FIDE ratings are updated manually via the CLI. You can also set
fide_idinconfig.yamlto have it linked on first harvest. The dashboard links directly to the player's FIDE profile atratings.fide.com/profile/{fide_id}.
Generate reports:
# Weekly report for a specific player
python main.py report --player your_chess_com_username --weekly
# Monthly report for all players, custom output directory
python main.py report --monthly --output reports/marchLaunch the dashboard:
# Terminal 1: Start the Python API backend
python main.py dashboard
# → http://localhost:8000
# Terminal 2: Start the Next.js frontend
cd frontend && pnpm install && pnpm dev
# Open http://localhost:3000Run the full pipeline:
python main.py run-all
# Executes: harvest → analyze → coach → patternsAdd -v before the subcommand for debug output:
python main.py -v harvest
python main.py -v analyze# 1. Fetch all games from the last 6 months
python main.py harvest
# 2. Run Stockfish analysis (let this run overnight for large backlogs)
python main.py analyze
# 3. Generate coaching insights
python main.py coach
# 4. Compute cross-game patterns
python main.py patterns
# 5. View results
python main.py serve# Pull new games, analyze, coach, update patterns, export — all in one command
python main.py run-all
# Generate weekly reports for coaches
python main.py report --player your_chess_com_username --weekly
# Review in dashboard
python main.py dashboard| Setting | Value | Rationale |
|---|---|---|
| Depth | 22 | Catches all tactical errors at beginner-to-intermediate levels |
| Threads | 6 | Leaves 2 cores free on M-series chips |
| Hash | 512 MB | Sufficient for single-game analysis |
| Time limit | 10s/move | Prevents hanging on complex endgame positions |
| MultiPV | 1 | Best move only (keeps analysis focused) |
Each move is classified by centipawn loss (how much worse it is than the engine's best move). Thresholds adapt to the player's tier — stricter for advanced players, looser for beginners:
| Classification | Beginner (<800) | Elementary (800–1200) | Intermediate (1200–1600) | Advanced (1600+) |
|---|---|---|---|---|
| Excellent | < 50cp | < 30cp | < 20cp | < 15cp |
| Good | < 100cp | < 50cp | < 40cp | < 30cp |
| Inaccuracy | < 200cp | < 100cp | < 70cp | < 60cp |
| Mistake | < 500cp | < 300cp | < 200cp | < 150cp |
| Blunder | 500+ | 300+ | 200+ | 150+ |
All engine evaluations are capped at ±1000 centipawns before computing centipawn loss, matching the industry standard used by Lichess and Chess.com. This prevents mate scores and extreme positions from distorting ACPL calculations. Mate-in-X scores are mapped to ±1000cp.
Centipawn evaluations are converted to win probability using the Lichess formula:
win% = 50 + 50 × (2 / (1 + exp(-0.00368208 × cp)) - 1)
This makes evaluation swings more intuitive — a drop from 70% to 30% win probability is far more meaningful than "lost 200 centipawns."
ArrakisEngine requires reasoning LLMs — models with chain-of-thought capabilities — for its coaching layer. This is a hard requirement, not a preference. Standard chat or instruction-tuned models produce shallow, generic feedback that fails to help players improve.
Chess coaching demands multi-step reasoning at every level:
| Coaching Task | Why Reasoning Is Essential |
|---|---|
| Tactical analysis | Must trace forcing sequences ("if Nxe5, then Qd4+ forces Kf1, and Bh3+ wins the queen") — requires look-ahead over multiple moves |
| Pattern recognition | Must connect move classifications, eval swings, and game phases into a coherent strategic narrative rather than listing isolated facts |
| Coaching history | Must read the last 5 games' coaching and deliberately vary advice, build on prior lessons, and track whether the player is improving at specific skills |
| Age-appropriate explanations | Must simplify complex positional concepts for a 7-year-old differently than for a 14-year-old, while keeping the analysis accurate |
| Game type detection | Must classify games into types (tactical battle, comeback, positional grind, opening disaster, etc.) and adjust coaching emphasis accordingly |
| Structured JSON output | Must produce reliable, parseable JSON with all required fields — reasoning models are significantly more consistent at this than non-reasoning models |
Supported reasoning models (8 providers):
| Provider | Model | API Identifier | Notes |
|---|---|---|---|
| Anthropic | Claude Opus 5 | claude-opus-5 |
Extended thinking, excellent coaching tone |
| OpenAI | GPT-5.6 Sol | gpt-5.6-sol |
Strong reasoning via Responses API |
| Gemini 3.5 Flash | gemini-3.5-flash |
Long context, strong reasoning | |
| xAI | Grok 4.5 | grok-4.5 |
OpenAI-compatible API |
| Mistral | Mistral Medium | mistral-medium-latest |
European alternative |
| DeepSeek | DeepSeek V4 Pro | deepseek-v4-pro |
Strong reasoning, affordable |
| Alibaba | Qwen 3.7 Max | qwen3.7-max |
Large reasoning model |
| Ollama (Local) | DeepSeek-R1 8B | deepseek-r1:8b |
Free, runs locally, no API key |
See ROADMAP.md for details on local model quality considerations.
Non-reasoning models will not work. Models without chain-of-thought (e.g., GPT-4o-mini, small instruction-tuned models, base models) miss tactical sequences, generate generic advice not grounded in actual positions, and produce inconsistent JSON. If you swap in a non-reasoning model, expect significantly degraded coaching quality.
For each analyzed game, the LLM produces:
| Output | Audience | Description |
|---|---|---|
| Game narrative | Child | 2–3 paragraph story of what happened, encouraging tone |
| Key lesson | Child | Single most important takeaway |
| Practical focus | Child | One specific thing to practice |
| Opening analysis | Both | Opening name, quality rating, counter-move assessment, and tip |
| Critical moments | Both | 3–5 positions with what happened vs. what was better |
| Player feedback | Child | Personal letter with 3 actionable tips, growth mindset framing |
| Coach notes | Coach | Technical summary for lesson planning |
To prevent the AI coach from giving the same advice every game, Arrakis injects a "coaching history" block into every prompt — the last N coached games' lessons, practical focuses, and narrative openings. The coach is then instructed to build on that history rather than repeat it.
The depth is configurable via the coaching_history_count setting (in config.yaml, the Settings page, or --history N on the CLI). Default is 5; range is 1–20.
Token cost — each history game adds ~500 prompt tokens. Pick the depth that fits your provider's context window:
| Depth | Extra prompt tokens | Recommended for |
|---|---|---|
| 5 (default) | ~2,500 | All providers, including Ollama 8B local — safe baseline |
| 10 | ~5,000 | All cloud providers; tight on Ollama 8B (may overflow) |
| 15 | ~7,500 | Cloud providers only |
| 20 (max) | ~10,000 | Large-context cloud providers only — Claude, Gemini |
When to increase it. If a player has 50+ coached games and you find the coach repeating itself or missing recurring issues that span more than 5 games, raise the depth to 10. If you're running a deep retrospective (end of month, end of season), 15–20 gives the AI enough context to surface long-arc patterns.
Local model warning. Ollama with deepseek-r1:8b has a smaller context window. Settings above 10 may cause prompt truncation or degraded coaching quality. Use 5 for local Ollama, 10–20 for Claude / Gemini / GPT-5.6.
Patterns are aggregated across all games per player:
Core Metrics:
- Opening performance — win rate by opening name, split by color (All / White / Black)
- ACPL trend — per-game ACPL (±1000cp capped) averaged in weekly buckets with game data points
- Phase analysis — error frequency and ACPL in opening (moves 1–15), middlegame (16–30), endgame (31+)
- Rating performance — win rate vs. higher/lower/equal rated opponents
- Move quality distribution — percentage of excellent/good/inaccuracy/mistake/blunder moves
Advanced Metrics (Phase 1):
- Accuracy % — percentage of moves matching the engine's best move (higher = more precise play)
- Consistency Score — standard deviation of per-game ACPL; rated as Very consistent / Consistent / Variable / Highly variable; includes best and worst game ACPL
- Danger Zones — histogram of blunders and mistakes by move number range (5-move buckets), highlighting the move range with the highest error rate; reveals opening gaps, middlegame tactical weakness, or endgame fatigue
- Endgame Conversion — tracks how well the player converts advantages: winning endgames (>200cp at move 30) converted to wins, losing endgames saved/drawn, equal endgames outplayed; includes endgame reach percentage
- Time Control Performance — win rate, ACPL, and blunder rate per time format (bullet/blitz/rapid/daily); highlights best and weakest formats
Deeper Insights (Phase 2):
- Critical Position Success Rate — how often the player finds good moves in high-stakes moments (>200cp swing possible); also tracks capitalizing on opponent blunders with SVG gauge charts
- Comeback & Collapse Rate — comeback: was losing by >200cp but recovered to win/draw; collapse: was winning by >200cp but let it slip; measures mental resilience and composure
- Opening Quality Analysis — ACPL during opening phase (moves 1-15) per opening name; rates each as "Strong — keep playing", "Solid", "Average", or "Struggling"; sorted by worst ACPL to highlight areas needing improvement
- Tactical Miss Rate — positions where a tactic existed (>200cp advantage available) but the player missed it; broken down by game phase (opening/middlegame/endgame) with stacked bar chart
- Repertoire Consistency — measures how focused the player's opening choices are, split by color; tracks unique openings, top-3 concentration %, and rates as Very focused / Reasonably consistent / Scattered / No clear repertoire
Time Pressure Analysis:
- Time Trouble Rate — percentage of games where the player's clock dropped below 30 seconds
- Average Time per Move by Phase — how long the player spends per move in opening, middlegame, and endgame
- Blunder Rate Under Pressure — comparison of blunder frequency when clock is below 60s vs above 60s
- Time Management Score — composite 0–100 score based on time trouble frequency and pressure-induced blunder rate
Self-Analysis (v1.4.0):
- Fix Your Openings — openings where you lose most often (Your ELO Leaks) paired with openings where you win most often (Your Strengths); split by color (White / Black) with a "Study most recent" link to the relevant game.
- Trap Patterns — recognizes ~100 well-known named opening traps and groups them into "Your Arsenal" (traps you successfully use to win) and "You Fall For" (traps your opponents have used to beat you). Backed by the Lichess
chess-openingsCC0 database — covers Stafford Gambit, Elephant Gambit, Fried Liver Attack, Englund Gambit, Halloween Gambit, Cochrane, Wayward Queen Attack, Latvian Gambit, Damiano Defense, Traxler Counterattack, and more.
How traps are detected. Each game's first ~20 moves are matched against the curated trap library using longest-prefix matching — the deepest signature wins. The library is shallow (≤16 plies) and explicitly curated to focus on beginner traps, not deep mainline variations. To rebuild from the latest Lichess source:
python scripts/build_traps.py.
Hunter Mode (v1.4.1 + v1.4.2 + v1.4.4):
A separate page (/[player]/hunt) for opponent prep. Enter an opponent's username + platform (chess.com or lichess), and Arrakis pulls their last 6 months of public games (no Stockfish — fast even for big accounts) and shows:
- Their Weaknesses (red) — openings the opponent loses, broken down by White / Black. These are your hunting targets.
- Their Strengths (green) — openings the opponent wins. Avoid steering into these lines.
Click any row to see how the opponent actually played that opening: a step-through mini-board of an actual game, "Game N of 5" controls to flip through up to 5 representative games, an annotated move list with the opponent's deviation from book theory highlighted in orange, and a "Study this position on Lichess →" deep link.
Local accumulating cache (v1.4.4): Each refresh fetches only games newer than the last cached date — much faster on subsequent lookups, and your opponent history persists across sessions. Sliding window (default 6 months) prunes naturally; optional hard cap available. See features.hunter_lookback_months and features.hunter_max_games_per_opponent in config.yaml.
Profile JSON is cached per (opponent, platform) for 24 hours. Click Refresh on the prep view to force a re-fetch. Disable the feature globally by setting features.hunter_mode: false in config.yaml.
Deep Scan — Tactical Blind Spots (v1.20.0): the opening profile tells you what an opponent plays; Deep Scan tells you which tactics they miss. Click Deep Scan (Stockfish) on the prep view to run the 12 motif detectors over the opponent's last N games (config features.hunter_scan_games, default 20) at full depth. It surfaces a Tactical Blind Spots card — the themes the opponent misses most ("Bait pins — misses 82% of pin tactics") — using the same card as your own Tactical Themes. Opt-in only (it's a multi-minute engine pass, never automatic), runs as a background job, and is incremental: a re-scan only analyzes newly-fetched games. CLI: python main.py hunt-scan --opponent <name>.
Tournament Prep (v1.21.0): prep a whole event at once. The Tournament tab (/[player]/tournament) holds saved, named rosters of opponents. Add opponents (or use "Add to tournament" from the Hunt page), hit Prep Roster, and Arrakis aggregates the field:
- Opening targets — openings the field collectively loses to ("Prep the Italian — 5 of 8 opponents lose to it") — your attacking targets.
- Opening cautions — openings the field wins with ("Avoid the Najdorf — 4 win with it") — lines to dodge.
- Field blind spots — the tactical themes the (Deep-Scanned) field collectively misses, with explicit scan coverage.
Combined analysis is cache-only (no Stockfish) and fast; the field blind-spots panel fills in as you Deep-Scan individual opponents. Tune the shared-opening threshold + roster cap via features.tournament_min_shared / features.tournament_max_opponents. CLI: python main.py tournament-prep --id <n>.
Built with Next.js 16, React 19, shadcn/ui, Tailwind CSS, and Recharts. Fully mobile-responsive (320px+). Requires Node.js 18+.
# Terminal 1: Start the Python API backend
python main.py dashboard
# Terminal 2: Start the Next.js frontend
cd frontend && pnpm install && pnpm dev
# Open http://localhost:3000Player-scoped URLs: All player pages use bookmarkable URLs like /<username>/games, /<username>/patterns, etc. Switching players in the header navigates to the same section under the new player's URL.
- Player-scoped URLs — every player page is bookmarkable and shareable:
/<username>/games,/<username>/patterns,/<username>/reports - Multi-player switching — player selector in the header; switching players navigates to the same section under the new player's URL
- Player Hub — default landing page with Chess.com, Lichess, and FIDE profiles, tier badge, game counts, and direct links to external profiles
- Games list — filterable by result, time control, coaching status, month, platform (Chess.com / Lichess), and date range
- Platform icons — ♜ Chess.com / ♞ Lichess shown per game
- Game analysis — interactive chessboard, move-by-move eval chart (bars colored by move classification), color-coded move list for both player and opponent
- Move quality summary — per-game table with proportional bars for excellent/good/inaccuracy/mistake/blunder
- Opening analysis — LLM-generated assessment with opening name, quality rating, counter-move correctness, and tips
- On-demand coaching — provider dropdown (8 providers: Claude, ChatGPT, Gemini, Grok, Mistral, DeepSeek, Qwen, Ollama) with Coach Game button on each game, auto-refresh on completion
- Feedback to Player — personal letter with 3 actionable tips and growth mindset framing
- Game Comparison View — select two games and compare side-by-side with independent chessboards, eval charts, move quality summaries, and a comparison table highlighting differences in ACPL, excellent moves, blunders, and more
- LLM Trend Summary — AI-generated coaching narrative interpreting cross-game patterns (provider dropdown with all 8 providers, regenerate option)
- Overview stat cards (games, win rate, accuracy %, ACPL, consistency, vs higher-rated)
- ACPL Trend chart with clickable info modal
- Move Quality Distribution donut with percentages
- Danger Zones histogram (blunders/mistakes by move range)
- Phase Performance bar chart (opening/middlegame/endgame)
- Endgame Conversion rates (winning/losing/equal positions)
- Critical Position gauges (under pressure + capitalizing on opponent mistakes)
- Tactical Awareness bars (found vs missed by phase)
- Resilience & Composure (comeback rate + collapse rate)
- Repertoire Consistency (white/black focus scores with top-3 openings)
- Time Control Performance table (win%, ACPL, blunder% per format)
- Opening Quality Analysis table (ACPL per opening with verdict badges)
- Time Pressure Analysis: time management score, time trouble rate, avg time per move by phase (bar chart), blunder rate comparison under pressure vs comfortable
- Rating Progression Chart — interactive line chart showing rating over time with result-colored dots (green win / red loss / amber draw), time class filter (all/rapid/blitz/bullet/daily), 10-game moving average trend line, and info modal
- Opening Repertoire Tracker — ECO distribution bar chart, sortable opening table with win rate and trend indicators (improving/declining/stable), All/White/Black filter tabs, and focus areas panel highlighting openings that need work
- Opening Win Rate table (split by All / White / Black) with interactive Opening Explorer — click any opening to expand a chessboard showing the opening position with step-through move controls, plus a linked list of all games using that opening; board orientation flips on the Black tab
- Opening Book Integration — ECO code and opening name badge, moves annotated with green checkmarks (matches book theory) and orange markers (deviations), with "Book move: X" vs "Player played: Y" callouts; backed by the full Lichess
chess-openingsCC0 dataset (3,690 named openings A00–E99) - Info modals (ⓘ) with educational explanations for every pattern component
- Monthly/weekly coaching reports for coaches
- Time class filter tabs: Rapid (default), Daily, All — stats recompute per filter
- Summary cards: games, W/L/D, win rate, rating change
- Results by time control table
- Game-by-game results with clickable links to game detail pages, sorted most-recent-first, color-coded result badges (green win / red loss / amber draw)
- ACPL analysis with interpretation
- Move quality distribution
- Game phase analysis (opening/middlegame/endgame) with worst-phase highlighting
- Top improvement areas (auto-generated from blunder/mistake counts and phase weaknesses)
- Critical positions to review with game links (from LLM coaching data)
- Coaching recommendations (aggregated from individual game coaching)
- PDF export via
window.print()with print-optimized CSS
- Data Updates panel — one-click buttons on the dashboard to run the pipeline without CLI:
- Fetch New Games → Run Analysis → Update Insights shown as a visual flow with arrows
- Run All Steps option to chain all four (harvest → analyze → patterns → coach) in one click
- Provider selector for coaching step (all 8 providers with Cloud/Local grouping)
- Player selector to run for a specific player or all players
- Real-time progress bar, step indicators, and friendly result summaries
- Tooltips explaining what each step does
- Mobile responsive — viewport meta tag (
width=device-width, set via Next.jsViewportexport in the root layout) ensures breakpoints fire at 1:1 scale on phones; all pages adapt to mobile (320px+), tablet, and desktop; ChessBoard auto-sizes via ResizeObserver; tables progressively hide low-priority columns; nav bar scrolls horizontally; player selector shows first names on mobile; pinch-zoom stays available - Light/dark mode — toggle with theme button, persists across sessions
- Error boundaries — root error boundary, player-scoped error boundary, custom 404 page
- Aria-labels on player selector buttons and game table rows
- Live data — reads from SQLite directly, updates in real-time
Arrakis_Engine/
├── CLAUDE.md # Project context for Claude Code
├── README.md # This file
├── config.yaml.example # Template config (copy to config.yaml)
├── config.yaml # Your personal config (gitignored)
├── requirements.txt # Python dependencies
├── .env # API keys (gitignored)
├── .gitignore
├── pyproject.toml # pytest marker config (integration/live)
├── main.py # CLI entry point — all commands
├── src/
│ ├── models.py # SQLite schema (11 tables incl. opponent cache) + idempotent migrations
│ ├── harvester.py # Multi-platform game fetcher (Chess.com + Lichess)
│ ├── analyzer.py # Stockfish move-by-move analysis engine + clock extraction
│ ├── coach.py # LLM coaching layer (configurable history depth, 8 providers)
│ ├── llm_providers.py # Unified LLM provider abstraction (Claude, OpenAI, Gemini, Grok, Mistral, DeepSeek, Qwen, Ollama)
│ ├── tiers.py # Adaptive tier system (Beginner → Expert)
│ ├── patterns.py # 20 cross-game pattern metrics + Self-Analysis + LLM trend summaries + weakness escalation (v1.19.0)
│ ├── motifs.py # (v1.14.0/v1.17.0) 12 tactical-motif detectors
│ ├── journal.py # (v1.12.0) Journal CRUD + weakness_alert (v1.19.0)
│ ├── hunter.py # v1.4.1+ Hunter Mode (opponent prep, accumulating PGN cache) + Deep Scan (v1.20.0)
│ ├── tournament.py # (v1.21.0) Tournament Prep — roster CRUD + combined cross-opponent analysis
│ ├── dashboard_server.py # REST API server (GET/POST/PUT/DELETE)
│ ├── pipeline_state.py # In-memory pipeline task state (thread-safe)
│ ├── scheduler.py # Automated pipeline scheduler (harvest → analyze → patterns → coach)
│ └── report.py # Report generator (structured JSON + markdown export)
├── frontend/ # Next.js 16 + React 19 + shadcn/ui dashboard
│ ├── app/
│ │ ├── layout.tsx # Root layout (providers, header, nav)
│ │ ├── page.tsx # Home → redirect to /dashboard
│ │ ├── providers.tsx # ThemeProvider, PlayerProvider (syncs from URL)
│ │ ├── globals.css # Global styles + print CSS for PDF export
│ │ ├── error.tsx # Root error boundary
│ │ ├── not-found.tsx # Custom 404 page
│ │ ├── dashboard/page.tsx # All-players overview
│ │ └── [player]/ # Player-scoped dynamic routes
│ │ ├── error.tsx # Player-scoped error boundary
│ │ ├── games/page.tsx # Games list with filters + compare mode
│ │ ├── games/[id]/page.tsx # Game detail: board, eval, coaching
│ │ ├── games/compare/page.tsx # Side-by-side game comparison
│ │ ├── patterns/page.tsx # Pattern analytics + AI trend summary
│ │ ├── journal/page.tsx # (v1.10.0) Threaded coaching diary
│ │ ├── hunt/page.tsx # (v1.4.1+) Opponent prep + Deep Scan (v1.20.0)
│ │ ├── tournament/page.tsx # (v1.21.0) Tournament Prep — rosters + combined analysis
│ │ └── reports/page.tsx # Coaching reports (Rapid/Daily/All + PDF)
│ ├── components/
│ │ ├── app-header.tsx # Title bar + player selector
│ │ ├── nav-bar.tsx # Navigation (player-scoped links)
│ │ ├── player-selector.tsx# Player switching (navigates URLs)
│ │ ├── player-card.tsx # Player profile card for dashboard
│ │ ├── report-view.tsx # Report renderer (time class filter + game links)
│ │ ├── games-table.tsx # Clickable game rows
│ │ ├── games-filters.tsx # Result, time, coaching, month, platform filters
│ │ ├── tier-badge.tsx # Color-coded tier display
│ │ ├── theme-toggle.tsx # Dark/light mode toggle
│ │ ├── pipeline-control-panel.tsx # Data Updates panel (harvest/analyze/patterns/coach)
│ │ ├── game-detail/ # ChessBoard, EvalChart, MoveList, CoachingPanels, ComparisonSummary
│ │ ├── patterns/ # visualization components + MotifThemes (v1.15.0) + Self-Analysis
│ │ ├── journal/ # (v1.11.0) TimelineThread, DayGroup, EntryCard, AddNoteForm
│ │ ├── hunter/ # OpponentSearch + TargetedPrep (v1.4.1+) + OpponentBlindSpots (v1.20.0) + AddToTournament (v1.21.0)
│ │ ├── tournament/ # (v1.21.0) OpeningTargets + OpponentCard
│ │ ├── settings/ # Players, Analysis, ApiKeys, Coaching sections
│ │ └── ui/ # shadcn/ui primitives (card, table, button, etc.)
│ ├── hooks/ # useChessNavigation (currentFen, endFen, moves), usePipeline, useCoaching
│ │ └── __tests__/ # (v1.6.0) Vitest specs for use-chess-navigation (clock-comment guard, boundaries)
│ ├── lib/ # API client (api.ts), types (types.ts), providers (providers.ts), utils
│ │ ├── chess/ # (v1.6.0) Shared chess helpers: parseMoveText, lichessAnalysisUrl, opening matching
│ │ ├── motifs.ts # (v1.15.0) Shared MOTIF_LABELS — 12 emoji+label pairs
│ │ ├── summary.ts # (v1.14.1) parseTrendSummary — JSON-array tolerant
│ │ └── chart-format.ts # (v1.18.3) Date-axis helpers for the time-scale chart
│ └── public/data/
│ ├── openings.json # 3,690-entry Lichess CC0 opening database
│ └── traps.json # 1,475-entry Lichess trap/gambit/attack library (v1.18.0)
├── scripts/
│ └── build_traps.py # Rebuilds openings.json + traps.json from Lichess CC0
├── docs/
│ ├── architecture.md # Tracked: contributor architecture reference
│ └── screenshots/ # Architecture diagram and screenshots
├── tests/ # Backend test suite (779 tests across 3 tiers)
│ ├── conftest.py # Shared fixtures (db, player, stockfish, llm)
│ ├── test_models.py # Schema, ensure_player, migrations, _slugify (v1.16.1)
│ ├── test_harvester.py
│ ├── test_analyzer.py
│ ├── test_motifs.py # v1.14.0/v1.17.0 — 12 motif detectors + calibration
│ ├── test_coach.py
│ ├── test_patterns.py # patterns + motif summary + phase split + prompt wiring
│ ├── test_journal.py # v1.12.0 Journal helpers
│ ├── test_tiers.py
│ ├── test_report.py
│ ├── test_dashboard_server.py # API endpoints + slug resolver + static guard
│ ├── test_main_cli.py # v1.15.3 — CLI dispatch + slug resolution
│ ├── test_llm_providers.py # Provider registry, model resolution, dispatch
│ ├── test_scheduler.py # Pipeline orchestration, cancel, provider passthrough
│ ├── test_loss_openings.py # v1.4.0 Self-Analysis aggregation
│ ├── test_trap_matcher.py # v1.4.0 trap library + matching + recent_game_ids
│ ├── test_hunter.py # v1.4.1+ Hunter Mode (cache, accumulation, reps) + Deep Scan (v1.20.0)
│ ├── test_tournament.py # (v1.21.0) Tournament Prep CRUD + combined analysis
│ ├── test_dev_runner.py # v1.5.0 `serve` subprocess orchestration
│ ├── test_analyzer_integration.py # Stockfish integration (pytest -m integration)
│ ├── test_coach_live.py # LLM API live tests (pytest -m live)
│ └── test_pipeline_e2e.py # Full pipeline E2E (requires both)
├── data/
│ └── chess_coach.db # SQLite database (auto-created, gitignored)
└── reports/ # Generated coach reports (gitignored)
| Table | Purpose |
|---|---|
players |
Player profiles (username = chess.com handle, slug = URL/API/CLI id (v1.16.1), display name, age, rating, FIDE ID + three FIDE ratings Classical/Rapid/Blitz (v1.26.0)) |
games |
Game records with PGN, ratings, result, platform, ACPL, analysis/coaching status (coaching status is pending/complete/error/skipped — skipped = analysed but no moves to coach, v1.28.1) + coaching_attempts (v1.28.0) / analysis_attempts (v1.29.0) bounded-retry counters |
move_analysis |
Per-move Stockfish evaluation (capped centipawn, win prob, classification, clock_seconds, motifs_json v1.14.0) |
game_coaching |
LLM-generated coaching output per game (narrative, feedback, opening analysis, coaching_meta) |
player_patterns |
Aggregated pattern statistics per player per period (incl. Self-Analysis v1.4.0 + motif_summary v1.15.0) |
journal_entries (v1.10.0) |
Chronological coaching diary — Recent Form Reviews + Parent Notes + Priority Weakness alerts (v1.19.0) |
opponent_cache (v1.4.1) |
Hunter Mode profile JSON cache, 24h TTL |
opponent_games (v1.4.4) |
Hunter Mode accumulating PGN cache, sliding-window pruned (+ per-game motifs_json/analyzed_at for Deep Scan, v1.20.0) |
tournaments / tournament_opponents (v1.21.0) |
Tournament Prep — player-scoped named rosters of opponents |
pipeline_lock |
Single-task lock coordinating harvest/analyze/coach/patterns across CLI, scheduler, and dashboard |
~1014 tests total — 779 backend (pytest) + 235 frontend (Vitest). Backend tests are organized into three tiers using pytest markers; integration (-m integration, Stockfish) and live (-m live, LLM key) tiers are excluded by default. Frontend tests run in a few seconds and cover the chess + chart + motif helper libraries, the use-chess-navigation hook, and the component suites.
# Unit tests only (default — fast, no external dependencies)
python -m pytest tests/ -v
# → 779 tests in ~30s, all mocked
# Stockfish integration tests (requires Stockfish binary)
python -m pytest tests/ -m integration -v
# → real Stockfish analysis on Scholar's Mate
# LLM API live tests (requires ARRAKIS_ANTHROPIC_API_KEY or ARRAKIS_OPENAI_API_KEY)
python -m pytest tests/ -m live -v
# → real coaching + structured-output + motif-citation compliance (~$0.30 per run)
# Frontend (Vitest)
cd frontend && npx vitest run
# → 235 tests in ~3s
cd frontend && npx next build # type-checkUnit tests (779 backend tests — all mocked, no external dependencies):
| File | Tests | Coverage |
|---|---|---|
test_models.py |
16 | Schema init, player upsert, constraints, PGN opponent extraction, DB path creation, migrations |
test_harvester.py |
20 | Chess.com + Lichess parsing: player side, result, time control, date, game URL, deduplication, platform filtering |
test_analyzer.py |
19 | Win probability formula, move classification boundaries, eval capping (±1000cp), PovScore→centipawn conversion |
test_coach.py |
18 | Move formatting, smart truncation (short vs 80+ move games), critical moments, JSON parsing, Claude/OpenAI provider switching, batch limit, DB status transitions |
test_patterns.py |
38 | Game phase classification, results aggregation, rating performance, accuracy, consistency, danger zones, endgame conversion, comeback/collapse detection, opening ACPL, tactical misses, repertoire consistency, opening name extraction |
test_tiers.py |
21 | Rating→tier boundary mapping (Beginner→Expert), tier-specific move thresholds, config validation |
test_report.py |
9 | Report generation (weekly/monthly), ACPL interpretation thresholds, time control tables, missing data handling |
test_dashboard_server.py |
42 | HTTP endpoints (players/games/status/patterns/hunt/tournament), filtering (result, time class, date range, player), CORS headers, 404 |
test_hunter.py |
49 | Hunter Mode opponent profiles, cache/accumulation, Deep Scan aggregation + incremental skip (v1.20.0) |
test_tournament.py |
15 | Tournament Prep roster CRUD + combined opening/blind-spot analysis (v1.21.0) |
test_llm_providers.py |
52 | Provider registry validation (8 providers), thinking tag stripping, model resolution (explicit/config/default), provider dispatch (Anthropic/OpenAI/Google/Mistral/Ollama), API key detection, availability listing |
test_scheduler.py |
6 | 4-step pipeline execution (harvest→analyze→patterns→coach), player filter passthrough, cancel event propagation, provider passthrough, progress update verification (1/4–4/4), Stockfish validation |
Stockfish integration tests (9 tests — requires Stockfish binary):
| File | Tests | Coverage |
|---|---|---|
test_analyzer_integration.py |
7 | End-to-end Stockfish analysis on Scholar's Mate: move row creation, eval sanity checks (opening ~0cp, mate→±1000cp), ACPL storage, move classifications, batch processing, stuck game recovery, empty PGN handling |
test_hunter.py |
1 | Deep-scan an opponent's game through the real engine (Hunter Mode) |
test_pipeline_e2e.py |
1 | (also -m live) counted in the integration tier — see Full pipeline E2E below |
The stockfish_path fixture auto-resolves from config.yaml → STOCKFISH_PATH env var → which stockfish. Tests skip with a clear message if Stockfish is not found.
LLM API live tests (12 tests — requires API key, ~$0.30/run):
| File | Tests | Coverage |
|---|---|---|
test_coach_live.py |
11 | Real LLM coaching: valid JSON response, all required keys present (narrative, key_lesson, practical_focus, critical_moments, coach_notes), DB storage with provider:model format, error handling |
The llm_provider fixture checks for ARRAKIS_ANTHROPIC_API_KEY first, falls back to ARRAKIS_OPENAI_API_KEY. Tests skip if neither is set.
Full pipeline E2E (1 test — requires both Stockfish + API key):
| File | Tests | Coverage |
|---|---|---|
test_pipeline_e2e.py |
1 | Insert game → Stockfish analysis → LLM coaching → verify complete status, move rows, and valid coaching JSON |
235 tests, few-second full run. No external dependencies; jsdom + Testing Library + @testing-library/jest-dom. Covers the chess + chart-format + motif helper libraries, the use-chess-navigation hook, and the Patterns / Journal / game-detail / settings / layout component suites.
cd frontend
pnpm test:run # single-shot (used by CI)
pnpm test # watch mode| File | Tests | Coverage |
|---|---|---|
lib/chess/__tests__/pgn.test.ts |
11 | parseMoveText — numeric prefixes, result markers (1-0/0-1/1/2-1/2/*), whitespace, empty input |
lib/chess/__tests__/openings.test.ts |
18 | normalizeOpeningName (ellipsis, punctuation, whitespace), findCanonicalLine (exact / normalized / longest-prefix matching), findDeviationIndex (first-diff index, -1 sentinel) |
lib/chess/__tests__/lichess.test.ts |
6 | v1.4.5 regression lock — /analysis/standard/{FEN} form, forbids ?pgn= and ?fen= |
hooks/__tests__/use-chess-navigation.test.ts |
17 | Empty / invalid PGN safety, v1.4.5 clock-comment leak guard ({[%clk ...]} must not leak into moves), FEN-length invariant, boundary navigation, board orientation, keyboard handler with input/textarea focus guard |
components/hunter/__tests__/targeted-prep.test.tsx |
5 | Opponent header + weakness row, click-to-expand mounts mini-board, Lichess URL form, Refresh callback, empty-state |
components/patterns/__tests__/you-fall-for.test.tsx |
4 | Trap rows render, expansion shows recent-game links to /<player>/games/<id>, Lichess URL form, empty-state |
components/patterns/__tests__/opening-explorer.test.tsx |
5 | Game-list links, SAN move rendering via parseMoveText, ECO badge after book fetch, mini-board mount, W/L/D badges |
CI runs pnpm test:run automatically between install and build on every push and PR (see .github/workflows/ci.yml).
"unable to open database file"
The data/ directory is created automatically. If you see this error, check that you're running commands from the project root directory.
"No such file or directory: '/usr/local/bin/stockfish'"
Update the stockfish.path in config.yaml to match your installation. Use which stockfish to find the correct path.
"ARRAKIS_ANTHROPIC_API_KEY not set" (or any provider key)
Create a .env file in the project root with your API keys (see Configure API keys). You only need keys for the providers you use. Ollama requires no API key.
"Cannot connect to Ollama"
Ollama must be running before you start coaching. Start it with ollama serve in a separate terminal. If using a non-default URL, update ollama_base_url in config.yaml.
Ollama coaching is slow
Local models are slower than cloud APIs. The 8B model (~30 tok/s on M3 Max) takes ~30–60s per game. For faster results, use a cloud provider. For better local quality, try a larger model: ollama pull deepseek-r1:14b or deepseek-r1:32b (requires more RAM).
Analysis is very slow
Homebrew Stockfish runs at ~4.4M nodes/sec vs ~9–14M nodes/sec for a source-compiled binary. Consider compiling from source (see Install Stockfish). Each move has a 10-second time limit to prevent hanging. You can also reduce depth in config.yaml — depth 18 is ~3x faster with minimal loss in accuracy for beginner-to-intermediate players.
OpenAI 429 rate limit errors
Your API tier has a tokens-per-minute cap (e.g. 10,000 TPM on free tier). Use --limit 5 to batch and allow the 10-second delay between calls. Upgrading your OpenAI plan raises the limit. Alternatively, use --provider claude.
Games show "error" analysis or coaching status
As of v1.28.0/v1.29.0, transient failures retry automatically — a failed
game is re-attempted on the next analyze/coach/run-all (up to 3 times each
for analysis and coaching) rather than being stranded. A game that exhausts its
retries is surfaced on the dashboard's Data Updates panel; clear it with:
- Coaching — open the game and click Coach Game (resets its retry
budget), or run
python main.py coach. - Analysis —
POST /api/pipeline/reset-analysis-errors(re-arms exhausted games), thenpython main.py analyze.
A game with no moves (abandoned before either side moved) is not an error —
it resolves to the skipped coaching status (analysed, nothing to coach).
"database is locked"
SQLite allows one writer at a time. Since v1.31.0 the CLI participates in the
same single-task lock as the dashboard and scheduler, so python main.py analyze/coach/patterns/run-all will refuse to start (exit 1) while another task
is running — just wait for it to finish. Status polls and other reads run
concurrently without issue (WAL mode).
This project wouldn't exist without the generous work of others:
- Chess coaches and parents who tested early versions, stress-tested the pipeline with real game data, and gave invaluable feedback on what makes coaching advice actually useful for young players.
- Stockfish (official-stockfish/Stockfish, GPL-3.0) — the open-source chess engine that powers all per-move analysis in this project. Decades of engineering distilled into a single binary.
- python-chess by Niklas Fiekas (GPL-3.0) — the library that makes PGN parsing and board manipulation effortless in Python.
- Lichess
chess-openings(lichess-org/chess-openings, CC0 / Public Domain) — the curated opening database (3,690 named openings) that backs bothfrontend/public/data/openings.jsonand the trap-detection library atfrontend/public/data/traps.json. Without this dataset, named-trap detection (Stafford, Fried Liver, Englund, Halloween, etc.) would not be possible. - Lichess — for the win probability formula and the PGN export API that powers Lichess game harvesting and Hunter Mode.
- Next.js, shadcn/ui, and Recharts — the frontend stack that made the dashboard possible.
- The reasoning model ecosystem — Anthropic, OpenAI, Google, xAI, Mistral, DeepSeek, Alibaba (Qwen), and the Ollama project — for building the AI models that turn raw analysis into real coaching.
ArrakisEngine is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
You are free to use, modify, and distribute this software under the terms of the AGPL-3.0. If you modify ArrakisEngine and provide it as a service over a network, you must make your modified source code available under the same license.
For commercial licensing inquiries (e.g. proprietary use without AGPL obligations), contact bleongcw@gmail.com.














