Workflow

AI_TTRPG

AI-TTRPG · D&D 5e Rules Engine as a Deterministic, Auditable Tool

Python Updated May 16, 2026 View on GitHub →

Agent Pipeline

Four-role pipeline for AI-assisted software engineering

01

Architect

Asks clarifying questions and explores alternatives before any code is written. Produces Architecture Decision Records (ADRs) and detailed specs.

ADRsSpecsConstraints
02

Planner

Takes ADRs and specs as input. Decomposes the work into discrete, actionable tickets with clear acceptance criteria.

TicketsMilestonesCriteria
03

Executor

Implements tickets following established conventions. Always proposes a plan before touching the codebase.

CodePRsPlan docs
04

QA Tester

Writes automated tests covering acceptance criteria, edge cases, and regressions after a feature is implemented. Does not modify feature code.

TestsCoverage
05

Reviewer

Validates code against the original acceptance criteria. Flags issues back to the Executor and provides final approval.

FeedbackApprovalNotes
06

System Architect

Maps components, tiers, connections, and protocols. Produces or updates architecture.yaml when the system shape changes.

architecture.yaml
07

Custodian

Keeps CLAUDE.md under 200 lines, routes content to external files, and cleans up stale references.

CLAUDE.md updates
08

Summarizer

Reads tickets, ADRs, specs, and git history to produce executive summaries of completed sprints or features. Writes output to summaries/.

Executive summaries
Interactive workflow graph coming soon

System Architecture

Real-time voice-powered D&D Game Master that resolves combat mathematically while narrating outcomes naturally

Player Browser

client

Web client for real-time bidirectional audio with the Game Master

Daily WebRTC Client
Audio Capture Microphone input streamed via WebRTC
Audio Playback Receives synthesized GM speech
UI Lock Listener Receives UI_LOCK signals to block input during state changes

Voice Pipeline

service

Pipecat-orchestrated pipeline that transcribes speech, classifies intent, calls the LLM, and synthesizes a spoken response

Pipecat 0.0.108 (Python)
Standard Pipeline Deepgram STT -> Semantic Router -> LLM -> OpenAI TTS (~4.5s TTFB, multi-provider)
Realtime Pipeline OpenAI Realtime API — unified STT + LLM + TTS over WebSocket (~400ms TTFB)
LLM Factory Provider abstraction — swap Claude / GPT / Gemini via LLM_PROVIDER env var
UI Lock Processor Emits UI_LOCK signal before game-state-changing actions begin
Pipeline Timing Latency instrumentation for each pipeline stage

Semantic Router

engine

Vector similarity classifier that routes player utterances to GAME_ACTION or GENERAL intent in under 15ms

Redis Vector + fastembed
Intent Embeddings Pre-seeded vector index of canonical intent phrases
Similarity Search Nearest-neighbour lookup to classify free-form speech

MCP Bridge

service

SSE-based client that forwards LLM tool calls from the voice pipeline to the rules engine over MCP protocol

SSE (Server-Sent Events)
Tool Call Proxy Translates LLM function calls into MCP tool invocations
SSE Connection Persistent event stream to the FastMCP server

Rules Engine

engine

Deterministic D&D 5e rules server — all dice rolls and damage calculations happen here so the AI cannot hallucinate outcomes; manages game lifecycle (create, load) and guards tool calls against no-active-game state

FastMCP + dnd-5e-core
dnd-5e-core Library Core ruleset for attack rolls, damage, modifiers
Combat Resolver Melee attack resolution (ranged and spells planned); returns error if no active game — call dnd_load_game first
OpenTelemetry Traces Audit trail of every rule invocation and result
dnd_new_game tool Accepts campaign_name, generates UUIDv4 game_id, seeds SQLite from GAME_STATE template (3 players + 4 NPCs) in one transaction, returns {status, game_id, campaign_name}
dnd_load_game tool Accepts game_id, sets active game on DEFAULT_STORE via load_game(), returns {status, game_id, entity_count} or {error} if not found
dnd_resolve_melee_attack tool Resolves a melee attack roll and damage using 5e rules; requires an active game
dnd_create_character tool Creates and persists a new player character entity
dnd_load_monster tool Loads a monster stat block into the active game as an NPC entity
dnd_get_magic_item tool Retrieves magic item properties and description
Auto-load Lifespan Hook At startup, if DEFAULT_STORE is SQLiteGameStateStore and exactly one game exists, auto-loads it and logs the game_id; logs informational message and starts with no active game when zero or multiple games are present

Game State

data

Pluggable store for characters, NPCs, and combat log — backend selected at startup via GAME_DB_PATH env var (SQLite in production, in-memory for local dev and CI)

Python (Protocol + InMemoryGameStateStore + SQLiteGameStateStore)
Character Data Player character stats, HP, equipment
NPC Data Enemy and NPC stat blocks for active encounters
Combat Log Append-only ordered event stream per game, keyed by UUIDv4 game_id (ADR-0004)
GameStateStore Protocol Full read/write/persist interface — entity access, save/load game, combat log (Tickets 0001-0002)
MigrationRunner Lightweight stdlib-only SQLite migration runner; applies numbered .sql files in lexicographic order, idempotent (db.py)
Initial Schema (0001) Four tables: games (includes campaign_name TEXT NOT NULL), entities, combat_log, schema_migrations — entity/event payloads stored as JSON blobs (migrations/0001_initial_schema.sql)
SQLiteGameStateStore Concrete persistence implementation behind GameStateStore Protocol; active when GAME_DB_PATH env var is set — implements all 8 Protocol methods plus create_game(game_id, campaign_name, template) which atomically inserts a games row and entity rows from a {players, npcs} template then calls load_game; selected via _make_default_store() factory in combat.py (Tickets 0004-0005)

Hub API

service

REST API for game session management — CRUD operations on games, entities, and combat logs for the web hub UI (ADR-0005); real-time SSE event streaming (ADR-0007)

FastAPI (Python)
Games Router REST endpoints: GET/POST /api/games, GET/DELETE /api/games/{id}
Entities Router GET /api/games/{id}/entities, GET /api/games/{id}/entities/{eid}, POST /api/games/{id}/entities/{eid}/level-up (applies ASI with 8 validation rules: players only, valid ASI levels, no double-spend, sum==2, no negatives, no score>20)
Combat Log Router GET /api/games/{id}/combat-log with pagination
HubStore Read-oriented SQLite access layer with busy_timeout=5000ms for WAL-mode concurrent access; read-time entity defaults (ability scores default to 10, items/asi_levels_claimed to []); apply_level_up() method with atomic transactions
Sessions Router POST/GET/DELETE /api/sessions — creates Daily.co rooms, spawns bot.py subprocess with GAME_ID, monitors lifecycle
EventBus In-process asyncio.Queue that fans out events from EventPoller to multiple SSE connections without blocking
SSE Endpoint GET /api/games/{gameId}/events — Server-Sent Events stream to browser; yields events read from EventBus for a game, with text/event-stream MIME type

Event Poller

service

Background task that polls event_outbox every 100ms and fans out persisted events to the EventBus for real-time synchronization (ADR-0007)

Python asyncio
Polling Loop Async task that runs every 100ms, queries event_outbox table for unseen events
Event Unmarshaling Deserializes JSON event payloads and instantiates typed event objects
EventBus Publisher Puts deserialized events onto the EventBus queue for active SSE connections

Event Outbox Table

data

SQLite table in game.db that stores events inserted by MCP tools in the same transaction as entity mutations; acts as the single source of truth for propagating changes to clients (ADR-0007)

SQLite
Events Table Schema Columns: id (UUID primary key), game_id (foreign key), event_type (string), payload (JSON blob), created_at (timestamp), synced (boolean)
Event Insertion MCP save_entity() and append_combat_log() methods insert rows atomically in the same transaction as mutations

Game Hub

client

Web UI for between-session game management — browse, create, inspect, and delete campaigns (ADR-0005); live real-time updates from server events (ADR-0007)

Next.js + React (TypeScript)
Game List Page /games — grid of GameCard components showing campaign name, entity count, last played
Create Game Page /games/new — form to create a new campaign seeded with default entities
Game Detail Page /games/[gameId] — entity roster table with HP bars, combat log preview, Start Voice Session button, delete button
Game Detail Live Component that subscribes to SSE events, refetches entity roster and combat log on entity_updated/combat_log_appended events
Character Picker Page /games/[gameId]/player — lists player entities as cards, each linking to that entity's character sheet
Character Sheet Page /games/[gameId]/player/[entityId] — displays ability scores with modifiers, HP bar, AC, items, and a conditional level-up panel when ASI points are available
Character Sheet Live Component that subscribes to SSE events, refetches character data on entity_updated events
useGameEvents Hook React hook that instantiates browser EventSource, parses text/event-stream, yields typed events; triggers refetch callbacks for character/combat updates
Level-Up Panel Client component with +/- controls for distributing 2 ASI points across ability scores; calls POST /api/games/{id}/entities/{eid}/level-up on submit
Interactive architecture graph coming soon