ADR-0002: A game is data; one player runs every game
- Status: Proposed
- Date: 2026-09-30
Context
Reelwright's split is "the core decides, the engine draws": the engine is a pure function of resolved specs and a frame number. A game can't work that way. The player decides things every frame, and a shipped game, above all on the web, runs with no core in the loop.
Three findings shape the answer:
- Security. A
.tscn,.tresor.pckcan carry GDScript, and loading it runs it. On the web, GDScript can callJavaScriptBridge.eval; on desktop,OS.execute. Godot has no safe loader: proposals #4925, #10968 and #5010 are still open (research/web-platform.md §7, research/generative-stack.md §3.1). - Cost. One Godot web engine is 38.8 MB raw and 9.6 MB over the wire on Vercel, measured on unspent's live preview (web-platform.md §1). Exporting it once per game wastes storage, bandwidth and build time, and needs Godot on a server.
- Verification. Only documents with known meaning can be checked before anyone plays: references, item obtainability, level reachability (generative-stack.md §8).
Decision
- A game is a Game Pack: schema-validated JSON documents plus media, in a folder that can be its own git repository. It holds no Godot resources and no scripts.
- The core authors and bakes.
core/(TypeScript, Node 24) is the only writer of pack documents. It validates, resolves the template, compiles expressions, and bakes the pack into a bundle: resolved canonical JSON, re-encoded media, and a manifest of content hashes. - The engine plays.
engine/is one Godot project, the player. It loads any bundle, checks its hashes and schema version, and runs it with a fixed vocabulary of kits (ADR-0003). The player refuses Godot resource types, script sources and unknown document kinds in a bundle. - Export is packaging, not compilation. A desktop game is the player binary plus a bundle. A web game is the shared player files plus a bundle fetched at start. Each player is built once per engine release.
- Generation that happens per playthrough runs in the engine. Worlds from a seed and roguelike floors are generated by kit passes in GDScript, because the core isn't there at play time. The core runs the same passes through headless engine jobs when it needs to preview or check them. Solver passes that need a full solver (ASP, SAT) run in the core at bake time and ship their output as data.
Alternatives considered
- The AI writes a Godot project per game. It's the most expressive option. Rejected as the platform's model: it can't be sandboxed, every game becomes a fork, and it can only be checked by playing. Frontier agents finish whole Godot games 41–54% of the time (generative-stack.md §3.1). It remains the escape hatch for developers, as local kits (ADR-0003).
- Export a Godot
.pckper game. It's the same code-execution hole, and it costs 45 MB per game. - Reelwright's pure-function engine. It doesn't fit interactive play.
Consequences
- The web platform needs no server-side Godot to play games. One immutable-cached engine serves every game.
- The engine must validate what it loads (hashes, plus generated GDScript validators for data that didn't come through a bake, such as saves).
- The engine's vocabulary limits what games can express. ADR-0003 says how the vocabulary grows.
- The core and the engine need identical seed and hash functions, checked against shared test vectors in both native and web builds (ADR-0004).