ADR-0003: The expressivity ladder, and where code may live
- Status: Proposed
- Date: 2026-09-30
Context
"Any game any user wants" needs more than parameters. Every game description language we surveyed runs out in the same three places: conditions and formulas, sequencing over time, and genuinely new mechanics (research/generative-stack.md §2.2). Each language answers the last one with code. The question is who writes that code, and where it runs.
ADR-0002 keeps code out of published games. That alone would cap Gamewright at whatever the engine already knows.
Decision
Games climb a ladder, and each rung is tried before the next.
| Rung | What | Who writes it | Where it runs |
|---|---|---|---|
| L0 Parameters | numbers, enums, id refs and tags in documents | Director, people | everywhere |
| L1 Expressions | typed, pure Expr strings for conditions, magnitudes and weights (a Python/GDScript subset, with whitelisted functions) |
Director, people | everywhere |
| L2 Graphs | flat rule sets, state machines, behavior trees, timelines, dialogue and quests, interpreted by the engine | Director, people | everywhere |
| L3 Kits | typed GDScript in the engine repo: components, actions, events, Expr functions, generator passes, BT tasks. Each kit registers its vocabulary with prompt-quality descriptions | coding agents and people, reviewed, with tests | everywhere, once released |
| L3′ Local kits | the same kit interface, inside a Game Pack's kits/ folder |
the owner's own coding agent | only on the owner's machine, in dev and desktop builds that opt in with a trust flag |
| L4 Sandboxed scripts | per-game Lua via a wasm-capable GDExtension | — | deferred |
Rules:
- Expressions are ours, not Godot's
Expressionclass. That class can call any method on the objects passed to it. It's untyped, and its errors surface only at runtime. The core parses and typechecks eachExpronce at bake and compiles it to a postfix array. A small GDScript stack VM evaluates it with a step budget. One grammar, one parser. - Graphs are bounded by construction.
eachqueries carry amax, event chains stop at a depth limit, and each tick has an action budget. Rules never wait; waiting belongs in a timeline or a state machine. - Vocabulary is generated from kits. Schema enums and union branches for components, actions, events, functions, passes and tasks come from kit registrations. The Director's tools can never offer what the runtime can't do.
- A vocabulary gap is a document. When the Director can't express a request with rungs
L0–L2, it files a
KitRequest: the need, examples, a proposed registration. In the owner's workspace a coding agent can build it as a local kit at once. Promoting it to an engine kit is a normal reviewed change. The rate ofKitRequests is the engine's roadmap signal. - Published games carry no code. The platform and the shared player reject a bundle that depends on local kits. A local kit reaches other players only by becoming an engine kit.
Alternatives considered
- Per-game GDScript written by the AI. Unbounded, but it can't be sandboxed, it can't be pointer-edited or locked, and "compiles" isn't "works": in the Mage study compile rate was anti-correlated with functional correctness. Kept only as local kits, where the owner accepts the risk on their own machine.
- A pure interpreter with no expressions. Every formula would become an enum. GAS, GDevelop and Construct all grew an expression layer.
- A Turing-complete JSON or visual language. It needs recursion (breaking ADR-0005), it carries the same safety surface as code, and Godot's VisualScript history is against it.
- Sandboxed Lua now. A second language for the model, GDExtension on the web, and a new sandbox to audit. Revisit only if kits demonstrably can't keep up.
Consequences
- The engine grows by kits. The fourth genre template should need mostly packs, not new kits; if it doesn't, the vocabulary is at the wrong level.
- Kits need prompt-quality registrations, tests and versioning. The kit registry is a contract between the engine, the schemas and the Director.
- Local kits give the owner "anything goes" on their own machine without forking the engine.