Engine
Core Python package structure, CLI entrypoints, orchestration modules, and SRD level-up engine contracts.
Engine
engine/ is the primary application package and execution boundary. It acts as the single Python import root for all backend logic—CLI commands, data pipelines, schema definitions, level-up computation, character persistence, GRPO training pipelines, and DM orchestration all live under this namespace. Nothing outside engine/ imports from server/ or web/; dependency flow is strictly inward.
Subpackages
core/: foundational runtime enums and shared types.engine/core/types.pydefinesGamePhaseandPlayerRoleenums used by the DM orchestration layer. These are string enums to allow direct JSON serialization without custom encoders.ingest/: data ingestion, normalization, and progression generation. This is where SRD JSON files are parsed, normalized into a consistent shape, loaded into SQLite, and then materialized as immutable progression artifacts. See Data Pipeline for the full flow.schemas.pymodules across subpackages: Pydantic contracts for progression, homebrew, level-up, GRPO, and TL;DR formats are defined in files such asengine/ingest/schemas.py,engine/srd_level_up/schemas.py,engine/characters/schemas.py, andengine/grpo/schemas.py. Models useextra="forbid"in many contracts to catch schema drift early, and TypeScript types inweb/src/types.tsmirror these backend shapes.srd_level_up/: stateless level-up engine that computes preview choices, validates decisions, and applies level-ups for SRD 2014 classes. Hardcodes class-specific knowledge like subclass choice levels and caster type categorization.dm/: model gateway, tool registry, and session orchestration for DM copilot features.characters/: saved-character persistence layer againstlorewright-app.sqlite. Wraps the stateless SRD level-up engine with character identity and snapshot history.grpo/: GRPO combat dataset pipelines for self-actor training data generation. The current flow rehydrates combat samples, transfers exact FIREBALL ally HP when available, and finalizes the enriched dataset in one pass.tldr/: TL;DR dataset builders that generate LLM-authored summaries for spells, feats, features, and character level snapshots, plus the sync helper that loads finalized JSONL intolorewright-app.sqlite.
Entrypoints
- CLI lives in
engine/app.py. Bothlorewrightandmythosconsole script entry points resolve toengine.app:main. The CLI usesargparsewith subcommands for each pipeline operation, includingdb-refresh,generate-progressions,grpo-combat-enrich,grpo-combat-finalize,spell-tldr-build,character-level-tldr-build,tldr-sync,spell-tldr-validate, character management, and stateless level-up. - Pipeline operations are exposed through Data Pipeline and Rebuild Runbook.
- SRD single-class level-up behavior is implemented in
engine/srd_level_up/engine.pyand documented in SRD Level-Up Contracts. - DM-facing modules are tracked in DM Orchestration.
Design Rationale
The engine is structured as a library-first package rather than a framework. Every public function accepts explicit path arguments (e.g., db_path, app_db_path) instead of relying on global configuration or environment variables. This makes the engine trivially testable—tests can point at isolated temp databases without monkey-patching config singletons.
The choice to keep engine/ as a flat namespace with subpackages (rather than splitting into separate installable packages) reflects the tight coupling between schemas, pipelines, and the level-up engine. Schema changes frequently cascade across all three, so co-location in a single package simplifies atomic updates and avoids cross-package version coordination.
The stateless level-up engine (srd_level_up/) is deliberately separated from character persistence (characters/). This allows the same preview/apply logic to be called both from the stateless tool API (/api/tools/srd-level-up/*) and from the character-scoped API (/api/characters/{id}/level-up/*). The character store composes the stateless engine rather than extending it.
Assumptions & Constraints
- Python 3.12+: The codebase uses modern typing syntax (
X | None,list[str]) withoutfrom __future__ import annotationsin most modules, requiring Python 3.12. - Single-class only: The SRD level-up engine assumes single-class characters. Multiclassing is out of scope for v1 and would require significant rework of the choice dependency graph.
- 2014 SRD only for level-up: While the ingest pipeline processes both 2014 and 2024 editions, the level-up engine hardcodes 2014-only paths (
classes/2014/,subclasses/2014/). This is an intentional v1 constraint. - SQLite as the sole data store: Both the SRD content database and the app runtime database use SQLite. This simplifies deployment (no external database server) but limits concurrent write throughput.
- Pydantic v2 strict mode:
extra="forbid"means any unexpected field in API payloads or pipeline data causes an immediate validation error. This trades flexibility for early error detection.
Conceptual Model
The engine can be understood as three concentric layers:
- Data layer (
ingest/,schemas/): Transforms raw SRD JSON into normalized, validated, and indexed content. This layer is write-once-read-many—once progression artifacts are generated, they are treated as immutable. - Computation layer (
srd_level_up/): Pure functions that take a character state and produce choices, validation results, or updated state. No side effects, no database writes. This is the core domain logic. - Persistence layer (
characters/): Wraps the computation layer with identity, history, and decision-plan storage. This is the only layer that writes tolorewright-app.sqlite.
The DM orchestration (dm/) and GRPO/TL;DR pipelines sit outside this core stack as independent consumers of engine schemas and data.