Document kinds
A game is documents. Every document says what it is in its kind and is checked against its JSON Schema in schemas/v1/. A file's name says its kind (driftwood.item.json is an Item), so tools, people and editors agree without opening it. Generated from the schemas.
Every document starts with the same five fields, left out of the tables below: kind; schemaVersion ("1.0"); id, which starts with its kind's prefix (arch.sentry, item.driftwood); name, for people; and meta: its description, status, provenance, notes and locks.
In a Game Pack (34)
GameManifest
game.json, at the pack's root
ids: a game id like game.tideline
schema: schemas/v1/game.schema.json
The game: game.json at the root of a pack. It pins a template, adds packs and kits, and names the documents the player boots with (theme, style, world, player, screens, input, camera). A reference left out is filled at bake with the only document of that kind, so a game on a template is playable before it names anything.
| Field | Type | What it is |
|---|---|---|
template required | object | The template this game plays like (ADR-0006), from library/templates/. |
packs | list of object | Library packs this game adds: shared documents that use kits. Not supported by bake yet. |
theme | a theme id like theme.drowned_coast | What the game is about. |
style | a style id like style.tideline | How it looks and sounds. |
language | a language code like en, fr or pt-BR | The language the game is written in. A Locale document carries it in another; the person picks one in the game's Options. |
rootSeed | integer | The seed every other seed is derived from by name. Worlds, loot and brains all follow from it. |
sim | object | How the simulation runs. |
clock | clock settings | How the world clock runs, for a game with kit.clock: world minutes per real second, the hour a new game starts, and when dusk and dawn fall. Every field is optional; left out, the kit's defaults. |
world | a world generator id like gen.tideline | The world generator. |
scenes | list of scene | The places the game is played in, joined by doors: a wood and the den under it, a coast and the hold of the wreck on it. Each is a world of its own (kit.world_tiles), made the first time the player goes in. The game starts in the first, which is the game's world. A door (kit.core's door, on a usable) or the travel action takes the player to another, and what was taken, worked or downed in the one left stays as it was until the player comes back, in a save too. Leave it out for a game played in one place. |
player | object | Who the player is and where they start. Left out, the template's (its manifest.player). |
flow | object | The screens the game opens with. Left out, the template's (its manifest.flow). |
menu | object | The menu every game has: Esc, a pad's Start, the pause action or the touch controls' Pause opens it, and the game stands still until it closes. It offers Continue, Restart, Options (volume, text size, and screen shake and flashes when the game has them) and Save and quit, and the title (or the first page of a game with none) offers Continue while a save of the game is on the device. The player's own, drawn in the game's UI on kit.ui's ui.pause and ui.options Screens; a Screen of the game's with that id lays a page out instead, from the menu feed. Left out, all of it is on, in the engine's words. Set a part false only when the genre can't have it. |
input | an input map id like input.action_survival | The controls. |
camera | a camera rig id like cam.ortho | The camera rig. |
kits | list of a kit id like kit.melee: kit, a dot, then lowercase letters, digits and _ | Kits this game runs beyond its template's. Bake writes the full list here (template, game and what they require). |
platforms | list of "desktop" | "web" | Where the game ships. The web is a first-class target. |
GameBrief
brief.json, at the pack's root
ids: a brief id like brief.tideline
schema: schemas/v1/brief.schema.json
The game's brief: brief.json at the root of a pack. The first thing the Director proposes and the person approves: the pitch, the pillars every later choice is checked against, the core loop, and caps on scope. A design document: bake leaves it out of the bundle.
| Field | Type | What it is |
|---|---|---|
pitch required | string | One or two sentences: what the player does, and why it is fun. |
pillars | list of string | Three to five short rules every later decision is checked against ("tactics beat stats"). |
template | a template id like tpl.action_survival | The template the game fits. |
coreLoop | object | What the player does over and over, in the vocabulary of kits. |
scope | object | Caps that keep the first version small. |
checkpoints | list of object | The checkpoints the person has passed (docs/design/m2-director.md §4), each recorded by a decide tool when the person says so: c2 by mark_played once they have played the first playable. The Director never writes these; later stages' tools wait for them. |
plan | plan | The way to a big ask, as milestones the person approves once (propose_plan): each a step a player can play, proven by its acceptance Playtests. complete_milestone marks one done when they pass; no other proposal may, and a done milestone stays as it was. |
Ability
<folder>/<slug>.ability.json, in any folder of the pack
ids: an ability id like ability.dash
schema: schemas/v1/ability.schema.json
Something a body can do on demand, with a cooldown and maybe a cost: a dash, a fireball, a shield, a ground slam, a swipe. Any mover carries abilities with kit.core's abilities component (the player and anything a brain drives alike). The player uses one with its input action; any other body uses one by itself (auto, while when holds) or when a state machine or rule asks (use_ability). What it does is a list of actions, built from what the kits already do: put an Effect on itself or its target (apply_effect), fire a volley (kit.ranged's fire), dash (kit.move's dash), hit everything round it (kit.melee's blast), strike ahead in a platformer (platformer_strike), throw a blow or a shot through the body's own components (strike, shoot). ability.used, ability.refused and ability.ready tell rules, cues and views.
| Field | Type | What it is |
|---|---|---|
action | an input action id like use or dodge | The InputMap action the player presses to use it. Only the player presses: any other body uses it by auto, or by use_ability from its state machine or a rule. |
auto | boolean | A body other than the player uses it by itself as soon as it is ready and when holds: a turret's volley, a foe's charge. |
when | boolean or an expression like self.health < 30 | It can only be used while this holds, with self the body using it: platformer_grounded(self) for a ground slam, self.health < 3 for a last stand. Left out, always. |
cooldownMs | number | After a use, how long until it can be used again, in milliseconds (rounded to whole ticks). |
cost | object | A stat the use spends (mana, stamina): it can't be used with less, and using it takes this much. |
do required | list of action | What a use does, in order, with self the body using it and target what it was used at (use_ability's at; a click's target for the player), which may be none. |
Archetype
<folder>/<slug>.archetype.json, in any folder of the pack
ids: an archetype id like arch.sentry
schema: schemas/v1/archetype.schema.json
A kind of entity: the player, a machine, a campfire. Its tags, stats and components (from kits) say what it is and does; its model and cues say how it looks and sounds.
| Field | Type | What it is |
|---|---|---|
tags | list of a tag: lowercase letters, digits, - and _ (at most 32) | Tags rules and expressions select by: has_tag(target, 'machine'). |
stats | list of object | Starting values of stats declared in a Stats document. A stat left out starts at its declared default. |
components | list of component | What the entity can do, one component per kit feature: {"component": "melee", …its fields}. |
model | a model id like model.pine | How it looks. |
cues | list of object | Sounds this entity plays when events happen to it: an event that names it (self, entity, target...) plays its cue at it, and one that names its archetype in an arch field and no entity of it (an acorn collected, a crate moved on a grid) plays the cue at the listener. on "alive" plays a loop for as long as it lives (a hum). |
Asset
<folder>/<slug>.asset.json, in any folder of the pack
ids: an asset id like asset.hero_run
schema: schemas/v1/asset.schema.json
A media file the person brought into the game, frozen: a picture (PNG, JPEG or WebP) or a sound (OGG Vorbis, WAV or MP3), kept in the pack under its content hash, with what its bytes are and where it came from. The person imports it (the Studio's Import, a file dropped on the Studio, or gamewright import); nothing else makes one, and the Director never writes one: it uses the person's assets by id, from a Sprite (sprite.sheet) or a Sound (sound.file). A changed file is a different asset: the hash pins the bytes, and a file that no longer matches it is refused. What the file is comes from its bytes, never its name, and must fit the player's limits. source is the ledger a store's AI disclosure is filled from, so say it exactly.
| Field | Type | What it is |
|---|---|---|
file required | asset file | Where the file is and the SHA-256 of its bytes. Import keeps it at assets/<name>-<the first 12 digits of its hash>.<format>, so a file is never overwritten by another: re-importing a changed file makes a new one, and undo finds the old one where it was. |
bytes required | integer | The file's size in bytes. At most 6,000,000: the web Studio sends a file in one message, and the web player fetches every file before the game starts. |
media required | image media or sound media | What the file is, read from its bytes when it was imported: a picture's format and size, or a sound's format, length, channels and rate. The core reads them again from the file at every check and refuses a file that no longer says the same. |
source required | source | Where the file came from, in the person's own words: who made it, under what licence, from where, and whether AI made it. A store's AI disclosure (Steam's content survey, itch.io's AI tags) and a credits page are filled from these, so leave a field unstated rather than guess. Every file is made before the game ships, never while it plays. |
originalName | string | The file's name when it was imported (hero_run.png), so a person knows which of their files it is. |
Biome
<folder>/<slug>.biome.json, in any folder of the pack
ids: a biome id like biome.coast
schema: schemas/v1/biome.schema.json
A kind of land: its grounds (how they walk and look), its props (what stands on it, and what taking them yields), and its water. Part of the world stamp.
| Field | Type | What it is |
|---|---|---|
grounds required | list of object | The grounds a world is made of. Drawn flat (a topdown2d CameraRig), a later ground's edge spills over an earlier one's where they meet, so list them from the one that gives way to the one that wins. |
props | list of object | What stands on the land. Props are not entities: they live in the world kit's tables with stable ids. |
water | object |
BodyPlan
<folder>/<slug>.bodyplan.json, in any folder of the pack
ids: a body plan id like plan.quadruped
schema: schemas/v1/bodyplan.schema.json
A body plan and the motion presets written for it (docs/design/m6-presentation.md §7): the roles a rigged Model's joints can play (a quadruped's body, head, tail and four legs), and named gaits and poses (gallop, trot, jump, hurt) made of the same keyed moves a Model writes, by role instead of by joint. A Model names a plan, says which of its joints plays each role, and takes presets by name as its poses; its own moves then change any joint of any pose. The library's plans live in library/plans/, and a pack may hold its own (one with a library plan's id replaces it for that game). Bake carries every plan a game's Models use into its bundle. Looks only: no state hash reads it.
| Field | Type | What it is |
|---|---|---|
roles required | list of object | The parts of this body a joint can play, each a pivot: a leg's role is the joint at the top of the leg, a head's the joint at the base of the neck. A Model maps each role to one of its joints; a role it leaves out goes to the joint of the same name, and its moves are left out when there is none. |
sizeRoles | list of a slug: lowercase letters, digits and _, starting with a letter (at most 48) | The roles whose joints' mean height above the ground is this body's size (a quadruped's four legs: its leg length). A Model's size over sizeM scales every forwardM and upM key, so a rabbit's bob is a rabbit's. None: shifts are not scaled. |
sizeM | number | The size the moves' forwardM and upM keys are written for, units (a fox's legs are about 0.42 long). |
presets required | list of object | The named poses this plan offers: gaits that loop (walk, trot, gallop, strut, flap) and one-shots (jump, fall, hurt). A Model takes one as a pose of the same name, or of the name it gives it. |
moves | list of object | What each role does in each preset: a flat list, at most one move per role and preset, read exactly as a Model's moves are (kit.mesh's Move), with role in place of joint and preset in place of pose. A role no move of a preset names stays at rest in it. |
CameraRig
<folder>/<slug>.camera.json, in any folder of the pack
ids: a camera rig id like cam.ortho
schema: schemas/v1/camera.schema.json
How the camera sees the game. ortho3d: a fixed yaw and pitch over an orthographic view that follows the player, snapped to texels so edges never crawl, with anything over the player stippled away. platformer2d: a flat side view of a 2D level (kit.platformer) at a whole-pixel scale, following the player and held inside the level; it reads viewHeightM (tiles of the level that fit the screen's height), followRate and snapTexels. topdown2d: a world (kit.world_tiles) drawn flat from above at a whole-pixel scale, its grounds autotiled and everything in it a sprite, for a game whose Style is flat_2d; it reads viewHeightM (tiles of the world that fit the screen's height), followRate and snapTexels.
| Field | Type | What it is |
|---|---|---|
rig required | "ortho3d" | "platformer2d" | "topdown2d" | |
yawDeg | number | Turn around the vertical axis; 45 looks along the diagonal. |
pitchDeg | number | Tilt down from the horizon. |
viewHeightM | number | How many metres of the world fit the screen's height. |
distanceM | number | How far back the camera sits along its view (for clipping and fog, not size). |
followRate | number | How fast it catches up with the player, per second. |
snapTexels | boolean | Snap to whole texels so edges never crawl. |
occlusion | "stipple" | "none" | What happens to anything between the camera and the player. |
Dialogue
<folder>/<slug>.dialogue.json, in any folder of the pack
ids: a dialogue id like dlg.harbour
schema: schemas/v1/dialogue.schema.json
A conversation, or a whole story, as a flat graph of lines and links (ADR-0003, L2), run by kit.core and started with the start_dialogue action. One line shows at a time: who says it and what they say. When the player advances, the line goes on to the first of its links without a choice whose when holds; with none, to the next line in the list, unless it is the last or says end, which ends the dialogue. Links with a choice are offered to the player once the line is read, and the one picked is taken. A line whose when does not hold is passed over as if it were advanced. Lines do actions as they are shown and links as they are taken, from the same vocabulary as rules: set_var keeps a flag a later line or link reads with when, end_game ends the game on an ending. Write the story top to bottom; give each branch's last line a link to where the branches meet again.
| Field | Type | What it is |
|---|---|---|
start required | a slug: lowercase letters, digits and _, starting with a letter (at most 48) | The id of the line it starts on. |
revealCps | number | How fast a line's words appear, in characters per second; 0 shows each line whole at once. Pressing advance while a line appears shows it whole; the next press goes on. Choices are offered once the line is whole. |
speakers | list of object | Who speaks, by id: the name shown over their lines. A line with no speaker is narration. |
lines required | list of object | Every line, in reading order: a line goes on to the next one in this list unless a link from it says otherwise. |
links | list of object | Where lines go other than the next line in the list: a jump (no choice), or a choice the player picks. Links from one line are tried in list order. |
Effect
<folder>/<slug>.effect.json, in any folder of the pack
ids: an effect id like effect.slowed
schema: schemas/v1/effect.schema.json
A status a body can be under for a while: slowed, poisoned, shielded, hasted, stunned, invulnerable. Anything can put one on any entity (the apply_effect action, an ability, a hit that carries one, a foe's hitEffect) and take it off (remove_effect). While it lasts it changes what the kits that move and fight read, all in one place: how fast the body moves (kit.move, kit.platformer), how much harm it takes and deals (kit.melee, kit.ranged, kit.platformer), whether it can move or act at all (blows, dodges, shots, abilities, a foe's touch), and whether anything can hurt it. It can also run actions every so often while it lasts (regeneration, poison). It lives in the entity's state, so saves and replays carry it exactly; effect.started, effect.renewed and effect.ended tell rules, cues and views.
| Field | Type | What it is |
|---|---|---|
durationMs | number | How long it lasts once put on, in milliseconds (rounded to whole ticks). 0 lasts until something takes it off. |
stacking | "refresh" | "extend" | "stack" | "keep" | What putting it on again while it lasts does. refresh: it starts its time over. extend: its time is added to what is left. stack: one more stack, up to maxStacks, and its time starts over (every modifier counts once per stack). keep: nothing, the one already on stays as it is. |
maxStacks | integer | With stacking stack: the most stacks it can have at once. |
speedMul | number | Multiplies how fast the body moves of its own accord, per stack: 0.5 is a slow, 1.5 a haste. Knockback and dashes are not slowed. |
damageTakenMul | number | Multiplies the harm that reaches the body, per stack: 0.5 a shield that halves blows, 2 a weakness. 0 shrugs every hit off (but the hit still lands: invulnerable turns it away). |
damageDealtMul | number | Multiplies the harm the body does with blows, shots and strikes, per stack: 2 is a rage. |
canMove | boolean | false holds the body where it stands: no walking, running or jumping of its own (it still falls and is still knocked about). A stun, a root, a freeze. |
canAct | boolean | false stops the body acting: no blows, dodges, shots or abilities, and a platformer foe that can't act does no harm to the touch. A stun or a silence. |
invulnerable | boolean | true turns every hit away while it lasts: blows, shots, strikes and stomps do nothing, and a platformer player passes spikes and foes unharmed (a fall still counts). |
everyMs | number | How often the every actions run while it lasts, in milliseconds: the first time this long after it is put on. |
every | list of action | Actions run every everyMs while it lasts, once per stack, with self the body it is on and source whoever put it on: regeneration is add_stat on health, poison is kit.melee's damage on self. |
GridRules
<folder>/<slug>.gridrules.json, in any folder of the pack
ids: a grid rules id like gridrules.sokoban
schema: schemas/v1/gridrules.schema.json
How a grid puzzle behaves (kit.grid_rules): PuzzleScript-style rewrite rules and win conditions, as flat lists. A rule is a row of pattern cells along a direction; its cells (with the rule's id as parent) say what each cell must hold (holds) and what it holds afterwards (becomes). Each turn: the avatar is marked to move, the rules that are not late run in list order, each until it changes nothing, then everything marked moves, then the late rules run the same way, then the win conditions are checked. Levels name the GridRules they play by.
| Field | Type | What it is |
|---|---|---|
rules | list of rule | The rules, in the order they run. Pushing is a rule too: with no rules the avatar only walks. |
cells | list of pattern cell | The pattern cells of every rule, each naming its rule as parent and its place along the rule's direction with at (0 is where the pattern starts). |
win | list of win condition | The level is won after a turn in which every condition holds. With none, a level is never won. |
passLimit | integer | The most passes one rule may make in one turn before it is stopped and the stop reported (a rule that keeps changing things forever). 200 is PuzzleScript's bound. |
InputMap
<folder>/<slug>.input.json, in any folder of the pack
ids: an input map id like input.action_survival
schema: schemas/v1/input.schema.json
The game's controls: abstract actions (move, use, dodge) and the keys, mouse buttons and pad buttons bound to them. The simulation only ever sees actions, so bots, tours and replays drive the same game a person does.
| Field | Type | What it is |
|---|---|---|
actions required | list of object | |
pointer | what the mouse, or a tap on the game, does in play | The pointer: a mouse, or a finger tapping the game's frame on a phone. The game hears it only in world terms the view works out: the point on the ground under it (on a grid of 1/64 m) and, on a click, the thing under it (an entity id, or prop:<prop id>), recorded in the input frame, so replays and Playtests repeat a pointer game exactly with no picture drawn. A mouse button presses an action as any key does (actions[].mouse). Menus and the game's Screens answer the mouse with or without it (hover chooses a row, a click takes it, a click on a key hint presses its key). Leave it out of a game the mouse doesn't play. |
Item
<folder>/<slug>.item.json, in any folder of the pack
ids: an item id like item.driftwood
schema: schemas/v1/item.schema.json
A thing the player can carry: a material, a tool, food. How it is obtained (harvest, recipes, loot, starting kit) is checked statically, so an item nothing can produce is caught before anyone plays.
| Field | Type | What it is |
|---|---|---|
description required | string | One line the player reads. |
bulk | number | How much of the carrying load one takes. |
stack | integer | How many fit in one slot. |
tags | list of a tag: lowercase letters, digits, - and _ (at most 32) | |
tool | object | Set when the item works as a tool: which verb it does (chop, dig, cut) and how hard a thing it can work. |
use | object | Set when the person can use it from what they carry: eat it, drink it, light it, blow it. The carry app's confirm on its row uses one (kit.inventory: a use:<item id> press), spends a charge, takes one away once its charges are spent, and sends inventory.used, which a rule answers with what it does (add_stat health, show_message). Without a use, confirm puts it in hand. |
model | a model id like model.pine | How it looks when dropped or held. |
icon | asset ref | Its inventory icon: a PNG or WebP in the pack. |
KitRequest
<folder>/<slug>.kitrequest.json, in any folder of the pack
ids: a kit request id like kitreq.hover_over_water
schema: schemas/v1/kitrequest.schema.json
A gap in the engine's vocabulary (ADR-0003 rule 4): something a game needs that no kit's components, actions, events, functions, passes or builders can say, filed by the Director instead of approximating it. It records the need, examples of use, the registration a kit would add, and the proposal that hit the gap. In the owner's workspace a coding agent can build it as a local kit; across games, the count of requests is the engine's roadmap signal. A design document: bake leaves it out of the bundle.
| Field | Type | What it is |
|---|---|---|
need required | string | What the game needs and why the vocabulary can't say it, in the person's terms: what the player should see and feel, and what was tried. |
examples required | list of string | How games would use it: one situation per entry, in plain words or as a sketch of the document fields it would take. |
proposed required | object | The registration a kit would add, in the shape of kit.json, so a coding agent can start from it. |
hitBy | object | Where the gap was found: the tool call and, when there was one, the proposal it was working on. |
Level
<folder>/<slug>.level.json, in any folder of the pack
ids: a level id like level.first_push
schema: schemas/v1/level.schema.json
One level of a grid puzzle (kit.grid): its size, its cells as rows of characters, and a legend saying which objects each character places. Objects are archetypes with a grid_object component. Levels play in order, lowest first; bake proves each one solvable with the core's solver and stores the shortest solution as its witness.
| Field | Type | What it is |
|---|---|---|
order | integer | Where the level comes in the game: lower plays first; levels with the same order go by id. |
rules | a grid rules id like gridrules.sokoban | The GridRules this level plays by. Left out: the game's only GridRules document, or none (the avatar only walks). |
sizeCells required | list of integer | [width, height] in cells. |
rows required | grid rows like 5#/#@o.#/5#: rows top to bottom split by /, where a number before a character repeats it | The cells, top row first. Each row expands to exactly width characters; 3# is ###. Digits and / are never legend characters. |
legend required | list of legend entry | What each character of rows places. Every character used must be here. |
witness | witness | Written by bake, never by hand: the shortest solution the core's solver found, and what the search saw (difficulty signals). |
Locale
<folder>/<slug>.locale.json, in any folder of the pack
ids: a locale id like locale.fr
schema: schemas/v1/locale.schema.json
The game in another language: every text a person reads, translated from the game's own language (the GameManifest's language). Each string replaces one text field, named by its key: the document's id, a colon, then the path to the field, its steps joined by '/', an array of things with ids stepped by the id and any other array by its index (dlg.elder:lines/hello/text, item.berries:name, rules.quest:rules/found/do/0/text, game.vale:menu/words/resume). A text the Locale leaves out shows in the game's own language. The person picks the language in the game's Options (the first time, their device's, when the game has it); it is presentation only, so saves, replays and the state hash never depend on it.
| Field | Type | What it is |
|---|---|---|
language required | a language code like en, fr or pt-BR | An ISO 639-1 language code, with a region where it matters (pt-BR). |
strings required | list of object | Each text it translates: the key naming a text field of the game's documents, and the text in this language, keeping any {placeholder} its source has. |
phrases | list of object | Text a game works out as it plays rather than writes in a field: a HUD line a Screen's bind chooses ('Find the relic.' if var.quest == 1 else ''), or the verb a key strip shows (a usable's talk). Each source text, exactly as the bind gives it, and its translation; kit.ui shows the translation wherever the bound text is that source. |
LootTable
<folder>/<slug>.loot.json, in any folder of the pack
ids: a loot table id like loot.sentry_scrap
schema: schemas/v1/loot.schema.json
What something drops: rolls picks from weighted entries. Weights may be expressions, so a drop can depend on the hour or the dropper.
| Field | Type | What it is |
|---|---|---|
entries required | list of object | |
rolls | integer | How many picks. |
Model
<folder>/<slug>.model.json, in any folder of the pack
ids: a model id like model.pine
schema: schemas/v1/model.schema.json
A Tier 1 model recipe: a builder from a kit (mesh.tree, figure.machine …) and its parameters, the colours of its palette slots, and its draw budget. The player builds it at load, cached by the recipe's hash.
| Field | Type | What it is |
|---|---|---|
builder required | "campfire.ring" | "driftwood.log" | "figure.person" | "machine.sentry" | "mesh.parts" | "rock.faceted" | "sound.file" | "sprite.baked" | "sprite.sheet" | "synth.bed" | "synth.layered" | "synth.sfx" | "tree.tiered" | "ui.theme" | "wreck.hull" | A builder a kit registers that makes a mesh. |
params | value | The builder's fields: an object checked against the builder named in builder (vocab.schema.json, Builder<Id>Params). Left out, every field takes its default. |
palette | list of object | The colour of each of the builder's slots, usually a Style slot. |
budget | object | The most it may cost to draw; the build fails past it. |
PlatformLevel
<folder>/<slug>.plevel.json, in any folder of the pack
ids: a platform level id like plevel.meadow
schema: schemas/v1/plevel.schema.json
One level of a 2D platformer (kit.platformer): Rooms placed on a grid, by hand (rooms) or by the rooms.chain generator (generate), which lays a path of rooms from a start room in the top row to an exit room in the bottom row and fills the rest. Levels play in order, lowest first; touching the exit goes on to the next. Bake proves each level completable with the core's reachability search, which plays the player archetype's own movement numbers, and stores the path it found as the level's witness, which a Playtest replays in the engine.
| Field | Type | What it is |
|---|---|---|
order | integer | Where the level comes in the game: lower plays first; levels with the same order go by id. |
roomTiles required | list of integer | [width, height] of every room in tiles; each placed Room must have this sizeTiles. |
gridRooms required | list of integer | [columns, rows] of the room grid. Row 0 is the top. A cell with no room is solid rock. |
rooms | list of placed room | Rooms placed by hand, one per grid cell. With generate, leave it out: bake writes the rooms the generator placed. |
generate | pass | Lay the rooms out with a generator pass instead of placing them by hand: {"pass": "rooms.chain", "id": "<its name>", …fields}, seeded by this level's id and the pass id. rooms.chain is the only pass a PlatformLevel takes. |
hazardDamage | number | Health a spike or a fall out of the level takes before the player goes back to the last checkpoint. |
look | level look | The colours the flat 2D view draws the level's tiles and sky in. Every field is optional. |
witness | witness | Written by bake, never by hand: a path the core's reachability search played from the start to the exit, tick by tick, with the player archetype's movement numbers, and what the search saw. |
chain | chain | Written by bake for a generated level: the attempt that produced a completable level, and the path of rooms it laid, start first. |
Playtest
<folder>/<slug>.playtest.json, in any folder of the pack
ids: a playtest id like test.stealth
schema: schemas/v1/playtest.schema.json
An acceptance scenario: a seed, a tour, a bot or the explorer, how long to run, what must hold at the end, the events it must reach, and metric thresholds. Every later change must keep it green.
| Field | Type | What it is |
|---|---|---|
seed required | integer | Unsigned 32-bit seed. Child seeds are derived by name path (deriveSeed), never by draw order. |
tour | a relative path inside the pack, like levels/forest.level.json | A tour file in the pack that drives the player, like playtests/proof.tour. |
locale | a language code like en, fr or pt-BR | Plays it in this language, one of the game's Locales: its frames (L3) are drawn in it, so a translation is checked to fit and read as the game's own words are. Left out, the game's own language. |
options | a person's options for a game, as its Options page sets them | Plays it as a person who set these on the game's Options page: its frames (L3) are drawn with them, so text grown to 200% must still fit and read, captions must read over the game, and a colour filter must keep things apart (with sees). Options change only what is drawn, so the run ends on the same state hash. Left out, the defaults every tool run draws. |
sees | a kind of colour blindness: protanopia (no red cones), deuteranopia (no green) or tritanopia (no blue) | Draws its frames (L3) as a person with this colour blindness sees them (Machado et al. 2009, full severity), after any colour filter its options turn on: every subject must stand apart from the ground round it, and every text from what is behind it, by their eyes. A red foe on green grass that reads by hue alone collapses for deuteranopia, the most common kind. Left out, as everyone sees them. |
steps | list of string | The tour written inline, one command per item, for a Playtest without a tour file: the tour language of docs/design/m1-player.md §8 (seed 3, boot game, press confirm, wait 2, await game.end:win 20, shot hero with player). Give one of tour, steps, bot or explore. |
bot | a slug: lowercase letters, digits and _, starting with a letter (at most 48) | A bot that plays instead of a tour, one a kit of the game registers: kit.core's monkey presses the game's actions and walks at random (fuzzing), kit.core's seek works toward goals the way a person would, kit.melee's fight_tactics plays a fight. |
explore | true | Search the game instead of playing it once: the engine's explorer (Go-Explore) spends ticks going back to the states it found and acting from them at random, and reports every event it reached with a replay of the shortest way there. Red when it never reaches the win (whenever a rule can win the game) or an event in require. Give one of tour, steps, bot or explore. |
ticks required | integer | How many simulation ticks to run; for explore, how many the explorer may spend in all. |
runs | integer | Play it this many times: on its seed, then on seeds derived from it (deriveSeed(seed, "runs/1"), "runs/2"...), each a new game. A bot fuzzes this way ({bot: monkey, runs: 4, ticks: 3600}). Every run must end free of errors, broken invariants and failed asserts; an event in require needs one run to reach it. |
skill | "novice" | "casual" | "skilled" | "perfect" | The hands of a bot that plays like a person (kit.platformer's playthrough): novice, casual (the default), skilled, or perfect (no reaction time, no timing error, no fumbles: it plays each level's witness exactly). Only with such a bot. |
goals | list of a seek goal like make:rcp.campfire, reach:tarn or fight | What the seek bot works toward, in order, by the obvious route and with a person's reaction time and fumbles: reach:<place>, near:<archetype id>, take:<prop kind>, have:<item id>, make:<recipe id>, make (the first thing a person would make: a recipe made by hand, needing a tool last, then the fewest items to come by; met at once in a game with no recipe), use:<item id> (comes by one if it carries none, as have:, then uses it as the carry app does: an Item with a use, eaten, drunk, lit), talk:<archetype id> (walks up to the nearest of them, uses it and reads its Dialogue to the end, taking the first choice on offer; talk alone: the nearest usable with a Dialogue), fight (the nearest foe), survive:<seconds> (stands until that many seconds of play have passed, or a win), win (waits for it). It works on the first goal not yet met, and is done when every goal is met; a goal may come twice (talk:arch.warden, have:item.bell, talk:arch.warden). With survive:<seconds>, the player downed or killed, or the game lost, before those seconds fails the goal being worked on and ends the run there; a lost game fails win. |
assert | list of boolean or an expression like self.health < 30 | What must hold where the run ends, checked by the engine's own Expr VM: self (the player), game, var and tune, the built-ins and any kit function its kit.json marks pure, like var.fires_lit >= 1, item_count(self, 'item.scrap') >= 1 or platformer_done('plevel.meadow'). A function that may change the game is refused. Not with explore, which has no one end: its gate is require. |
require | list of an event id like crafting.made, or game.end:<outcome> | Events the run must reach: an event id (crafting.made), or game.end:<outcome> (game.end:win). Checked in the trace of a tour or bot run (with runs, one run reaching it is enough), and against what the explorer reached. |
metrics | list of object | Trace metrics and the range they must fall in. |
Recipe
<folder>/<slug>.recipe.json, in any folder of the pack
ids: a recipe id like rcp.campfire
schema: schemas/v1/recipe.schema.json
How to make something: what it consumes, what it makes or builds, where, with which tool and in how many world minutes.
| Field | Type | What it is |
|---|---|---|
station | an archetype id like arch.sentry | The archetype to stand at (a campfire, a bench). Left out, it is made by hand anywhere. |
inputs required | list of item count | What it consumes. |
outputs required | list of item count | What it makes. Empty when it only builds. |
builds | an archetype id like arch.sentry | An archetype placed in front of the maker (a campfire). |
timeMin | number | World minutes it takes. |
tool | object | A tool the maker must carry: an item whose tool verb matches, with at least this hardness. |
Room
<folder>/<slug>.room.json, in any folder of the pack
ids: a room id like room.meadow
schema: schemas/v1/room.schema.json
One room of a 2D platformer (kit.platformer): a screen of tiles written as rows of characters, and a legend saying what each character is: terrain (empty, solid, a one-way platform, a ladder, spikes), a thing to place (an archetype: a collectible, a checkpoint, the exit, an enemy) or the player's start. PlatformLevels place rooms on a grid, by hand or with the rooms.chain generator, which reads openSides and roles to build a path that is completable by construction; bake then proves it with the core's reachability search. Row 0 is the top of the room.
| Field | Type | What it is |
|---|---|---|
sizeTiles required | list of integer | [width, height] in tiles. Every room of one PlatformLevel has that level's roomTiles. |
rows required | grid rows like 5#/#@o.#/5#: rows top to bottom split by /, where a number before a character repeats it | The cells, top row first. Each row expands to exactly width characters; 3# is ###. Digits and / are never legend characters. |
legend required | list of room legend entry | What each character of rows is. Every character used must be here. |
openSides | list of "left" | "right" | "up" | "down" | The edges this room leaves open for a neighbour, which rooms.chain relies on when it lays a path through it: left and right openings are walked or jumped through, down is dropped through into the room below, up is dropped into from the room above. Keep openings where a neighbour's will meet them (left and right near the floor, up and down in the middle columns). |
roles | list of "start" | "path" | "exit" | "filler" | What rooms.chain may place this room as: start (the first room of the path; needs a start cell), path (a room the path passes through), exit (the last room; needs a thing whose platformer_touch role is exit) and filler (off the path). Left out, the room is never generated into a level and is only placed by hand. |
RuleSet
<folder>/<slug>.rules.json, in any folder of the pack
ids: a rule set id like rules.fires
schema: schemas/v1/rules.schema.json
Event → where → do (ADR-0003, L2). When an event happens and where holds, the rule does its actions in order. Rules never wait; waiting belongs in a state machine.
| Field | Type | What it is |
|---|---|---|
rules required | list of object |
Score
<folder>/<slug>.score.json, in any folder of the pack
ids: a score id like score.night_coast
schema: schemas/v1/score.schema.json
A game's music as a score kit.music renders at load (docs/design/m6-presentation.md §2): a key, a mode, a tempo and a meter; sections of whole bars, each with its chords; and stems, each voiced by a Sound (a synth.layered or synth.sfx recipe played at each note's pitch) and playing a generator or explicit notes. Every stem renders to exactly bars x beats per bar x 60 / tempo x rate samples and loops without a click. Stems play together; a layer turns stems up and down from the game (an expression over self, game, var and tune, eased per bar), and the first section whose when holds plays, switching on a bar line. Music only presents the game: it never changes it.
| Field | Type | What it is |
|---|---|---|
key required | a pitch class like C, F# or Bb | The tonic: what scale degree 1 and chord I are. |
mode required | "major" | "minor" | "dorian" | "phrygian" | "lydian" | "mixolydian" | "locrian" | "harmonic_minor" | "pentatonic_major" | "pentatonic_minor" | The scale degrees and chords are drawn from. minor is natural minor; the pentatonics keep melodies to five safe notes, and their chords come from major and minor. |
tempo required | integer | Beats a minute, the beat being the meter's lower number. |
meter | a meter like 4/4, 3/4 or 6/8 | Beats a bar over the beat's note value. |
rate | integer | Samples a second every stem renders at. 22050 is plenty for synthesized music and bakes twice as fast as 44100. |
seed | integer | Varies what the generators choose (random arpeggios, humanized hits) without changing the score. |
sections required | list of object | The score's parts. At every bar line the first section whose when holds plays (a section without when always holds, so put the default last). A section loops until another takes over. |
layers | list of object | Volumes stems follow: each a level from 0 (silent) to 1 (full), read from the game and eased toward over easeBars bars. |
stems required | list of object | The parts that play together: a bass, a pad, an arpeggio, drums. Each renders once per section it plays in. |
Screen
<folder>/<slug>.screen.json, in any folder of the pack
ids: a screen id like ui.hud
schema: schemas/v1/screen.schema.json
A screen or HUD: a flat list of widgets placed by anchor and nested by parent id, bound to game state by expressions or fed by the UI's feeds. Drawn in whole multiples of the Style's pitch.
| Field | Type | What it is |
|---|---|---|
frame | "slate" | "none" | "veil" | slate draws the screen as an app on the slate tablet; none draws its widgets straight over the game; veil also dims the world behind the whole screen (the title). |
open | an input action id like use or dodge | The input action that opens this app on the slate (carry, make). |
verb | string | What confirm does in this app, shown in the key strip (make, hold). The engine says use when it is left out. |
widgets required | list of object |
Sound
<folder>/<slug>.sound.json, in any folder of the pack
ids: a sound id like snd.hit
schema: schemas/v1/sound.schema.json
A Tier 1 sound recipe: a synth builder from a kit (synth.sfx, synth.layered, synth.bed) and its parameters, the mix category it plays in, its level, how many variants, and whether it loops. Baked to buffers at load.
| Field | Type | What it is |
|---|---|---|
builder required | "campfire.ring" | "driftwood.log" | "figure.person" | "machine.sentry" | "mesh.parts" | "rock.faceted" | "sound.file" | "sprite.baked" | "sprite.sheet" | "synth.bed" | "synth.layered" | "synth.sfx" | "tree.tiered" | "ui.theme" | "wreck.hull" | A builder a kit registers that makes a sound. |
params | value | The builder's fields: an object checked against the builder named in builder (vocab.schema.json, Builder<Id>Params). Left out, every field takes its default. |
category | a slug: lowercase letters, digits and _, starting with a letter (at most 48) | The mix-sheet row it plays in: bed, machine, event, step, ui, music (a Score's stems), or a row the Style adds. |
levelDb | number | Level relative to its category. |
variants | integer | How many takes to bake; each play picks one. |
loop | boolean | Loops seamlessly (synth.bed always loops). |
caption | string | What a person who can't hear it reads when they turn Captions on, in a few words: beast snorts, bell rings. Written without brackets (the player adds them). Caption the sounds that tell the player something they could miss, a warning above all; leave out their own steps and clicks. One-shots only: a loop or bed is never captioned. |
Sprite
<folder>/<slug>.sprite.json, in any folder of the pack
ids: a sprite id like sprite.hero
schema: schemas/v1/sprite.schema.json
A Tier 1 sprite recipe: a builder that renders art for 2D games (sprite.baked renders a Model recipe from a fixed orthographic camera into pixel-art frames for named animations), its parameters, and the colours its palette snaps to. The player bakes it at load into one atlas with frame metadata, cached by the recipe's hash, so 2D games get their art from the same recipes as 3D ones. Archetypes show it through the sprite component.
| Field | Type | What it is |
|---|---|---|
builder required | "campfire.ring" | "driftwood.log" | "figure.person" | "machine.sentry" | "mesh.parts" | "rock.faceted" | "sound.file" | "sprite.baked" | "sprite.sheet" | "synth.bed" | "synth.layered" | "synth.sfx" | "tree.tiered" | "ui.theme" | "wreck.hull" | A builder a kit registers that makes an image or a tileset (sprite.baked). |
params | value | The builder's fields: an object checked against the builder named in builder (vocab.schema.json, Builder<Id>Params). Left out, every field takes its default. |
palette | list of object | Extra colours every pixel may snap to, besides the shades of the model's own palette (an outline colour, a highlight the model lacks). |
StateMachine
<folder>/<slug>.sm.json, in any folder of the pack
ids: a state machine id like sm.sentry
schema: schemas/v1/sm.schema.json
A brain as a flat list of states (ADR-0003, L2): patrol → notice → hunt → attack → forget. States may nest by parent id; a parent's transitions apply in all its children. A transition fires on an event, when a condition holds, or after some seconds in the state.
| Field | Type | What it is |
|---|---|---|
initial required | a slug: lowercase letters, digits and _, starting with a letter (at most 48) | The state it starts in. |
states required | list of object | |
transitions | list of object |
Stats
<folder>/<slug>.stats.json, in any folder of the pack
ids: a stats id like stats.core
schema: schemas/v1/stats.schema.json
Declares the numbers entities carry (stats, read as self.health) and the game's variables (read as var.fires_lit). Expressions are checked against these names.
| Field | Type | What it is |
|---|---|---|
stats | list of object | Stats every entity may carry. An archetype gives each a base value; actions change it within min and max. |
vars | list of object | Game variables: one value each for the whole game, read as var.<id> and changed by actions. |
Style
<folder>/<slug>.style.json, in any folder of the pack
ids: a style id like style.tideline
schema: schemas/v1/style.schema.json
How the game looks and sounds (ADR-0006): the render profile, the palette every @slot names, material families, sky and fog, the light budget, UI tokens, the audio mix and the juice (vfx). Only presentation reads it, so restyling a game is one document.
| Field | Type | What it is |
|---|---|---|
profile required | "lit_ortho3d" | "flat_2d" | The render profile that draws the game. lit_ortho3d: lit 3D under a fixed orthographic camera, in the Compatibility renderer (ADR-0007). flat_2d: the kits' own 2D views draw it (tiles from the side, a grid of cells, a world from above under a topdown2d CameraRig) and no 3D scene is built; the palette, UI tokens and audio mix apply as ever. |
palette | list of object | Named colours. Content refers to them as @slot (@bark, @turf), never by value. Some views also read slots by name (the flat grid view's backdrop, backdrop_top and backdrop_bottom): the vocabulary lists each view's palette. |
materials | list of object | Materials content can be made of. The family says how the profile lights it: made (land, wood, cloth: rough and matte, nothing quite straight), found (machine metal: panelled, ruled, with specular seams), water, foliage, emissive (gives light). |
sky | object | Light and air through the day. Night is the moon colour with the night ambient. |
lights | object | Point lights: how many the profile keeps lit at once (nearest first), and the warm and cold light colours. |
ui | object | The look of the UI. |
audio | object | The mix: category rows, the ambient beds, the sounds of events and the score. |
vfx | object | Juice: what the game shows when something happens, drawn by kit.vfx. Each effect answers an event with particles where it happened, a flash or a squash on the entity it names, and a shake of the view. Effects are looks: they never change play, and the person can turn shake down and flashes off in the game's Options. |
Theme
<folder>/<slug>.theme.json, in any folder of the pack
ids: a theme id like theme.drowned_coast
schema: schemas/v1/theme.schema.json
What the game is about (ADR-0006): its fiction, tone, what it never does, and how it names things. Mechanics never read it; the Director and generators do.
| Field | Type | What it is |
|---|---|---|
fiction required | string | The world in a paragraph: where, when, who, and what went wrong. |
tone | string | How it feels, in a few words: quiet, tense, wry. |
never | list of string | Things this game never does or shows. |
naming | string | How things are named: the words, sounds and patterns names use. |
Timeline
<folder>/<slug>.timeline.json, in any folder of the pack
ids: a timeline id like tl.intro
schema: schemas/v1/timeline.schema.json
Things that happen one after another over time (ADR-0003, L2): a cutscene, a boss's phases, a gate that grinds open and then lets the wave in. Steps run in order in the simulation, counted in ticks and never by the clock on the wall, so a Timeline replays, saves and loads like the rest of the game. It starts on an event, as a rule does (on, where, once), with that event's self, target and fields, or from the start_timeline action, with the self it names. Each tick it does steps until one must wait: wait some seconds, until a condition holds (a boss's health under a threshold), say a line for as long as it takes to read, or move an entity there over some seconds. do runs actions at once (set_var, spawn, despawn, give, take, play_cue, set_state, face, start_timeline, end_game, any kit's action); camera points the views' camera at something or shakes it. A cutscene takes the player's control while it runs; a skippable one ends at once on the skip press, doing what is left of its actions and moves without waiting. Rules hear timeline.started and timeline.done.
| Field | Type | What it is |
|---|---|---|
on | an event name like melee.hit or game.start: lowercase dotted words | An event that starts it, as a rule's on does: game.start, entity.spawned, melee.hit, a variable's own emit. Its steps read that event's fields (event.<field>), and its self and target when the event carries them. Left out, only the start_timeline action starts it. A Timeline already running for the same self is not started again. |
where | boolean or an expression like self.health < 30 | It starts on its event only when this holds, read as a rule's where, with the event's self, target and fields: event.arch == 'arch.warden'. |
once | boolean | It runs at most once a game: once started, nothing starts it again. A cutscene seen once, a boss's phases. |
cutscene | boolean | The player has no control while it runs: walking, using and fighting keys do nothing, and cutscene() is true, for a Screen that hides the HUD. The rest of the world goes on, so keep a cutscene to a quiet moment, or hold the dangerous ones still with set_state. |
skippable | boolean | A cutscene the player may end at once with the skip press (the InputMap's skip action, or confirm when it has none): the do steps left run and the moves left end where they were going, without waiting; lines, waits and camera steps are passed over. Only a cutscene is skipped. |
steps required | list of step | What it does, in order. Each step's id is unique in the Timeline; a tour awaits timeline.step:<timeline id>/<step id> as a step begins. |
Tuning
<folder>/<slug>.tuning.json, in any folder of the pack
ids: a tuning id like tune.combat
schema: schemas/v1/tuning.schema.json
Tuning knobs: named numbers with a range and a reason, read by expressions as tune.<id>. Bot sweeps turn them within their ranges; the reason says what the number is for, so nobody changes it blind.
| Field | Type | What it is |
|---|---|---|
knobs required | list of object |
WorldGen
<folder>/<slug>.worldgen.json, in any folder of the pack
ids: a world generator id like gen.tideline
schema: schemas/v1/worldgen.schema.json
A world generator: a flat list of passes from kits (noise.height, scatter.props …), each with its own named seed stream, so adding a pass never moves another pass's output (ADR-0004). Editing it changes the world stamp, and old saves are refused by name.
| Field | Type | What it is |
|---|---|---|
sizeTiles required | list of integer | [width, height] of the world in tiles. |
chunkTiles | integer | Side of a streamed chunk in tiles. |
biome required | a biome id like biome.coast | The biome it builds. |
passes required | list of pass | The passes, each {"pass": "<kit pass>", "id": "<its name here>", "after": [ids], …fields}. |
Made by the tools, or shipped with the engine (5)
Bundle
<pack>/.gamewright/build/<hash12>/bundle.json, written by bake
ids: a document id like item.iron_sword: a lowercase kind prefix, a dot, then lowercase letters, digits, _ and .
schema: schemas/v1/bundle.schema.json
bundle.json: the manifest of a baked game. The core writes it; the player checks it before loading anything (ADR-0002). content.json holds every resolved document; exprs.json the compiled expressions; media/ the files, named by content hash. id is the game's GameManifest id.
| Field | Type | What it is |
|---|---|---|
engine required | object | The player versions that can run this bundle: min inclusive, below exclusive. |
template required | object | |
kits required | list of object | Every kit the game uses, with the version it was baked against. |
content required | file ref | |
exprs required | file ref | |
documentCount | integer | |
media required | list of object | |
stamp required | object | The world stamp (ADR-0004): a hash of what shapes generated worlds. A save made under another stamp is refused by name or migrated. |
bakedBy required | object |
ChangeSet
<pack>/.gamewright/changes/<id>.json, written by the Director's tools
ids: a change id like chg.20260930_sword_damage
schema: schemas/v1/changeset.schema.json
A proposed, reviewable change to a pack. Every tool that changes anything, whether driven by the agent or the user, produces one. Nothing touches the pack until it is approved. Operations carry the base hash of the file they change, so a proposal made against an older version is marked stale instead of overwriting newer work. Undo applies a new change set that is the inverse of an applied one; history is never rewritten. Change sets live in .gamewright/changes/ (and, when the project is a git repo, applied ones become commits). Unlike pack documents, a ChangeSet has its own lifecycle status instead of meta.status. Adapted from Reelwright's changeset.schema.json (MIT; see core/UPSTREAM.md).
| Field | Type | What it is |
|---|---|---|
title required | string | One line, imperative: 'Make the iron sword slower and stronger'. |
rationale required | string | Why, in a few sentences: what was asked and how this answers it. |
author required | object | |
createdAt required | an RFC 3339 date-time like 2026-09-30T00:00:00Z | |
provenance required | provenance | Where this document or asset came from. Required on everything, so the project can always answer who made this, with what, and under which license. |
status required | "proposed" | "approved" | "applied" | "rejected" | "reverted" | "stale" | "superseded" | proposed: awaiting review. approved: accepted, not yet written. applied: written to the pack. rejected: declined by the person. reverted: undone by a later change set. stale: a base file changed since it was proposed. superseded: the agent replaced its own pending proposal with a newer one (supersededBy) before anyone decided it; it is history, not a decision. |
history | list of object | |
tool | object | Agent or CLI tool that produced it. |
base | object | |
operations required | list of operation | |
previews | list of object | Images that show the change: screenshots, before/after pairs, diffs. |
impact | list of object | What else the dependency graph says this change affects. |
variation | object | Set when this is one of several alternatives ('give me 4 options'). Approving one rejects its siblings. |
inverseOf | a change id like chg.20260930_sword_damage | The applied change set this one undoes. |
draft | true | The person's own change held to the draft rule (docs.edit's draft, and the undo of the person's own changes): it may keep game-rule problems in the documents it writes, never a schema error there or a new problem in another document. Redo holds it to the same rule. |
supersedes | a change id like chg.20260930_sword_damage | The agent's earlier pending proposal this one replaces (the propose tools' replaces); that one is now superseded. |
supersededBy | list of a change id like chg.20260930_sword_damage | Set when the status is superseded: the proposal that replaced this one (the options, when a variation group did). |
after | list of a change id like chg.20260930_sword_damage | Pending proposals this one builds on, in the order they apply: it was made and checked against the pack as it would be with them applied, and it can be approved only once they are. A base that is rejected or goes stale leaves this one stale. |
createdDirs | list of a relative path inside the pack, like levels/forest.level.json | Folders that applying this change created, deepest last. Undoing it removes them again when they are empty. |
diagnostics | list of object | Validation results for the proposed documents (warnings only; errors never reach a proposal). |
verification | VerifyReport document | The verification report for the pack as this change would leave it (docs/design/m2-director.md §5): L0 always; L1 and L2 when it touches simulation documents, with every Playtest re-run and compared with the pack before it. Written by the validators and the engine after proposal, never by the proposing tool. |
cost | object | What producing this proposal cost. |
Kit
engine/addons/gamewright/kits/<kit>/kit.json, beside the kit's engine code
ids: a kit id like kit.melee: kit, a dot, then lowercase letters, digits and _
schema: schemas/v1/kit.schema.json
A kit's kit.json: the vocabulary a kit of engine code adds to Gamewright. The core generates vocab.schema.json and the Expr symbol table from every kit's registration, so documents, the Director's tools and the runtime always agree (ADR-0003). Written by engine developers, never by the Director.
| Field | Type | What it is |
|---|---|---|
version required | a semantic version like 1.2.0 | |
description required | string | What the kit makes possible and when to use it, written as a prompt for the Director. |
requires | list of a kit id like kit.melee: kit, a dot, then lowercase letters, digits and _ | Kits this kit depends on. kit.core is always present and need not be listed. |
systems | list of system decl | Simulation systems. The player orders them by after, provides and consumes; a cycle is an error. |
views | list of view decl | Presentation nodes (GwView) that draw the kit's state. Views never write to the simulation. |
structs | list of struct decl | Named records that fields can use (type struct or list). Flat: a struct may use another struct by id, never itself, directly or through others. |
components | list of vocab decl | Components an Archetype can carry. Each becomes a branch of the component union, discriminated by "component": "<id>". |
actions | list of vocab decl | Actions rules, state machines, dialogue and timelines can perform. Each becomes a branch of the action union, discriminated by "action": "<id>". |
events | list of event decl | Events the kit emits, with their fields. Rules listen with on: "<id>"; Expr reads event.<field>. |
functions | list of function decl | Functions Expr can call. Pure: they read state and never change it. |
passes | list of vocab decl | World generator passes a WorldGen document can list, discriminated by "pass": "<id>". |
tasks | list of vocab decl | Behavior tree tasks, discriminated by "task": "<id>". |
builders | list of builder decl | Tier 1 asset builders a Model, Sound or other recipe document can name, discriminated by "builder": "<id>". Versioned: a change to a builder's output is a new major. |
tourVerbs | list of tour verb decl | Verbs the kit adds to the tour language. |
bots | list of bot decl | Bots the kit registers: drivers that play the player's part by input alone, named by a Playtest's bot and by the engine CLI's sim --bot. |
saveKeys | list of a slug: lowercase letters, digits and _, starting with a letter (at most 48) | Keys this kit's systems write into a save. Unique across all kits. |
Template
library/templates/<name>/template.json, with the documents it supplies
ids: a template id like tpl.action_survival
schema: schemas/v1/template.schema.json
A genre template (ADR-0006): what a game plays like. It lives in library/templates/<name>/template.json, next to the default documents it supplies. A game pins one template by id and version; bake merges the template's documents with the game's, and a game document with the same id replaces the template's whole.
| Field | Type | What it is |
|---|---|---|
version required | a semantic version like 1.2.0 | This template's version. A change that alters how existing games play is a new major; games pin a major ("1") or an exact version. |
useWhen | list of string | When a game fits this template, one situation per entry, so the Director can match a brief to it: "the player survives against a place and what hunts in it". |
notFor | list of string | What this template is the wrong start for, and what to pick instead. |
kits required | list of a kit id like kit.melee: kit, a dot, then lowercase letters, digits and _ | The kits every game on this template runs. kit.core is always present. A game may add kits in its GameManifest. |
defaults | list of a document id like item.iron_sword: a lowercase kind prefix, a dot, then lowercase letters, digits, _ and . | Ids of the documents this template supplies (the files in its folder), so a game starts playable and replaces them one by one. |
knobs | list of a slug: lowercase letters, digits and _, starting with a letter (at most 48) | Ids of the tuning knobs (in the template's Tuning documents) the Director may turn for a game on this template. |
questions | list of object | What the Director must ask the person before making a game on this template. |
manifest | object | What a game on this template starts with when its manifest (game.json) leaves it out: who the player is and where they start, the screens, the menu and the clock. Bake fills each missing field from here, so a game.json that only pins the template boots into play. |
acceptance | list of object | What every game on this template must show when bots play it: the template's acceptance metrics. |
VerifyReport
written by gamewright verify and on every proposal
schema: schemas/v1/verify.schema.json
What the verification ladder found (ARCHITECTURE §8): one report for a pack, or for a proposal as it would leave the pack. Rungs run in order and a rung runs only when the ones below it are green: L0 spec (schemas, semantics, static analysis), L1 boot (600 idle ticks twice, the same state, a clean log), L2 play (every Playtest, with metrics from its trace, engine invariants on every tick, and the explorer's reachability), L3 frames (named shots, with property checks and the readability of their subjects), and, only when asked for by name, L4 judge (a vision model asked grounded questions about L3's frames: where is the player, the threat, the goal; what is the player supposed to do) and L5 web (every Playtest tour in headless Chromium on the web export, ending on the native run's state hash); L4 judges L3's frames even when L3 is red. Every value comes from a validator or the engine, never from the agent. A proposal's report also compares every Playtest with the pack as it was, and a scenario that was green before and is red after is a regression, named first in the headline. A proposal that touches only presentation must leave every state hash as it was (restyle). The Director reads this, never raw logs.
| Field | Type | What it is |
|---|---|---|
ok required | boolean | True when every rung that ran passed or had nothing to check, and nothing regressed. |
headline required | string | One or two sentences for the model and the person. Regressions come first, as REGRESSION: … |
game | a document id like item.iron_sword: a lowercase kind prefix, a dot, then lowercase letters, digits, _ and . | The game's GameManifest id. |
bundle | bundle ref | The baked bundle the engine ran. |
change | change scope | The proposal this report is for, and why these rungs ran. |
restyle | restyle | What a presentation-only proposal proves (docs/design/m6-presentation.md §1): the simulation never reads a look, so L1's state hash and every L2 scenario's final state hash must equal the same runs' on the pack before it, bit for bit. Any difference turns that rung red with restyle.changed_play. |
regressions | list of regression | Scenarios that were green before the change and are red after it. |
rungs required | list of rung | The rungs asked for, in order. |
signals | list of string | Soft signals (research/testing-verification.md §5.2): worth a look, never a failure until calibrated. |
engineGaps | list of string | What the engine can't do yet that this report would have used, as contracts (an option, a result field). |