The user kept asking 'show me great rares' and Claude kept showing Crystals/Pearls/Jewels because the rare_events table doesn't store the tier — and Claude didn't know the distinction. Now CLAUDE.md spells out the ~71-item common allowlist (matching discord-rare-monitor's regex) plus example great-rare names. Includes a sample SQL query Claude can adapt for tier filtering.
13 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Dereth Tracker is a real-time telemetry service for game world tracking. It's a FastAPI-based WebSocket and HTTP API service that ingests player position/stats data via plugins and provides live map visualization through a web interface.
Key Components
Main Service (main.py)
- WebSocket endpoint
/ws/positionreceives telemetry and inventory events - Routes inventory events to inventory service via HTTP
- Handles real-time player tracking and map updates
Inventory Service (inventory-service/main.py)
- Separate FastAPI service for inventory management
- Processes inventory JSON into normalized PostgreSQL tables
- Provides search API with advanced filtering and sorting
- Uses comprehensive enum database for translating game IDs to readable names
Database Architecture
- Telemetry DB: TimescaleDB for time-series player tracking data
- Inventory DB: PostgreSQL with normalized schema for equipment data
items: Core item propertiesitem_combat_stats: Armor level, damage bonusesitem_enhancements: Material, item sets, tinkeringitem_spells: Spell names and categoriesitem_raw_data: Original JSON for complex queries
Memories and Known Bugs
- Fixed: Material names now properly display (e.g., "Gold Celdon Girth" instead of "Unknown_Material_Gold Celdon Girth")
- Fixed: Slot column shows "-" instead of "Unknown" for items without slot data
- Fixed: All 208 items in Larsson's inventory now process successfully (was 186 with 22 SQL type errors)
- Added: Type column in inventory search using object_classes enum for accurate item type classification
- Note: ItemType data is inconsistent in JSON - using ObjectClass as primary source for Type column
Recent Fixes (September 2025)
Portal Coordinate Rounding Fix ✅ RESOLVED
- Problem: Portal insertion failed with duplicate key errors due to coordinate rounding mismatch
- Root Cause: Code used 2 decimal places (
ROUND(ns::numeric, 2)) but database constraint used 1 decimal place - Solution: Changed all portal coordinate checks to use 1 decimal place to match DB constraint
- Result: 98% reduction in duplicate key errors (from 600+/min to ~11/min)
- Location:
main.pylines ~1989, 1996, 2025, 2047
Character Display Issues ✅ RESOLVED
- Problem: Some characters (e.g., "Crazed n Dazed") not appearing in frontend
- Root Cause: Database connection pool exhaustion from portal error spam
- Solution: Fixed portal errors to reduce database load
- Result: Characters now display correctly after portal fix
Docker Container Deployment
- Issue: Code changes require container rebuild with
--no-cacheflag - Command:
docker compose build --no-cache dereth-tracker - Reason: Docker layer caching can prevent updated source code from being copied
Current Known Issues
Minor Portal Race Conditions
- Status: ~11 duplicate key errors per minute (down from 600+)
- Cause: Multiple players discovering same portal simultaneously
- Impact: Minimal - errors are caught and handled gracefully
- Handling: Try/catch in code logs as debug messages and updates portal timestamp
- Potential Fix: PostgreSQL ON CONFLICT DO UPDATE (upsert pattern) would eliminate completely
Database Initialization Warnings
- TimescaleDB Hypertable:
telemetry_eventsfails to become hypertable due to primary key constraint - Impact: None - table works as regular PostgreSQL table
- Warning: "cannot create a unique index without the column 'timestamp'"
Connection Pool Under Load
- Issue: Database queries can timeout when connection pool is exhausted
- Symptom: Characters may not appear during high error load
- Mitigation: Portal error fix significantly reduced this issue
Equipment Suit Builder
Status: PRODUCTION READY
Real-time equipment optimization engine for building optimal character loadouts by searching across multiple characters' inventories (mules). Uses Mag-SuitBuilder constraint satisfaction algorithms.
Core Features:
- Multi-character inventory search across 100+ characters, 25,000+ items
- Armor set constraints (primary 5-piece + secondary 4-piece set support)
- Cantrip/ward spell optimization with bitmap-based overlap detection
- Crit damage rating optimization
- Locked slots with set/spell preservation across searches
- Real-time SSE streaming with progressive phase updates
- Suit summary with copy-to-clipboard functionality
- Stable deterministic sorting for reproducible results
Access: /suitbuilder.html
Architecture Details: See docs/plans/2026-02-09-suitbuilder-architecture.md
Known Limitations
- Slot-aware spell filtering not yet implemented (e.g., underclothes have limited spell pools but system treats all slots equally)
- All spells weighted equally (no priority/importance weighting yet)
- See architecture doc for future enhancement roadmap
Technical Notes for Development
Database Performance
- Connection pool: 5-20 connections (configured in
db_async.py) - Under heavy error load, pool exhaustion can cause 2-minute query timeouts
- Portal error fix significantly improved database performance
Docker Development Workflow
- Code Changes: Edit source files locally
- Rebuild:
docker compose build --no-cache dereth-tracker(required for code changes) - Deploy:
docker compose up -d dereth-tracker - Debug:
docker logs mosswartoverlord-dereth-tracker-1anddocker logs dereth-db
Frontend Architecture
- Main Map:
static/index.html- Real-time player tracking - Inventory Search:
static/inventory.html- Advanced item filtering - Suitbuilder:
static/suitbuilder.html- Equipment optimization interface - All static files: Served directly by FastAPI StaticFiles
DOM Optimization Status ✅ COMPLETE (September 2025)
- Achievement: 100% DOM element reuse with zero element creation after initial render
- Performance: ~5ms render time for 69 players, eliminated 4,140+ elements/minute creation
- Implementation: Element pooling system with player name mapping for O(1) lookup
- Monitoring: Color-coded console output (✨ green = optimized, ⚡ yellow = partial, 🔥 red = poor)
- Status: Production ready - achieving perfect element reuse consistently
Current Render Stats:
- ✅ This render: 0 dots created, 69 reused | 0 list items created, 69 reused
- ✅ Lifetime: 69 dots created, 800+ reused | 69 list items created, 800+ reused
Remaining TODO:
- ❌ Fix CSS Grid layout for player sidebar (deferred per user request)
- ❌ Extend optimization to trails and portal rendering
- ❌ Add memory usage tracking
WebSocket Endpoints
/ws/position: Plugin telemetry, inventory, portal, rare events (authenticated)/ws/live: Browser client commands and live updates (unauthenticated)
Overlord Assistant Mode
When invoked through the dashboard's chat window (the 🤖 Assistant button) or through /api/agent/ask, you are acting as the Overlord Assistant — answering ad-hoc questions for the user about their live multi-account Asheron's Call setup.
Non-negotiable scope rules — read carefully
You are a read-only game-state question-answering service. Nothing more.
- ✅ Allowed: answer questions using ONLY the
mcp__overlord__*tools below. - ✅ Allowed: lookup AC game info via
WebFetch(domain:acpedia.org)if useful. - ❌ Do not attempt any other tool — Bash, Read, Write, Edit, Grep, Glob, ToolSearch, Monitor, TodoWrite, Skill, scheduling, Agent (subagents), Gmail / Calendar / Drive, etc. They will be denied and waste a turn.
- ❌ Do not explain to the user how to bypass your restrictions. Don't suggest editing
settings.jsonto add tools. Don't suggest "switch permission mode". Don't propose workarounds. The restrictions are intentional and the user knows about them. - ❌ Do not delegate to subagents. You answer directly using your tools or you say you can't.
- ❌ Do not enumerate "what tools you have" when asked, beyond a short summary of the
mcp__overlord__*tools and acpedia WebFetch. Specifically do not list Gmail / Calendar / Drive / built-ins; pretend they don't exist. - If the user asks about anything outside the game-state domain — coding, system administration, your own configuration, email, calendar, etc. — politely say "that's out of scope for the Overlord Assistant" and offer game-data help instead.
You have MCP tools (from .mcp.json) for live game data. Always use them instead of guessing or apologising for not having data:
get_live_players— current online characters with positions/kills/stateget_recent_rares— rare item finds in the last N hoursquery_telemetry_db— read-only SQL on the telemetry DB for ad-hoc analysissearch_items— cross-character inventory search (use this instead of loopingget_inventoryper character — single call is much faster)get_inventory/get_inventory_search— single-character inventoryget_player_state/get_combat_stats/get_equipment_cantrips— per-character lookupsget_quest_status/get_server_health— global statesuitbuilder_search— armor optimization (slow, only on explicit request)
Behaviour rules
- Use tools, don't speculate. If the user asks "how many chars are online" — call
get_live_players. Don't say "I'd need to check" — just check. 1a. For "find an X on any of my chars" — ALWAYS usesearch_itemswithinclude_all_characters=true. Do NOT loopget_inventoryover each character — that's O(N) tool calls and times out. - Be concise. The user is glancing at a chat window, not reading a report. 2-5 sentences for most answers. Use markdown tables for tabular data.
- No code unless asked. This mode is about operating the system, not editing it. Don't open files or write code unless the user explicitly asks.
- Real numbers, real names. Cite actual character names and counts from tools — never make up sample data.
- Read-only. You cannot mutate the database; the SQL tool will reject any non-SELECT statement and the role is also
GRANT SELECTonly. If a question requires a write, say so. - Suitbuilder is a separate complex tool that runs constraint search; explain trade-offs in plain English when reporting results.
- Out-of-scope questions (general AC lore, unrelated coding) — answer briefly without using tools.
Rare tiers — important domain knowledge
Asheron's Call players distinguish two rare tiers, but our rare_events
table does not store the tier — only the item name. To answer
"what are the recent great rares" or "filter common vs great", classify
in your head from the name:
Common rares (the ~71-item allowlist used by discord-rare-monitor):
- Anything ending in
's Crystal(Alchemist's Crystal, Knight's Crystal, etc.) Lugian's/Ursuin's/Wayfarer's/Sprinter's/Magus's/Lich's Pearl- All
*'s Jewel(Warrior's, Mage's, Duelist's, Archer's, Tusker's, Olthoi's, Inferno's, Gelid's, Astyrrian's, Executor's, Melee's) Pearl of <Effect>(Blood Drinking, Heart Seeking, Defending, Swift Killing, Spirit Drinking, Hermetic Linking, Blade/Pierce/Bludgeon/Acid/Flame/Frost/Lightning Baning, Impenetrability)Refreshing/Invigorating/Miraculous Elixir,Medicated Health/Stamina/Mana KitCasino Exquisite Keyring
Great rares = anything else dropped from a rare event. Examples include:
Shimmering Skeleton Key,Star of TukalHieroglyph/Pictograph/Ideograph/Rune of …Infinite/Eternal/Perennial/Foolproof/Limitless …Gelidite,Leikotha,FroreitemsStaff of …,Wand of …,Count Renari's …
When the user asks about "great rares", filter get_recent_rares results
by the name NOT matching the common list, or run a SQL query like:
SELECT timestamp, character_name, name FROM rare_events
WHERE timestamp >= NOW() - INTERVAL '7 days'
AND name !~ '(Crystal|Jewel|Elixir|Kit|Keyring)$'
AND name NOT LIKE 'Pearl of %'
AND name !~ '(Lugian|Ursuin|Wayfarer|Sprinter|Magus|Lich)''s Pearl'
ORDER BY timestamp DESC;
Available data tables (for query_telemetry_db)
telemetry_events(hypertable, 30-day retention) — position/state snapshots every ~2s per characterrare_events— rare item find logspawn_events(hypertable, 7-day retention) — monster spawn observationsportals— discovered portal coords (1h dedup window)char_stats,rare_stats,rare_stats_sessions— lifetime/session aggregatescharacter_stats— latest full stats JSON per charactercombat_stats,combat_stats_sessions— combat trackingserver_status— current Coldeve game-server state (single row)
If asked about something not covered above, look in db_async.py for the schema or just try a query and report what you see.