Rust game engine for the SideQuest AI Narrator -- a tabletop RPG engine where coordinated Claude agents run the game. Ported from Python (sq-2).
Players connect via WebSocket. Seven Claude agents collaborate as the game master: narrating scenes, building worlds, creating creatures, managing dialogue, and routing player intent. The ML stack (image generation, audio mixing) stays in Python as a sidecar daemon.
React Client (sidequest-ui)
| |
| WebSocket /ws | REST /api/*
v v
+------------------------------------------------+
| sidequest-server |
| axum HTTP/WS, session lifecycle, CORS, |
| structured tracing, watcher telemetry |
+------------------------+-----------------------+
| GameService trait
v
+------------------------------------------------+
| sidequest-agents |
| 7 agents, orchestrator state machine, |
| ClaudeClient, prompt framework, JSON extract |
+------------+-------------------+---------------+
| |
v v
+--------------------+ +--------------------+ +---------------------------+
| sidequest-game | | sidequest-genre | | sidequest-daemon-client |
| GameSnapshot, NPCs,| | YAML pack loader, | | Unix socket client for |
| combat, chase, | | trope inheritance, | | Python media daemon |
| persistence, turns | | name gen, cache | | (JSON-RPC) |
+---------+----------+ +---------+----------+ +---------------------------+
| |
+-----------+-----------+
|
+-----------+-----------+-----------+
v v v
+--------------------+ +---------------+ +---------------+ +---------------+
| sidequest-protocol | | encountergen | | loadoutgen | | namegen |
| GameMessage enum, | | CLI: enemy | | CLI: starting | | CLI: NPC |
| newtypes, sanitize | | stat blocks | | equipment | | identity |
+--------------------+ +---------------+ +---------------+ +---------------+
Communication protocol between UI and server. Defines the GameMessage enum,
typed payloads for every message type, input sanitization, and newtype wrappers
for domain IDs.
Depends on: nothing
YAML genre pack loader and narrative models. Handles pack loading and validation, trope inheritance resolution, name generation (Markov chains + template blending), and a genre cache for hot-reloading packs at runtime.
Depends on: protocol
Core game state engine. 59 modules (~23.7k LOC) covering:
- GameSnapshot -- composable game state
- Characters/NPCs -- full model with inventory, abilities, disposition
- Combat -- classification, resolution, turn management
- Chase -- cinematic chase engine (ADR-017)
- Turn modes -- exploration, combat, chase, character creation
- Session lifecycle -- start, save, load, resume
- Persistence -- SQLite via rusqlite
- Character builder -- state machine (ADR-015)
- Tension tracker -- dual-track model (ADR-024)
- Beat filter, render queue -- pacing control (ADR-025)
- Music director, audio mixer, voice router -- cinematic audio cues and pre-rendered music selection
- Multiplayer, guest NPC -- concurrent player sessions
- Lore store, conlang -- knowledge indexing and constructed languages
- Prerender scheduler -- speculative image rendering between narration turns
One module stubbed: perception.rs (RED phase, story 8-6).
Depends on: protocol, genre
Claude CLI subprocess orchestration. All LLM calls go through
claude -p as a Tokio subprocess, never through the Anthropic SDK
(ADR-001).
5 agents (post ADR-067): Narrator (unified — exploration/combat/chase/dialogue), WorldBuilder, Troper, Resonator, IntentRouter. CreatureSmith, Ensemble, and Dialectician were absorbed into Narrator via conditional prompt sections.
Infrastructure:
- Orchestrator --
GameServicetrait, agent state machine - ClaudeClient -- subprocess management, timeout handling
- Prompt framework -- attention zones (EARLY/VALLEY/LATE) (ADR-009)
- JSON extractor -- 3-tier fallback (ADR-013)
- Context builder, turn record telemetry, patch legality, entity tracking
Depends on: protocol, game
axum HTTP/WebSocket server. REST endpoints for save/load, character listing,
genre pack metadata. WebSocket at /ws for real-time game events. Includes
session lifecycle, CORS, structured tracing via tracing-subscriber,
a /watcher endpoint for telemetry (ADR-031),
and graceful shutdown.
Depends on: all other crates
Unix socket client for the Python media daemon (sidequest-daemon). JSON-RPC protocol for image generation and embedding requests. Typed request/response structs with error handling.
Depends on: protocol
CLI binary that generates enemy stat blocks from genre pack data. Produces culture-appropriate names (Markov chains), class/archetype stats, HP, abilities, weaknesses, OCEAN personality, trope connections, and visual prompts.
Depends on: genre
CLI binary that generates starting equipment sets from genre pack inventory catalogs. Resolves items, applies tier scaling, and generates narrative hooks.
Depends on: genre
CLI binary that generates complete NPC identity blocks from genre pack data. Culture-appropriate names, archetype personality, OCEAN profile, dialogue quirks, inventory hints, and trope connections.
Depends on: genre
cargo build # Build all 9 crates
cargo test # Run all tests (182 test files)
cargo clippy -- -D warnings # Lint
cargo fmt -- --check # Format check
cargo run -p sidequest-server # Run the serverRequires Rust 1.80+ (edition 2021). See rust-toolchain.toml for the
pinned version.
| Decision | Rationale | ADR |
|---|---|---|
| Claude CLI only | Subprocess calls (claude -p), not the SDK. Simpler auth, observable, debuggable. |
001 |
| GameService facade | Server depends on a trait, never on game internals. Keeps axum layer thin and testable. | -- |
| JSON delta patches | Agents emit state patches, not full state replacements. Bandwidth-efficient, auditable. | 011 |
| Genre packs as YAML | Runtime-swappable narrative configuration. Any genre, any setting. | 003 |
| Graceful degradation | Agent timeouts produce degraded responses instead of errors. The game never crashes. | 006 |
| Attention-aware prompts | Prompt zones (EARLY/VALLEY/LATE) place critical context where the LLM pays attention. | 009 |
| 3-tier JSON extraction | Regex, then partial parse, then re-prompt. Handles malformed LLM output. | 013 |
| Story-driven testing | Tests named by feature story (e.g., combat_classification_story_5_2_tests.rs). |
-- |
All ADRs live in the orchestrator at docs/adr/.
For developers coming from the Python codebase:
| Python (sq-2) | Rust (this repo) |
|---|---|
| Pydantic models | serde structs |
| asyncio | tokio |
| aiohttp | axum |
| pyyaml | serde_yaml |
| sqlite3 | rusqlite |
subprocess.run(["claude", "-p", ...]) |
tokio::process::Command |
- orc-quest -- Orchestrator (sprint tracking, ADRs, genre packs)
- sidequest-ui -- React/TypeScript game client
- sidequest-daemon -- Python media services (image gen, embeddings)
Default branch: develop (gitflow). Feature branches: feat/{description}.
PRs target develop.