{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "anyOf": [
    {
      "additionalProperties": false,
      "$id": "resource",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "node"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "resource",
          "type": "string"
        },
        "node": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "output",
            "yield",
            "active_ms",
            "respawn_ms",
            "count",
            "sprite",
            "level",
            "place"
          ],
          "properties": {
            "output": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
              "maxLength": 80,
              "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
              "type": "string"
            },
            "yield": {
              "minimum": 1,
              "maximum": 20,
              "description": "Units gathered per execution at perfect timing.",
              "type": "integer"
            },
            "active_ms": {
              "minimum": 0,
              "maximum": 3600000,
              "description": "Time the player is busy gathering once.",
              "type": "integer"
            },
            "respawn_ms": {
              "minimum": 0,
              "maximum": 3600000,
              "description": "Time before the same plant gives again. Waiting is never paid.",
              "type": "integer"
            },
            "count": {
              "minimum": 1,
              "maximum": 12,
              "description": "How many of these nodes the slot holds.",
              "type": "integer"
            },
            "sprite": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
              "maxLength": 120,
              "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
              "type": "string"
            },
            "sprite_empty": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
              "maxLength": 120,
              "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
              "type": "string"
            },
            "growth": {
              "minItems": 1,
              "maxItems": 3,
              "description": "Young stages of a node that grows back, from the youngest up (after `sprite_empty`, before `sprite`). The client shows the stage by how much of the respawn time has passed. Needs `sprite_empty`.",
              "type": "array",
              "items": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              }
            },
            "solid": {
              "description": "The node blocks the way, ready or not, as a tree does: a stump stays in the way until the tree is grown again. Left out: the player walks through it.",
              "type": "boolean"
            },
            "opens_after": {
              "minItems": 1,
              "maxItems": 4,
              "description": "Quests the node waits for, each with the line its refusal says. The node cannot be struck by a character who was offered one of them and has not finished it; a character who was offered none strikes the node as usual. Two entries may not both be offered to one wake pocket. The refusal costs nothing.",
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "quest"
                ],
                "properties": {
                  "quest": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "line": {
                    "minLength": 1,
                    "maxLength": 80,
                    "description": "The one short line the refusal says. Left out: \"It is not ready yet.\"",
                    "type": "string"
                  }
                }
              }
            },
            "tool": {
              "anyOf": [
                {
                  "const": "basket",
                  "type": "string"
                },
                {
                  "const": "trowel",
                  "type": "string"
                },
                {
                  "const": "axe",
                  "type": "string"
                },
                {
                  "const": "staff",
                  "type": "string"
                },
                {
                  "const": "hammer",
                  "type": "string"
                },
                {
                  "const": "pick",
                  "type": "string"
                },
                {
                  "const": "rod",
                  "type": "string"
                }
              ]
            },
            "work": {
              "description": "How the node is worked when it needs no tool. \"mine\": broken with blows that run by themselves after one press, as felling does; it trains digging, a pick or a hammer in the bag makes it faster (the best one, worn a little each time), and bare hands still do it. Left out: gathered by hand with the minigame of `game`.",
              "const": "mine",
              "type": "string"
            },
            "level": {
              "minimum": 1,
              "maximum": 20,
              "description": "Level required, in the profession the node trains: felling when the tool is an axe, fishing when it is a rod, digging when the node is mined, foraging otherwise.",
              "type": "integer"
            },
            "game": {
              "description": "The minigame played when gathering: \"timing\" (stop a marker in the middle, the default), \"strike\" (three timed hammer strikes, for stone), \"pick\" (press only for the ripe ones, for plants), \"snap\" (press fast, for wood) or \"fish\" (cast, wait for the bite and strike as the float goes under, then reel in; for a node fished with a rod).",
              "anyOf": [
                {
                  "const": "timing",
                  "type": "string"
                },
                {
                  "const": "strike",
                  "type": "string"
                },
                {
                  "const": "pick",
                  "type": "string"
                },
                {
                  "const": "snap",
                  "type": "string"
                },
                {
                  "const": "fish",
                  "type": "string"
                }
              ]
            },
            "hours": {
              "description": "Hours of the in-game day when it can be gathered, [from, to).",
              "type": "array",
              "items": [
                {
                  "minimum": 0,
                  "maximum": 23,
                  "type": "integer"
                },
                {
                  "minimum": 1,
                  "maximum": 24,
                  "type": "integer"
                }
              ],
              "additionalItems": false,
              "minItems": 2,
              "maxItems": 2
            },
            "place": {
              "additionalProperties": false,
              "description": "An empty resource slot of the map (`count` nodes inside it), and optionally single-node slots scattered over the map.",
              "type": "object",
              "required": [
                "slot"
              ],
              "properties": {
                "slot": {
                  "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                  "maxLength": 80,
                  "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                  "type": "string"
                },
                "scatter": {
                  "minItems": 1,
                  "uniqueItems": true,
                  "description": "More resource slots that each hold exactly one of these nodes: a tree or a log standing where the map already shows one. They add supply only: `count`, the value and the opening's log floor follow the main slot.",
                  "type": "array",
                  "items": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                },
                "water": {
                  "description": "The nodes lie on the water tiles of the slot that touch the land, and are worked from the bank: fishing spots. Left out, they lie on the land tiles of the slot.",
                  "const": true,
                  "type": "boolean"
                }
              }
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "treasure",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "treasure",
          "type": "string"
        },
        "dig": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "hint",
            "active_ms",
            "respawn_ms",
            "count",
            "level",
            "finds",
            "place"
          ],
          "properties": {
            "hint": {
              "minLength": 1,
              "maxLength": 240,
              "type": "string"
            },
            "active_ms": {
              "minimum": 0,
              "maximum": 3600000,
              "description": "Time the player is busy digging once.",
              "type": "integer"
            },
            "respawn_ms": {
              "minimum": 0,
              "maximum": 3600000,
              "description": "Time before the spot can be dug again.",
              "type": "integer"
            },
            "count": {
              "minimum": 1,
              "maximum": 8,
              "type": "integer"
            },
            "tool": {
              "anyOf": [
                {
                  "const": "basket",
                  "type": "string"
                },
                {
                  "const": "trowel",
                  "type": "string"
                },
                {
                  "const": "axe",
                  "type": "string"
                },
                {
                  "const": "staff",
                  "type": "string"
                },
                {
                  "const": "hammer",
                  "type": "string"
                },
                {
                  "const": "pick",
                  "type": "string"
                },
                {
                  "const": "rod",
                  "type": "string"
                }
              ],
              "description": "The tool the dig asks for and wears. Left out: the find is picked up by hand, with no timing window and no tool worn."
            },
            "level": {
              "minimum": 1,
              "maximum": 20,
              "description": "Digging level required.",
              "type": "integer"
            },
            "finds": {
              "minItems": 1,
              "maxItems": 8,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "item",
                  "chance_pct",
                  "share_pct"
                ],
                "properties": {
                  "item": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "chance_pct": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Chance of this find per dig, at perfect timing. Chances of one spot sum to at most 100.",
                    "type": "integer"
                  },
                  "share_pct": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Part of the dig cost this find carries. Shares sum to 100.",
                    "type": "integer"
                  }
                }
              }
            },
            "place": {
              "additionalProperties": false,
              "description": "An empty slot from the ruins map. Only one extension is active per slot.",
              "type": "object",
              "required": [
                "slot"
              ],
              "properties": {
                "slot": {
                  "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                  "maxLength": 80,
                  "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                  "type": "string"
                }
              }
            },
            "pocket": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
              "maxLength": 80,
              "description": "A wake pocket's own spot: only a character who woke in this pocket sees it and digs it, once, and it always gives its one find. A gift of the start, so it prices nothing (it is no source in the value table).",
              "type": "string"
            }
          }
        },
        "search": {
          "additionalProperties": false,
          "description": "A searchable: a table of rows drawn once a day. It takes no tool and has no timing window. A treasure file carries `search` or `dig`, never both.",
          "type": "object",
          "required": [
            "rows",
            "place"
          ],
          "properties": {
            "rows": {
              "minItems": 1,
              "maxItems": 8,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "item",
                  "chance_pct"
                ],
                "properties": {
                  "item": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "chance_pct": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Chance of this row being inside after a refill. Each row is drawn on its own; a rare or legendary row is tilted by luck like a dig's rare find.",
                    "type": "integer"
                  }
                }
              }
            },
            "place": {
              "additionalProperties": false,
              "description": "The map prop (its `id`) that can be searched: pressing A beside it opens a window of what is inside. Nothing is put into it; it refills once a day at dawn (docs/design/SEARCH.md).",
              "type": "object",
              "required": [
                "prop"
              ],
              "properties": {
                "prop": {
                  "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                  "maxLength": 80,
                  "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "recipe",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "recipe"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "recipe",
          "type": "string"
        },
        "recipe": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "station",
            "active_ms",
            "level",
            "inputs",
            "outputs",
            "known"
          ],
          "properties": {
            "station": {
              "anyOf": [
                {
                  "const": "fire",
                  "type": "string"
                },
                {
                  "const": "workshop",
                  "type": "string"
                }
              ]
            },
            "active_ms": {
              "minimum": 0,
              "maximum": 3600000,
              "description": "Time the player is busy making it once.",
              "type": "integer"
            },
            "level": {
              "minimum": 1,
              "maximum": 20,
              "description": "The recipe's level in its profession: each missing level takes craft_level_step_bp off the chance (no wall). It sits inside the level band of the recipe's tier.",
              "type": "integer"
            },
            "profession": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
              "maxLength": 80,
              "description": "The profession that trains this recipe and sets its chance. Left out: the profession that works its station.",
              "type": "string"
            },
            "inputs": {
              "minItems": 1,
              "maxItems": 6,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "item",
                  "qty"
                ],
                "properties": {
                  "item": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "qty": {
                    "minimum": 1,
                    "maximum": 999,
                    "type": "integer"
                  }
                }
              }
            },
            "outputs": {
              "minItems": 1,
              "maxItems": 4,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "item",
                  "qty",
                  "share_pct"
                ],
                "properties": {
                  "item": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "qty": {
                    "minimum": 1,
                    "maximum": 999,
                    "description": "Units produced per execution at perfect timing.",
                    "type": "integer"
                  },
                  "share_pct": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Part of the activity cost this output carries. Shares of one activity sum to 100.",
                    "type": "integer"
                  }
                }
              }
            },
            "miss": {
              "additionalProperties": false,
              "description": "What a missed craft leaves (G40): part of the inputs and nothing else, worth less than a success. Left out: the default share of the inputs. When the share leaves nothing, one unit of the most valuable input comes back.",
              "type": "object",
              "properties": {
                "keep_pct": {
                  "minimum": 0,
                  "maximum": 90,
                  "description": "Part of each input a miss keeps, rounded down, at most 90 percent. Left out: params.craft_miss_keep_pct.",
                  "type": "integer"
                }
              }
            },
            "known": {
              "description": "\"start\": every player knows it. \"found\": learned from a recipe scrap found by digging, or from a quest.",
              "anyOf": [
                {
                  "const": "start",
                  "type": "string"
                },
                {
                  "const": "found",
                  "type": "string"
                }
              ]
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "template",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "template"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "template",
          "type": "string"
        },
        "template": {
          "additionalProperties": false,
          "description": "A recipe template: the assembler turns it into one recipe for every combination of items that fit its class inputs, in a fixed order and never more than params.template_max_recipes. Every recipe it makes is known from the start.",
          "type": "object",
          "required": [
            "station",
            "active_ms",
            "level",
            "inputs",
            "outputs"
          ],
          "properties": {
            "station": {
              "anyOf": [
                {
                  "const": "fire",
                  "type": "string"
                },
                {
                  "const": "workshop",
                  "type": "string"
                }
              ]
            },
            "active_ms": {
              "minimum": 0,
              "maximum": 3600000,
              "description": "Time the player is busy making it once.",
              "type": "integer"
            },
            "level": {
              "minimum": 1,
              "maximum": 20,
              "description": "The recipe's level in its profession: each missing level takes craft_level_step_bp off the chance (no wall). It sits inside the level band of the recipe's tier.",
              "type": "integer"
            },
            "profession": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
              "maxLength": 80,
              "description": "The profession that trains this recipe and sets its chance. Left out: the profession that works its station.",
              "type": "string"
            },
            "inputs": {
              "minItems": 1,
              "maxItems": 6,
              "description": "Items by id, or by class and lowest tier. At least one input names a class.",
              "type": "array",
              "items": {
                "anyOf": [
                  {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "item",
                      "qty"
                    ],
                    "properties": {
                      "item": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      },
                      "qty": {
                        "minimum": 1,
                        "maximum": 999,
                        "type": "integer"
                      }
                    }
                  },
                  {
                    "additionalProperties": false,
                    "description": "Any item of this class (and family, when named) at this tier or above. A base class also takes every class declared under it.",
                    "type": "object",
                    "required": [
                      "class",
                      "min_tier",
                      "qty"
                    ],
                    "properties": {
                      "class": {
                        "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                        "anyOf": [
                          {
                            "const": "plant",
                            "type": "string"
                          },
                          {
                            "const": "wood",
                            "type": "string"
                          },
                          {
                            "const": "fibre",
                            "type": "string"
                          },
                          {
                            "const": "stone",
                            "type": "string"
                          },
                          {
                            "const": "clay",
                            "type": "string"
                          },
                          {
                            "const": "mineral",
                            "type": "string"
                          },
                          {
                            "const": "metal",
                            "type": "string"
                          },
                          {
                            "const": "animal",
                            "type": "string"
                          },
                          {
                            "const": "meal",
                            "type": "string"
                          },
                          {
                            "const": "tool",
                            "type": "string"
                          },
                          {
                            "const": "garment",
                            "type": "string"
                          },
                          {
                            "const": "jewelry",
                            "type": "string"
                          },
                          {
                            "const": "relic",
                            "type": "string"
                          },
                          {
                            "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                            "maxLength": 80,
                            "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                            "type": "string"
                          }
                        ]
                      },
                      "family": {
                        "pattern": "^[a-z0-9_-]+$",
                        "maxLength": 24,
                        "description": "Only items of this family.",
                        "type": "string"
                      },
                      "min_tier": {
                        "minimum": 1,
                        "maximum": 20,
                        "description": "The lowest tier that fits.",
                        "type": "integer"
                      },
                      "min_grade": {
                        "minimum": 2,
                        "maximum": 3,
                        "description": "Only a good of this grade or finer (HUNTING section 5). Left out, any grade fits.",
                        "type": "integer"
                      },
                      "qty": {
                        "minimum": 1,
                        "maximum": 999,
                        "type": "integer"
                      }
                    }
                  }
                ]
              }
            },
            "outputs": {
              "minItems": 1,
              "maxItems": 4,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "item",
                  "qty",
                  "share_pct"
                ],
                "properties": {
                  "item": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "qty": {
                    "minimum": 1,
                    "maximum": 999,
                    "description": "Units produced per execution at perfect timing.",
                    "type": "integer"
                  },
                  "share_pct": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Part of the activity cost this output carries. Shares of one activity sum to 100.",
                    "type": "integer"
                  }
                }
              }
            },
            "miss": {
              "additionalProperties": false,
              "description": "What a missed craft leaves (G40): part of the inputs and nothing else, worth less than a success. Left out: the default share of the inputs. When the share leaves nothing, one unit of the most valuable input comes back.",
              "type": "object",
              "properties": {
                "keep_pct": {
                  "minimum": 0,
                  "maximum": 90,
                  "description": "Part of each input a miss keeps, rounded down, at most 90 percent. Left out: params.craft_miss_keep_pct.",
                  "type": "integer"
                }
              }
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "npc",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "npc"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "npc",
          "type": "string"
        },
        "npc": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "name",
            "sprite",
            "place",
            "dialogue"
          ],
          "properties": {
            "name": {
              "minLength": 1,
              "maxLength": 40,
              "type": "string"
            },
            "sprite": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
              "maxLength": 120,
              "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
              "type": "string"
            },
            "place": {
              "additionalProperties": false,
              "description": "An empty slot from the ruins map. Only one extension is active per slot.",
              "type": "object",
              "required": [
                "slot"
              ],
              "properties": {
                "slot": {
                  "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                  "maxLength": 80,
                  "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                  "type": "string"
                }
              }
            },
            "dialogue": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "stranger"
              ],
              "properties": {
                "stranger": {
                  "minItems": 1,
                  "maxItems": 6,
                  "type": "array",
                  "items": {
                    "minLength": 1,
                    "maxLength": 240,
                    "type": "string"
                  }
                },
                "acquaintance": {
                  "minItems": 1,
                  "maxItems": 6,
                  "type": "array",
                  "items": {
                    "minLength": 1,
                    "maxLength": 240,
                    "type": "string"
                  }
                },
                "friend": {
                  "minItems": 1,
                  "maxItems": 6,
                  "type": "array",
                  "items": {
                    "minLength": 1,
                    "maxLength": 240,
                    "type": "string"
                  }
                },
                "family": {
                  "minItems": 1,
                  "maxItems": 6,
                  "type": "array",
                  "items": {
                    "minLength": 1,
                    "maxLength": 240,
                    "type": "string"
                  }
                },
                "first": {
                  "minItems": 1,
                  "maxItems": 8,
                  "description": "Said the first time the player talks to them, before anything else.",
                  "type": "array",
                  "items": {
                    "minLength": 1,
                    "maxLength": 240,
                    "type": "string"
                  }
                }
              }
            },
            "remarks": {
              "maxItems": 16,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "description": "A standing line that waits for a change in the world and is said once, in place of the standing line. Of the remarks that hold, the highest priority wins, then the one whose condition names the most, then the first in the file.",
                "type": "object",
                "required": [
                  "when",
                  "say"
                ],
                "properties": {
                  "when": {
                    "additionalProperties": false,
                    "minProperties": 1,
                    "description": "Holds when every condition it names holds.",
                    "type": "object",
                    "properties": {
                      "done": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Quests the player has finished.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "active": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Quests the player is on now.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "not_done": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Quests the player has not finished yet, on them or not.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "restored": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Places the player has mended.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "built": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Camp parts that stand, themselves or under an upgrade.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "has": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Items with at least one in the bag.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "visited": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Map slots the player has stood in.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "race": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Races the player may be: holds when the player is one of them. A line may differ by race; a quest step never does.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "time": {
                        "description": "By day (05:00 to 20:00) or by night.",
                        "anyOf": [
                          {
                            "const": "day",
                            "type": "string"
                          },
                          {
                            "const": "night",
                            "type": "string"
                          }
                        ]
                      }
                    }
                  },
                  "say": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "One box, two at most (G21).",
                    "type": "array",
                    "items": {
                      "minLength": 1,
                      "maxLength": 240,
                      "type": "string"
                    }
                  },
                  "priority": {
                    "minimum": 0,
                    "maximum": 9,
                    "description": "Higher is said first when more than one remark holds. Left out, 0.",
                    "type": "integer"
                  }
                }
              }
            },
            "topics": {
              "maxItems": 8,
              "description": "Things the player can ask about: always, or once the topic's `when` holds (G21). The menu shows four at most, the ones not yet asked first.",
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "ask",
                  "lines"
                ],
                "properties": {
                  "ask": {
                    "minLength": 1,
                    "maxLength": 40,
                    "description": "The question the player can pick, for example \"About the mist\".",
                    "type": "string"
                  },
                  "lines": {
                    "minItems": 1,
                    "maxItems": 6,
                    "type": "array",
                    "items": {
                      "minLength": 1,
                      "maxLength": 240,
                      "type": "string"
                    }
                  },
                  "when": {
                    "additionalProperties": false,
                    "minProperties": 1,
                    "description": "Holds when every condition it names holds.",
                    "type": "object",
                    "properties": {
                      "done": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Quests the player has finished.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "active": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Quests the player is on now.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "not_done": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Quests the player has not finished yet, on them or not.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "restored": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Places the player has mended.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "built": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Camp parts that stand, themselves or under an upgrade.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "has": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Items with at least one in the bag.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "visited": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Map slots the player has stood in.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "race": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Races the player may be: holds when the player is one of them. A line may differ by race; a quest step never does.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "time": {
                        "description": "By day (05:00 to 20:00) or by night.",
                        "anyOf": [
                          {
                            "const": "day",
                            "type": "string"
                          },
                          {
                            "const": "night",
                            "type": "string"
                          }
                        ]
                      }
                    }
                  }
                }
              }
            },
            "shop": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
              "maxLength": 80,
              "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
              "type": "string"
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "quest",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "quest"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "quest",
          "type": "string"
        },
        "quest": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "summary",
            "steps",
            "reward"
          ],
          "properties": {
            "giver": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
              "maxLength": 80,
              "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
              "type": "string"
            },
            "pocket": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
              "maxLength": 80,
              "description": "Offered to a character who woke in this wake pocket only: the pocket's own first task. Left out: every character. Never a race: no quest is gated by race.",
              "type": "string"
            },
            "type": {
              "description": "The kind of question the quest asks: one of the fourteen in docs/design/GAME_RULES.md section 3. Two quests of one type never follow each other in a chain (G17). Optional in the schema so an old file still reads; the check refuses a quest without one since the core content declared it (GRUNT P-3, 8.10).",
              "anyOf": [
                {
                  "const": "investigation",
                  "type": "string"
                },
                {
                  "const": "discovery",
                  "type": "string"
                },
                {
                  "const": "fetch_twist",
                  "type": "string"
                },
                {
                  "const": "dilemma",
                  "type": "string"
                },
                {
                  "const": "skill",
                  "type": "string"
                },
                {
                  "const": "collection",
                  "type": "string"
                },
                {
                  "const": "tending",
                  "type": "string"
                },
                {
                  "const": "riddle",
                  "type": "string"
                },
                {
                  "const": "mastery",
                  "type": "string"
                },
                {
                  "const": "gamble",
                  "type": "string"
                },
                {
                  "const": "persuasion",
                  "type": "string"
                },
                {
                  "const": "errand",
                  "type": "string"
                },
                {
                  "const": "maze",
                  "type": "string"
                },
                {
                  "const": "stealth",
                  "type": "string"
                }
              ]
            },
            "summary": {
              "minLength": 1,
              "maxLength": 240,
              "type": "string"
            },
            "steps": {
              "minItems": 1,
              "maxItems": 6,
              "type": "array",
              "items": {
                "anyOf": [
                  {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "kind",
                      "item",
                      "qty"
                    ],
                    "properties": {
                      "kind": {
                        "const": "bring",
                        "type": "string"
                      },
                      "item": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      },
                      "qty": {
                        "minimum": 1,
                        "maximum": 99,
                        "type": "integer"
                      }
                    }
                  },
                  {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "kind",
                      "recipe"
                    ],
                    "properties": {
                      "kind": {
                        "const": "make",
                        "type": "string"
                      },
                      "recipe": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      }
                    }
                  },
                  {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "kind",
                      "restore"
                    ],
                    "properties": {
                      "kind": {
                        "const": "restore",
                        "type": "string"
                      },
                      "restore": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      }
                    }
                  },
                  {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "kind",
                      "camp"
                    ],
                    "properties": {
                      "kind": {
                        "const": "build",
                        "type": "string"
                      },
                      "camp": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      }
                    }
                  },
                  {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "kind",
                      "slot"
                    ],
                    "properties": {
                      "kind": {
                        "const": "visit",
                        "type": "string"
                      },
                      "slot": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      }
                    }
                  },
                  {
                    "additionalProperties": false,
                    "description": "Keep this fire burning without a gap from dusk through dawn. \"own\" is the fire the player kindled outside the town (G30, G48).",
                    "type": "object",
                    "required": [
                      "kind",
                      "fire"
                    ],
                    "properties": {
                      "kind": {
                        "const": "tend",
                        "type": "string"
                      },
                      "fire": {
                        "anyOf": [
                          {
                            "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                            "maxLength": 80,
                            "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                            "type": "string"
                          },
                          {
                            "const": "own",
                            "type": "string"
                          }
                        ]
                      }
                    }
                  },
                  {
                    "additionalProperties": false,
                    "description": "Bring one of an item that is found, not farmed: only digging gives it, or one timed plant (a resource with hours) that nothing else gives, makes or sells. The journal shows the clue, not a count, until the item is in the bag.",
                    "type": "object",
                    "required": [
                      "kind",
                      "item",
                      "clue"
                    ],
                    "properties": {
                      "kind": {
                        "const": "find",
                        "type": "string"
                      },
                      "item": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      },
                      "clue": {
                        "minLength": 1,
                        "maxLength": 240,
                        "description": "What the journal shows for this step: something to notice or work out. Never the answer, never a count.",
                        "type": "string"
                      }
                    }
                  },
                  {
                    "additionalProperties": false,
                    "description": "A catch and a reveal (G6): the journal shows the clue, the world holds the answer.",
                    "type": "object",
                    "required": [
                      "kind",
                      "clue",
                      "props",
                      "answer"
                    ],
                    "properties": {
                      "kind": {
                        "const": "riddle",
                        "type": "string"
                      },
                      "clue": {
                        "minLength": 1,
                        "maxLength": 240,
                        "description": "What the journal shows for this step: something to notice or work out. Never the answer, never a count.",
                        "type": "string"
                      },
                      "props": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Ids of the map props the riddle reads: the inscriptions it pays for (G8).",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "answer": {
                        "description": "What solves the riddle. The kernel completes the step on that event and on nothing else.",
                        "anyOf": [
                          {
                            "additionalProperties": false,
                            "description": "Solved by digging at this dig spot, whatever comes up.",
                            "type": "object",
                            "required": [
                              "kind",
                              "treasure"
                            ],
                            "properties": {
                              "kind": {
                                "const": "dig",
                                "type": "string"
                              },
                              "treasure": {
                                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                                "maxLength": 80,
                                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                                "type": "string"
                              }
                            }
                          },
                          {
                            "additionalProperties": false,
                            "description": "Solved by standing in this slot by day (05:00 to 20:00) or by night.",
                            "type": "object",
                            "required": [
                              "kind",
                              "slot",
                              "at"
                            ],
                            "properties": {
                              "kind": {
                                "const": "visit",
                                "type": "string"
                              },
                              "slot": {
                                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                                "maxLength": 80,
                                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                                "type": "string"
                              },
                              "at": {
                                "anyOf": [
                                  {
                                    "const": "day",
                                    "type": "string"
                                  },
                                  {
                                    "const": "night",
                                    "type": "string"
                                  }
                                ]
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                ]
              }
            },
            "requires": {
              "maxItems": 4,
              "type": "array",
              "items": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              }
            },
            "talk": {
              "additionalProperties": false,
              "description": "What the giver says, in the giver's voice. \"{name}\" in a line is the player's name.",
              "type": "object",
              "properties": {
                "offer": {
                  "minItems": 1,
                  "maxItems": 3,
                  "description": "The giver asks. Left out, the summary.",
                  "type": "array",
                  "items": {
                    "minLength": 1,
                    "maxLength": 240,
                    "type": "string"
                  }
                },
                "waiting": {
                  "minItems": 1,
                  "maxItems": 2,
                  "description": "The giver, while the player is on it. Left out, the summary.",
                  "type": "array",
                  "items": {
                    "minLength": 1,
                    "maxLength": 240,
                    "type": "string"
                  }
                },
                "done": {
                  "minItems": 1,
                  "maxItems": 3,
                  "description": "The giver, when the player hands it in.",
                  "type": "array",
                  "items": {
                    "minLength": 1,
                    "maxLength": 240,
                    "type": "string"
                  }
                },
                "done_bitten": {
                  "minItems": 1,
                  "maxItems": 3,
                  "description": "The giver, at the hand-in, when a predator bit the player while the task was open, on an outing that came home. Left out, done.",
                  "type": "array",
                  "items": {
                    "minLength": 1,
                    "maxLength": 240,
                    "type": "string"
                  }
                },
                "variants": {
                  "minItems": 1,
                  "maxItems": 4,
                  "description": "Other words for the same task while a condition holds, for example `when.race`: the first variant that holds and names that part replaces it. The steps and the reward never change.",
                  "type": "array",
                  "items": {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "when"
                    ],
                    "properties": {
                      "when": {
                        "additionalProperties": false,
                        "minProperties": 1,
                        "description": "Holds when every condition it names holds.",
                        "type": "object",
                        "properties": {
                          "done": {
                            "minItems": 1,
                            "maxItems": 4,
                            "description": "Quests the player has finished.",
                            "type": "array",
                            "items": {
                              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                              "maxLength": 80,
                              "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                              "type": "string"
                            }
                          },
                          "active": {
                            "minItems": 1,
                            "maxItems": 4,
                            "description": "Quests the player is on now.",
                            "type": "array",
                            "items": {
                              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                              "maxLength": 80,
                              "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                              "type": "string"
                            }
                          },
                          "not_done": {
                            "minItems": 1,
                            "maxItems": 4,
                            "description": "Quests the player has not finished yet, on them or not.",
                            "type": "array",
                            "items": {
                              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                              "maxLength": 80,
                              "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                              "type": "string"
                            }
                          },
                          "restored": {
                            "minItems": 1,
                            "maxItems": 4,
                            "description": "Places the player has mended.",
                            "type": "array",
                            "items": {
                              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                              "maxLength": 80,
                              "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                              "type": "string"
                            }
                          },
                          "built": {
                            "minItems": 1,
                            "maxItems": 4,
                            "description": "Camp parts that stand, themselves or under an upgrade.",
                            "type": "array",
                            "items": {
                              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                              "maxLength": 80,
                              "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                              "type": "string"
                            }
                          },
                          "has": {
                            "minItems": 1,
                            "maxItems": 4,
                            "description": "Items with at least one in the bag.",
                            "type": "array",
                            "items": {
                              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                              "maxLength": 80,
                              "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                              "type": "string"
                            }
                          },
                          "visited": {
                            "minItems": 1,
                            "maxItems": 4,
                            "description": "Map slots the player has stood in.",
                            "type": "array",
                            "items": {
                              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                              "maxLength": 80,
                              "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                              "type": "string"
                            }
                          },
                          "race": {
                            "minItems": 1,
                            "maxItems": 4,
                            "description": "Races the player may be: holds when the player is one of them. A line may differ by race; a quest step never does.",
                            "type": "array",
                            "items": {
                              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                              "maxLength": 80,
                              "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                              "type": "string"
                            }
                          },
                          "time": {
                            "description": "By day (05:00 to 20:00) or by night.",
                            "anyOf": [
                              {
                                "const": "day",
                                "type": "string"
                              },
                              {
                                "const": "night",
                                "type": "string"
                              }
                            ]
                          }
                        }
                      },
                      "offer": {
                        "minItems": 1,
                        "maxItems": 3,
                        "type": "array",
                        "items": {
                          "minLength": 1,
                          "maxLength": 240,
                          "type": "string"
                        }
                      },
                      "waiting": {
                        "minItems": 1,
                        "maxItems": 2,
                        "type": "array",
                        "items": {
                          "minLength": 1,
                          "maxLength": 240,
                          "type": "string"
                        }
                      },
                      "done": {
                        "minItems": 1,
                        "maxItems": 3,
                        "type": "array",
                        "items": {
                          "minLength": 1,
                          "maxLength": 240,
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            },
            "reward": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "rep"
              ],
              "properties": {
                "rep": {
                  "minimum": 0,
                  "maximum": 100,
                  "description": "Reputation with the giver. Never traded, so it can be generous.",
                  "type": "integer"
                },
                "gold_mg": {
                  "minimum": 1,
                  "maximum": 10000000,
                  "type": "integer"
                },
                "items": {
                  "maxItems": 4,
                  "type": "array",
                  "items": {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "item",
                      "qty"
                    ],
                    "properties": {
                      "item": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      },
                      "qty": {
                        "minimum": 1,
                        "maximum": 999,
                        "type": "integer"
                      },
                      "instead": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "What a character whose wake pocket lays the item on the ground gets in its place: something that pocket lacks.",
                        "type": "string"
                      }
                    }
                  }
                },
                "recipes": {
                  "maxItems": 4,
                  "type": "array",
                  "items": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                },
                "opens": {
                  "maxItems": 2,
                  "description": "Ids of restores the quest opens when it is done: a place reward, the way a restore opens by hand.",
                  "type": "array",
                  "items": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              }
            },
            "maze": {
              "additionalProperties": false,
              "description": "A maze quest only: where its shape is drawn and how much the entrance shows of it (DECISIONS 2026-10-11).",
              "type": "object",
              "required": [
                "tier",
                "area"
              ],
              "properties": {
                "tier": {
                  "minimum": 1,
                  "maximum": 4,
                  "description": "The rung of help at the entrance: 1 a peek from above with a faint line of the route and the landmarks, 2 the peek with the landmarks lit a moment, 3 the peek of the shape alone, 4 no peek.",
                  "type": "integer"
                },
                "area": {
                  "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                  "maxLength": 80,
                  "description": "The map area that frames the whole maze: stepping into it from outside is the entrance, and the peek fits it on the screen.",
                  "type": "string"
                },
                "landmark": {
                  "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                  "maxLength": 120,
                  "description": "The sprite of the props inside the area that mark the way; tiers 1 and 2 light them in the peek.",
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "expedition",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "expedition"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "expedition",
          "type": "string"
        },
        "expedition": {
          "additionalProperties": false,
          "description": "A place entered under a clock: its giver-less quest chain persists, and a missed window waits for another night (G36).",
          "type": "object",
          "required": [
            "place",
            "window",
            "exit",
            "chain",
            "climax",
            "reward",
            "on_miss"
          ],
          "properties": {
            "place": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
              "maxLength": 80,
              "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
              "type": "string"
            },
            "window": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "bells"
              ],
              "properties": {
                "bells": {
                  "minItems": 2,
                  "maxItems": 6,
                  "description": "Night hours in the order they ring: every 20..23 hour before every 0..4 hour.",
                  "type": "array",
                  "items": {
                    "anyOf": [
                      {
                        "minimum": 20,
                        "maximum": 23,
                        "type": "integer"
                      },
                      {
                        "minimum": 0,
                        "maximum": 4,
                        "type": "integer"
                      }
                    ]
                  }
                }
              }
            },
            "exit": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
              "maxLength": 80,
              "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
              "type": "string"
            },
            "chain": {
              "minItems": 3,
              "maxItems": 5,
              "type": "array",
              "items": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              }
            },
            "climax": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "slot"
              ],
              "properties": {
                "slot": {
                  "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                  "maxLength": 80,
                  "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                  "type": "string"
                },
                "lines": {
                  "minItems": 1,
                  "maxItems": 3,
                  "description": "The narrator's boxes at the climax, the same whether the reward opens a way or the player opened it by hand.",
                  "type": "array",
                  "items": {
                    "minLength": 1,
                    "maxLength": 240,
                    "type": "string"
                  }
                }
              }
            },
            "reward": {
              "additionalProperties": false,
              "type": "object",
              "properties": {
                "opens": {
                  "maxItems": 2,
                  "type": "array",
                  "items": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                },
                "items": {
                  "maxItems": 4,
                  "type": "array",
                  "items": {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "item",
                      "qty"
                    ],
                    "properties": {
                      "item": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      },
                      "qty": {
                        "minimum": 1,
                        "maximum": 999,
                        "type": "integer"
                      },
                      "instead": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "What a character whose wake pocket lays the item on the ground gets in its place: something that pocket lacks.",
                        "type": "string"
                      }
                    }
                  }
                }
              }
            },
            "on_miss": {
              "const": "wait",
              "type": "string"
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "restore",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "restore"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "restore",
          "type": "string"
        },
        "restore": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "place",
            "before",
            "after",
            "inputs",
            "active_ms",
            "passable"
          ],
          "properties": {
            "place": {
              "additionalProperties": false,
              "description": "An empty slot from the ruins map. Only one extension is active per slot.",
              "type": "object",
              "required": [
                "slot"
              ],
              "properties": {
                "slot": {
                  "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                  "maxLength": 80,
                  "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                  "type": "string"
                }
              }
            },
            "before": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
              "maxLength": 120,
              "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
              "type": "string"
            },
            "after": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
              "maxLength": 120,
              "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
              "type": "string"
            },
            "inputs": {
              "maxItems": 6,
              "description": "What the player brings to restore it. Empty: it does not open by hand, only as a quest's reward.opens.",
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "item",
                  "qty"
                ],
                "properties": {
                  "item": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "qty": {
                    "minimum": 1,
                    "maximum": 999,
                    "type": "integer"
                  }
                }
              }
            },
            "active_ms": {
              "minimum": 0,
              "maximum": 3600000,
              "description": "Time the player is busy restoring it.",
              "type": "integer"
            },
            "passable": {
              "description": "Whether the restored thing can be walked over (a bridge).",
              "type": "boolean"
            },
            "sign": {
              "minLength": 1,
              "maxLength": 40,
              "type": "string"
            },
            "mist": {
              "minimum": 1,
              "maximum": 64,
              "description": "Tiles around the place that the mist leaves clear once it is mended (B2, NEXT 16). Left out: the mend clears none. The kernel does not read it; the client draws it.",
              "type": "integer"
            },
            "stone": {
              "additionalProperties": false,
              "description": "The mended place is a returners' stone (NEXT 16 e): the written names above a line the player carves. Left out: the place keeps the plain \"Mended by\" sign.",
              "type": "object",
              "required": [
                "names",
                "default_name",
                "purse"
              ],
              "properties": {
                "names": {
                  "minItems": 6,
                  "maxItems": 12,
                  "description": "Names cut by earlier hands, one word each, top to bottom. Written content: the stone is the speaker of every line on it (STORY.md S2).",
                  "type": "array",
                  "items": {
                    "pattern": "^\\S{1,16}$",
                    "type": "string"
                  }
                },
                "default_name": {
                  "pattern": "^[A-Za-z0-9 _-]{1,16}$",
                  "description": "The name cut for a player who types none. The kernel's name rule: 1 to 16 letters, digits, spaces, \"_\" or \"-\".",
                  "type": "string"
                },
                "purse": {
                  "minItems": 1,
                  "maxItems": 2,
                  "description": "What the stone says after the mend, in its own voice: never who left the purse or when (STORY.md S1, S2).",
                  "type": "array",
                  "items": {
                    "minLength": 1,
                    "maxLength": 240,
                    "type": "string"
                  }
                }
              }
            },
            "done": {
              "minItems": 1,
              "maxItems": 2,
              "description": "The narrator, when the place is mended: what is there now, never what was (STORY.md S1). \"{name}\" is the player's name.",
              "type": "array",
              "items": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "camp",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "camp"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "camp",
          "type": "string"
        },
        "camp": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "slot",
            "inputs",
            "active_ms",
            "sprite"
          ],
          "properties": {
            "slot": {
              "anyOf": [
                {
                  "const": "fire",
                  "type": "string"
                },
                {
                  "const": "storage",
                  "type": "string"
                },
                {
                  "const": "workshop",
                  "type": "string"
                },
                {
                  "const": "shelf",
                  "type": "string"
                }
              ]
            },
            "inputs": {
              "minItems": 1,
              "maxItems": 6,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "item",
                  "qty"
                ],
                "properties": {
                  "item": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "qty": {
                    "minimum": 1,
                    "maximum": 999,
                    "type": "integer"
                  }
                }
              }
            },
            "active_ms": {
              "minimum": 0,
              "maximum": 3600000,
              "description": "Time the player is busy building it.",
              "type": "integer"
            },
            "sprite": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
              "maxLength": 120,
              "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
              "type": "string"
            },
            "provides": {
              "anyOf": [
                {
                  "const": "fire",
                  "type": "string"
                },
                {
                  "const": "workshop",
                  "type": "string"
                }
              ]
            },
            "tier": {
              "minimum": 1,
              "maximum": 20,
              "description": "Tier of the station this part provides, 1 when left out. A recipe needs a station of at least its own tier.",
              "type": "integer"
            },
            "bag_bonus": {
              "minimum": 1,
              "maximum": 40,
              "type": "integer"
            },
            "upgrades": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
              "maxLength": 80,
              "description": "The camp part this one is built over, in the same slot: that part stands first, and this one replaces it. It gives at least what that part gave.",
              "type": "string"
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "race",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "race"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "race",
          "type": "string"
        },
        "race": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "name",
            "about",
            "attributes",
            "home",
            "sprite"
          ],
          "properties": {
            "name": {
              "minLength": 1,
              "maxLength": 40,
              "type": "string"
            },
            "about": {
              "minLength": 1,
              "maxLength": 240,
              "type": "string",
              "description": "Who they are, in one or two present-tense sentences, shown on the title card."
            },
            "attributes": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "might",
                "senses",
                "agility",
                "luck",
                "stamina",
                "spirit"
              ],
              "properties": {
                "might": {
                  "minimum": 0,
                  "maximum": 5,
                  "description": "Bag places; heavy work; later combat. Was \"carry\".",
                  "type": "integer"
                },
                "senses": {
                  "minimum": 0,
                  "maximum": 5,
                  "type": "integer"
                },
                "agility": {
                  "minimum": 0,
                  "maximum": 5,
                  "type": "integer"
                },
                "luck": {
                  "minimum": 0,
                  "maximum": 5,
                  "type": "integer"
                },
                "stamina": {
                  "minimum": 0,
                  "maximum": 5,
                  "type": "integer"
                },
                "spirit": {
                  "minimum": 0,
                  "maximum": 5,
                  "type": "integer"
                }
              },
              "description": "The fixed profile: no free points at the title. The check holds every race to one total."
            },
            "home": {
              "minItems": 2,
              "maxItems": 2,
              "uniqueItems": true,
              "description": "The two home attributes: a world that is strong for the race lifts each by one, a weak world lowers each by one (never below 0).",
              "type": "array",
              "items": {
                "anyOf": [
                  {
                    "const": "might",
                    "type": "string"
                  },
                  {
                    "const": "senses",
                    "type": "string"
                  },
                  {
                    "const": "agility",
                    "type": "string"
                  },
                  {
                    "const": "luck",
                    "type": "string"
                  },
                  {
                    "const": "stamina",
                    "type": "string"
                  },
                  {
                    "const": "spirit",
                    "type": "string"
                  }
                ]
              }
            },
            "grades": {
              "description": "How each world takes the race. A world left out is \"normal\". The valley is neutral to every race.",
              "type": "object",
              "patternProperties": {
                "^[a-z0-9_-]+$": {
                  "anyOf": [
                    {
                      "const": "weak",
                      "type": "string"
                    },
                    {
                      "const": "normal",
                      "type": "string"
                    },
                    {
                      "const": "strong",
                      "type": "string"
                    }
                  ]
                }
              }
            },
            "outfit": {
              "maxItems": 3,
              "description": "What the race wears on the first morning: put in the bag and worn at once. Never a weapon, never the pocket's tool.",
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "item",
                  "qty"
                ],
                "properties": {
                  "item": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "qty": {
                    "minimum": 1,
                    "maximum": 999,
                    "type": "integer"
                  }
                }
              }
            },
            "body": {
              "additionalProperties": false,
              "description": "One body fact per race, equal in size (DECISIONS.md, 2026-10-23): a weakness always matched by a real advantage. It never opens or closes a place (G70).",
              "type": "object",
              "required": [
                "name",
                "about",
                "effects"
              ],
              "properties": {
                "name": {
                  "minLength": 1,
                  "maxLength": 40,
                  "type": "string"
                },
                "about": {
                  "minLength": 1,
                  "maxLength": 240,
                  "type": "string",
                  "description": "The body fact in one plain sentence: the good side and its cost."
                },
                "effects": {
                  "minItems": 1,
                  "maxItems": 2,
                  "description": "One or two effects, each named once: the good side and, when the body pays for it here, the cost.",
                  "type": "array",
                  "items": {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "effect",
                      "value"
                    ],
                    "properties": {
                      "effect": {
                        "description": "What the body changes: the mend of a rest by a fire, the fill of food, the share of the load that counts for pace, or the places of any bag.",
                        "anyOf": [
                          {
                            "const": "rest_mend_permille",
                            "type": "string"
                          },
                          {
                            "const": "food_fill_permille",
                            "type": "string"
                          },
                          {
                            "const": "pace_load_permille",
                            "type": "string"
                          },
                          {
                            "const": "bag_places",
                            "type": "string"
                          }
                        ]
                      },
                      "value": {
                        "minimum": 1,
                        "maximum": 5000,
                        "description": "Permille for the three rates, a count for bag_places.",
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            },
            "sprite": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
              "maxLength": 120,
              "description": "The figure the race is drawn with. Until the race's own art comes, an existing sheet.",
              "type": "string"
            },
            "sprite_alt": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
              "maxLength": 120,
              "description": "Another look of the same race (for example a woman instead of a man), offered by one toggle on the character screen. Same sprite sizes and directions as `sprite`.",
              "type": "string"
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "items",
          "type": "string"
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "creator",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "profile"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "creator",
          "type": "string"
        },
        "profile": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "name"
          ],
          "properties": {
            "name": {
              "minLength": 1,
              "maxLength": 40,
              "type": "string",
              "description": "How the game names you on signs and in the credits."
            },
            "role": {
              "minLength": 1,
              "maxLength": 40,
              "description": "A few words under your name, for example \"Main developer\".",
              "type": "string"
            },
            "signature": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "text",
                "style"
              ],
              "properties": {
                "text": {
                  "minLength": 1,
                  "maxLength": 24,
                  "description": "The words of your signature, shown over your signs without a click.",
                  "type": "string"
                },
                "style": {
                  "description": "\"carved\" (into stone), \"painted\" (on wood), \"ink\", \"gold\" (leaf) or \"moss\".",
                  "anyOf": [
                    {
                      "const": "carved",
                      "type": "string"
                    },
                    {
                      "const": "painted",
                      "type": "string"
                    },
                    {
                      "const": "ink",
                      "type": "string"
                    },
                    {
                      "const": "gold",
                      "type": "string"
                    },
                    {
                      "const": "moss",
                      "type": "string"
                    }
                  ]
                },
                "glyph": {
                  "description": "A small mark beside the words.",
                  "anyOf": [
                    {
                      "const": "none",
                      "type": "string"
                    },
                    {
                      "const": "leaf",
                      "type": "string"
                    },
                    {
                      "const": "star",
                      "type": "string"
                    },
                    {
                      "const": "sun",
                      "type": "string"
                    },
                    {
                      "const": "flower",
                      "type": "string"
                    },
                    {
                      "const": "key",
                      "type": "string"
                    },
                    {
                      "const": "wave",
                      "type": "string"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "reaction",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "reaction"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "reaction",
          "type": "string"
        },
        "reaction": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "on",
            "when",
            "then"
          ],
          "properties": {
            "on": {
              "description": "What it hangs on: every node of a resource, every spot of a treasure (\"*\": of every one), or every tile of a ground tag.",
              "anyOf": [
                {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "resource"
                  ],
                  "properties": {
                    "resource": {
                      "anyOf": [
                        {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        },
                        {
                          "const": "*",
                          "type": "string"
                        }
                      ]
                    }
                  }
                },
                {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "treasure"
                  ],
                  "properties": {
                    "treasure": {
                      "anyOf": [
                        {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        },
                        {
                          "const": "*",
                          "type": "string"
                        }
                      ]
                    }
                  }
                },
                {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "tag"
                  ],
                  "properties": {
                    "tag": {
                      "anyOf": [
                        {
                          "const": "grove",
                          "type": "string"
                        },
                        {
                          "const": "meadow",
                          "type": "string"
                        },
                        {
                          "const": "riverbank",
                          "type": "string"
                        },
                        {
                          "const": "ruin",
                          "type": "string"
                        },
                        {
                          "const": "town",
                          "type": "string"
                        },
                        {
                          "const": "water",
                          "type": "string"
                        },
                        {
                          "const": "path",
                          "type": "string"
                        },
                        {
                          "const": "interior",
                          "type": "string"
                        }
                      ]
                    }
                  }
                }
              ]
            },
            "when": {
              "additionalProperties": false,
              "minProperties": 1,
              "description": "Holds when every condition it names holds.",
              "type": "object",
              "properties": {
                "hours": {
                  "description": "Game hours it holds, [from, to), as a node's `hours`.",
                  "type": "array",
                  "items": [
                    {
                      "minimum": 0,
                      "maximum": 23,
                      "type": "integer"
                    },
                    {
                      "minimum": 1,
                      "maximum": 24,
                      "type": "integer"
                    }
                  ],
                  "additionalItems": false,
                  "minItems": 2,
                  "maxItems": 2
                },
                "night": {
                  "description": "true: only at night (20:00 to 05:00); false: only by day.",
                  "type": "boolean"
                },
                "ready_for_ms": {
                  "minimum": 0,
                  "maximum": 3600000,
                  "description": "The node it hangs on has stood ready, untouched, for at least this long, in game ms.",
                  "type": "integer"
                },
                "on_tag": {
                  "anyOf": [
                    {
                      "const": "grove",
                      "type": "string"
                    },
                    {
                      "const": "meadow",
                      "type": "string"
                    },
                    {
                      "const": "riverbank",
                      "type": "string"
                    },
                    {
                      "const": "ruin",
                      "type": "string"
                    },
                    {
                      "const": "town",
                      "type": "string"
                    },
                    {
                      "const": "water",
                      "type": "string"
                    },
                    {
                      "const": "path",
                      "type": "string"
                    },
                    {
                      "const": "interior",
                      "type": "string"
                    }
                  ],
                  "description": "The tile of the node or spot it hangs on carries this ground tag."
                },
                "player_within": {
                  "minimum": 1,
                  "maximum": 8,
                  "description": "The player stands within this many tiles (the larger of the two distances).",
                  "type": "integer"
                },
                "unseen": {
                  "description": "The spot it hangs on lies beyond the player's sense reach, so the find does not show yet.",
                  "const": true,
                  "type": "boolean"
                },
                "chance_pct": {
                  "minimum": 1,
                  "maximum": 100,
                  "description": "It holds on this many days in a hundred, drawn once a game day for each thing it hangs on, the same on every replay.",
                  "type": "integer"
                },
                "weather": {
                  "description": "The day's weather in the region of the thing it hangs on is this one.",
                  "anyOf": [
                    {
                      "const": "clear",
                      "type": "string"
                    },
                    {
                      "const": "rain",
                      "type": "string"
                    },
                    {
                      "const": "fog",
                      "type": "string"
                    }
                  ]
                }
              }
            },
            "then": {
              "description": "Its one effect: a node that appears, a slower or quicker respawn, or a sign for the eyes and ears.",
              "anyOf": [
                {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "appear"
                  ],
                  "properties": {
                    "appear": {
                      "additionalProperties": false,
                      "description": "A node that exists only while the condition holds, with the id \"<reaction id>@<node id>\". It is a source of its own in the value table, priced as any node (R6, R7).",
                      "type": "object",
                      "required": [
                        "output",
                        "yield",
                        "active_ms",
                        "respawn_ms",
                        "level",
                        "sprite",
                        "at"
                      ],
                      "properties": {
                        "output": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        },
                        "yield": {
                          "minimum": 1,
                          "maximum": 20,
                          "description": "Units gathered per execution at perfect timing.",
                          "type": "integer"
                        },
                        "active_ms": {
                          "minimum": 0,
                          "maximum": 3600000,
                          "description": "Time the player is busy gathering it once.",
                          "type": "integer"
                        },
                        "respawn_ms": {
                          "minimum": 0,
                          "maximum": 3600000,
                          "description": "Time before it can show again once taken, while its condition still holds. Waiting is never paid.",
                          "type": "integer"
                        },
                        "level": {
                          "minimum": 1,
                          "maximum": 20,
                          "description": "Foraging level required.",
                          "type": "integer"
                        },
                        "tool": {
                          "anyOf": [
                            {
                              "const": "basket",
                              "type": "string"
                            },
                            {
                              "const": "trowel",
                              "type": "string"
                            },
                            {
                              "const": "axe",
                              "type": "string"
                            },
                            {
                              "const": "staff",
                              "type": "string"
                            },
                            {
                              "const": "hammer",
                              "type": "string"
                            },
                            {
                              "const": "pick",
                              "type": "string"
                            },
                            {
                              "const": "rod",
                              "type": "string"
                            }
                          ]
                        },
                        "game": {
                          "description": "The minigame played when gathering it, as a resource's `game`.",
                          "anyOf": [
                            {
                              "const": "timing",
                              "type": "string"
                            },
                            {
                              "const": "strike",
                              "type": "string"
                            },
                            {
                              "const": "pick",
                              "type": "string"
                            },
                            {
                              "const": "snap",
                              "type": "string"
                            }
                          ]
                        },
                        "sprite": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                          "maxLength": 120,
                          "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                          "type": "string"
                        },
                        "at": {
                          "description": "The tile it shows on: the tile of the thing it hangs on, or the one next to it on that side.",
                          "anyOf": [
                            {
                              "const": "here",
                              "type": "string"
                            },
                            {
                              "const": "above",
                              "type": "string"
                            },
                            {
                              "const": "below",
                              "type": "string"
                            },
                            {
                              "const": "left",
                              "type": "string"
                            },
                            {
                              "const": "right",
                              "type": "string"
                            }
                          ]
                        }
                      }
                    }
                  }
                },
                {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "wait"
                  ],
                  "properties": {
                    "wait": {
                      "minimum": 500,
                      "maximum": 2000,
                      "description": "Scales the respawn of the node it hangs on, in permille. Waiting is never paid, so no value moves.",
                      "type": "integer"
                    }
                  }
                },
                {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "cue"
                  ],
                  "properties": {
                    "cue": {
                      "description": "A sign the client draws at a tile, with a shape and a sound and never a number or a word: birds lift from cover, fish rise on the water.",
                      "anyOf": [
                        {
                          "const": "birds_lift",
                          "type": "string"
                        },
                        {
                          "const": "fish_rise",
                          "type": "string"
                        }
                      ]
                    },
                    "spread": {
                      "minimum": 0,
                      "maximum": 2,
                      "description": "The cue shows on a tile drawn within this many tiles of the thing, so it marks an area and not the tile. Left out: 0.",
                      "type": "integer"
                    }
                  }
                }
              ]
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "shop",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "shop"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "shop",
          "type": "string"
        },
        "shop": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "sells",
            "buys"
          ],
          "properties": {
            "sells": {
              "maxItems": 40,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "item",
                  "price_mg"
                ],
                "properties": {
                  "item": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "price_mg": {
                    "minimum": 1,
                    "maximum": 100000000,
                    "description": "Price in milli-gold (1000 = 1 gold).",
                    "type": "integer"
                  }
                }
              }
            },
            "buys": {
              "description": "Whether the shop buys anything that has a value.",
              "type": "boolean"
            },
            "interior": {
              "description": "The room its keeper trades in (DECISIONS.md, 2026-10-11, the shop interior). With it, the keeper stands inside: their place is the door, the player walks in, and the trade opens by talking across the counter. `food`: worn planks, a runner to the counter, a lit stove in a brick chimney. Left out, the keeper trades in the open.",
              "const": "food",
              "type": "string"
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "profession",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "profession"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "profession",
          "type": "string"
        },
        "profession": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "name",
            "verb",
            "xp_levels",
            "techniques"
          ],
          "properties": {
            "name": {
              "minLength": 1,
              "maxLength": 40,
              "type": "string"
            },
            "verb": {
              "anyOf": [
                {
                  "const": "gather",
                  "type": "string"
                },
                {
                  "const": "fell",
                  "type": "string"
                },
                {
                  "const": "dig",
                  "type": "string"
                },
                {
                  "const": "fish",
                  "type": "string"
                },
                {
                  "const": "craft",
                  "type": "string"
                }
              ]
            },
            "stations": {
              "minItems": 1,
              "maxItems": 4,
              "uniqueItems": true,
              "description": "A craft profession only: the stations whose recipes it gates when a recipe names no profession. One profession per station.",
              "type": "array",
              "items": {
                "anyOf": [
                  {
                    "const": "fire",
                    "type": "string"
                  },
                  {
                    "const": "workshop",
                    "type": "string"
                  }
                ]
              }
            },
            "xp_levels": {
              "minItems": 1,
              "maxItems": 20,
              "description": "Total experience (active seconds) needed for level 2, 3, ...",
              "type": "array",
              "items": {
                "minimum": 1,
                "maximum": 10000000,
                "type": "integer"
              }
            },
            "techniques": {
              "maxItems": 10,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "level",
                  "name",
                  "effect",
                  "value",
                  "text"
                ],
                "properties": {
                  "level": {
                    "minimum": 2,
                    "maximum": 20,
                    "type": "integer"
                  },
                  "name": {
                    "minLength": 1,
                    "maxLength": 40,
                    "type": "string"
                  },
                  "effect": {
                    "description": "What a technique does. A closed list: the kernel knows how to apply each one. A craft effect of a technique (craft_speed_pct, craft_chance_pct) helps the recipes of its own profession.",
                    "anyOf": [
                      {
                        "const": "see_ripe",
                        "type": "string"
                      },
                      {
                        "const": "timing_window_pct",
                        "type": "string"
                      },
                      {
                        "const": "second_hint",
                        "type": "string"
                      },
                      {
                        "const": "rare_find_pct",
                        "type": "string"
                      },
                      {
                        "const": "craft_speed_pct",
                        "type": "string"
                      },
                      {
                        "const": "meal_fill_pct",
                        "type": "string"
                      },
                      {
                        "const": "gather_speed_pct",
                        "type": "string"
                      },
                      {
                        "const": "dig_speed_pct",
                        "type": "string"
                      },
                      {
                        "const": "craft_chance_pct",
                        "type": "string"
                      }
                    ]
                  },
                  "value": {
                    "minimum": 0,
                    "maximum": 100,
                    "type": "integer"
                  },
                  "text": {
                    "minLength": 1,
                    "maxLength": 240,
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "params",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "params"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "params",
          "type": "string"
        },
        "params": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "k_permille",
            "k_craft_permille",
            "r_mg_per_value_ms",
            "t_min_ms",
            "max_action_ms",
            "effort_permille",
            "power_cap_permille",
            "rarity_budget_permille",
            "open_tiers",
            "tier_min_level",
            "template_max_recipes",
            "craft_chance_bp",
            "craft_level_step_bp",
            "craft_luck_max_permille",
            "craft_miss_keep_pct",
            "wear_effect_max_pct",
            "wear_set_bonus_pct",
            "rarity_bonus_ms",
            "drift_budget_permille",
            "hp_cap",
            "day_length_ms",
            "walk_step_ms",
            "hp_low_pct",
            "fire",
            "hazard",
            "bag_base",
            "place_stack",
            "load",
            "gait",
            "hunt",
            "wild",
            "starting_gold_mg",
            "per_point"
          ],
          "properties": {
            "k_permille": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              }
            },
            "light": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "keys"
              ],
              "properties": {
                "keys": {
                  "minItems": 2,
                  "maxItems": 12,
                  "description": "The hours the valley is washed in a colour (ART_SYSTEM section 2, LIGHTING.md section 5): one shade that the scene multiplies in, the dusk and the night alike. Its strength steps evenly between keys, in whole percent; its colour is the next key's, so every minute is a palette colour. The owner's pick of 30.10: 16:30 #eda83a 12%, 17:30 28%, 19:00 #e2533f 40%, the night #172a52 at STYLE 5's 92%.",
                  "type": "array",
                  "items": {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "at",
                      "tint",
                      "pct"
                    ],
                    "properties": {
                      "at": {
                        "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
                        "description": "The hour and minute the wash is at its own strength, for example \"16:30\".",
                        "type": "string"
                      },
                      "tint": {
                        "pattern": "^#[0-9a-f]{6}$",
                        "description": "The colour the scene is multiplied by at that hour.",
                        "type": "string"
                      },
                      "pct": {
                        "minimum": 0,
                        "maximum": 100,
                        "description": "How strong the wash is at that hour, in whole percent. The hours between two keys step evenly.",
                        "type": "integer"
                      }
                    }
                  }
                },
                "read": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "floor",
                    "fog"
                  ],
                  "properties": {
                    "floor": {
                      "minimum": 0,
                      "maximum": 100,
                      "description": "The night kept readable (the owner, 30.10): inside a light's pool and in the ring of three tiles around the player, the meadow's middle green keeps at least this luma, in whole percent.",
                      "type": "integer"
                    },
                    "fog": {
                      "minimum": 0,
                      "maximum": 100,
                      "description": "The most a fog bank may cover at full night, in whole percent, so fog cannot stack on the night's shade past the floor.",
                      "type": "integer"
                    }
                  }
                },
                "sun": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "alpha",
                    "cap",
                    "keys"
                  ],
                  "properties": {
                    "alpha": {
                      "minimum": 0,
                      "maximum": 100,
                      "description": "How dark the cast shadow is, in whole percent (STYLE section 4: 30).",
                      "type": "integer"
                    },
                    "cap": {
                      "minimum": 0,
                      "maximum": 200,
                      "description": "The longest a shadow may be, in percent of the caster's height (STYLE section 4: 110).",
                      "type": "integer"
                    },
                    "keys": {
                      "minItems": 2,
                      "maxItems": 12,
                      "description": "The sun's shear by hour (STYLE section 4): the shadow leans right and toward the viewer by these shares of the caster's height, so a negative `down` is the sun in the south and the shadow falling away, up-screen. The hours between two keys step evenly; outside the first and the last the sun is down.",
                      "type": "array",
                      "items": {
                        "additionalProperties": false,
                        "type": "object",
                        "required": [
                          "at",
                          "right",
                          "down"
                        ],
                        "properties": {
                          "at": {
                            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
                            "description": "The hour and minute the sun is at its own shear, for example \"16:30\".",
                            "type": "string"
                          },
                          "right": {
                            "minimum": -200,
                            "maximum": 200,
                            "description": "How far the shadow leans right, in percent of the caster's height; negative in the morning, positive in the evening.",
                            "type": "integer"
                          },
                          "down": {
                            "minimum": -100,
                            "maximum": 100,
                            "description": "How far the shadow leans toward the viewer, in percent of the caster's height; negative when the sun stands south (STYLE section 4: the shadow falls up-screen, away from the viewer, behind the caster).",
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            },
            "k_craft_permille": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              },
              "description": "What the trader pays for a made good, an item whose value comes from a recipe, per mille of r * V; k_permille holds for the rest. At least k_permille and always below 1000, so the trader never pays what the shop charges (DECISIONS.md, 2026-10-09: the crafting economy)."
            },
            "r_mg_per_value_ms": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              }
            },
            "t_min_ms": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              }
            },
            "max_action_ms": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              }
            },
            "effort_permille": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "easy",
                "medium",
                "craft",
                "hard"
              ],
              "properties": {
                "easy": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  }
                },
                "medium": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  }
                },
                "craft": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  }
                },
                "hard": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  }
                }
              }
            },
            "power_cap_permille": {
              "minItems": 1,
              "maxItems": 40,
              "description": "P(level): the clamp on any rate multiplier, by character level (1-based).",
              "type": "array",
              "items": {
                "minimum": 1000,
                "maximum": 10000,
                "type": "integer"
              }
            },
            "rarity_budget_permille": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              }
            },
            "open_tiers": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              },
              "description": "The highest item tier the world has opened. A stage or a new town opens the next one."
            },
            "tier_min_level": {
              "minItems": 1,
              "maxItems": 20,
              "description": "The lowest level of each tier, from tier 1. A recipe of tier t needs a level from entry t up to one below entry t + 1 (up to 20 for the last), and a node that gives tier 3 or above needs at least entry t, or a slow respawn. Starts at 1 and rises with every tier; one entry for every open tier at least.",
              "type": "array",
              "items": {
                "minimum": 1,
                "maximum": 20,
                "type": "integer"
              }
            },
            "template_max_recipes": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              },
              "description": "The most recipes one recipe template may make."
            },
            "craft_chance_bp": {
              "minItems": 1,
              "maxItems": 20,
              "description": "The chance a craft succeeds at exactly its level, in basis points, by the recipe's tier from tier 1; the last entry holds for the tiers above it. Never rises with the tier; 10000 is a sure craft.",
              "type": "array",
              "items": {
                "minimum": 1000,
                "maximum": 10000,
                "type": "integer"
              }
            },
            "craft_level_step_bp": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              },
              "description": "What each level of the recipe's profession above the recipe's level adds to the craft chance, in basis points."
            },
            "craft_luck_max_permille": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              },
              "description": "The largest part of the gap to a sure craft that luck and luck items close, in permille: skill decides, luck tilts (G40)."
            },
            "craft_miss_keep_pct": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              },
              "description": "Part of each input a missed craft keeps when the recipe names no share, rounded down."
            },
            "wear_effect_max_pct": {
              "minItems": 1,
              "maxItems": 20,
              "description": "The largest value of one effect on a worn item, by the item's tier from tier 1; the last entry holds for the tiers above it. Never falls with the tier.",
              "type": "array",
              "items": {
                "minimum": 0,
                "maximum": 100,
                "type": "integer"
              }
            },
            "wear_set_bonus_pct": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              },
              "description": "How much a full set adds to the effects of its pieces, in percent of their sum, rounded down."
            },
            "rarity_bonus_ms": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "common",
                "uncommon",
                "rare",
                "legendary"
              ],
              "properties": {
                "common": {
                  "type": "integer"
                },
                "uncommon": {
                  "type": "integer"
                },
                "rare": {
                  "type": "integer"
                },
                "legendary": {
                  "type": "integer"
                }
              }
            },
            "drift_budget_permille": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              }
            },
            "hp_cap": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              },
              "description": "The health bar's base top in whole points; each point of stamina adds per_point.stamina. Nothing drains it by the clock (G28)."
            },
            "day_length_ms": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              }
            },
            "walk_step_ms": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              },
              "description": "Milliseconds the player takes to walk one tile. The client walks at this pace; the kernel checks a position report against it (DECISIONS.md, 2026-10-10: health and hazards)."
            },
            "move_reach_max": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              },
              "description": "The maximum number of tiles a movement report may cover after the walk-time allowance (G56). Defaults to 32."
            },
            "hp_low_pct": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              },
              "description": "Work takes half as long again while health is at or below this share of its cap, in percent. 0 slows it only when health is empty (G32)."
            },
            "fire": {
              "additionalProperties": false,
              "description": "A tended fire uses the game tick, which pauses while the player is away.",
              "type": "object",
              "required": [
                "cap",
                "log_ms",
                "warn_min",
                "safe_tiles",
                "burn_pct_per_d"
              ],
              "properties": {
                "cap": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Most logs of burn time a fire may hold ahead of the game tick."
                },
                "log_ms": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Game milliseconds one wood item adds to a fire."
                },
                "warn_min": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Game minutes before a burning fire goes out when its warning is shown."
                },
                "safe_tiles": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "How far from the player's own fire, in tiles, the ground is safe while it burns: 1 is its tile and the ring around it (G30)."
                },
                "burn_pct_per_d": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "How much faster a log of the player's own fire burns for each step of the area's danger d, in percent (G30, G48)."
                }
              }
            },
            "hazard": {
              "additionalProperties": false,
              "description": "How hard each kind of hazard hits (G30, G34). A hazard is a rule, not a roll.",
              "type": "object",
              "required": [
                "predator",
                "pit",
                "trap",
                "deep_water"
              ],
              "properties": {
                "predator": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "reach",
                    "pace_ms",
                    "eyes",
                    "catch_ms",
                    "bite_ms",
                    "bite_pct",
                    "refill_bp"
                  ],
                  "properties": {
                    "reach": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "value",
                        "min",
                        "max",
                        "step"
                      ],
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "min": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        },
                        "step": {
                          "minimum": 0,
                          "description": "Largest change one decision may make.",
                          "type": "integer"
                        }
                      },
                      "description": "Tiles from its footprint within which a predator notices the player, unless the prop says otherwise."
                    },
                    "pace_ms": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "value",
                        "min",
                        "max",
                        "step"
                      ],
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "min": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        },
                        "step": {
                          "minimum": 0,
                          "description": "Largest change one decision may make.",
                          "type": "integer"
                        }
                      },
                      "description": "Game milliseconds a predator on a round takes to walk one tile."
                    },
                    "eyes": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "value",
                        "min",
                        "max",
                        "step"
                      ],
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "min": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        },
                        "step": {
                          "minimum": 0,
                          "description": "Largest change one decision may make.",
                          "type": "integer"
                        }
                      },
                      "description": "Tiles ahead watched by a predator on a round."
                    },
                    "catch_ms": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "value",
                        "min",
                        "max",
                        "step"
                      ],
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "min": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        },
                        "step": {
                          "minimum": 0,
                          "description": "Largest change one decision may make.",
                          "type": "integer"
                        }
                      },
                      "description": "From the start of a chase to the first bite."
                    },
                    "bite_ms": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "value",
                        "min",
                        "max",
                        "step"
                      ],
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "min": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        },
                        "step": {
                          "minimum": 0,
                          "description": "Largest change one decision may make.",
                          "type": "integer"
                        }
                      },
                      "description": "Between one bite and the next."
                    },
                    "bite_pct": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "value",
                        "min",
                        "max",
                        "step"
                      ],
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "min": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        },
                        "step": {
                          "minimum": 0,
                          "description": "Largest change one decision may make.",
                          "type": "integer"
                        }
                      },
                      "description": "What one bite takes, in percent of the health cap."
                    },
                    "refill_bp": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "value",
                        "min",
                        "max",
                        "step"
                      ],
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "min": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        },
                        "step": {
                          "minimum": 0,
                          "description": "Largest change one decision may make.",
                          "type": "integer"
                        }
                      },
                      "description": "How high a hunt outrun to a safe place lifts health, in basis points of the health cap: it gives back what its bites took, up to this share. It never lowers health."
                    }
                  }
                },
                "pit": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "harm_pct"
                  ],
                  "properties": {
                    "harm_pct": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "value",
                        "min",
                        "max",
                        "step"
                      ],
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "min": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        },
                        "step": {
                          "minimum": 0,
                          "description": "Largest change one decision may make.",
                          "type": "integer"
                        }
                      },
                      "description": "What stepping into a pit takes, in percent of the health cap."
                    }
                  }
                },
                "trap": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "harm_pct"
                  ],
                  "properties": {
                    "harm_pct": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "value",
                        "min",
                        "max",
                        "step"
                      ],
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "min": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        },
                        "step": {
                          "minimum": 0,
                          "description": "Largest change one decision may make.",
                          "type": "integer"
                        }
                      },
                      "description": "What stepping on a trap takes, in percent of the health cap."
                    }
                  }
                },
                "deep_water": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "harm_pct"
                  ],
                  "properties": {
                    "harm_pct": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "value",
                        "min",
                        "max",
                        "step"
                      ],
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "min": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        },
                        "step": {
                          "minimum": 0,
                          "description": "Largest change one decision may make.",
                          "type": "integer"
                        }
                      },
                      "description": "What stepping into deep water takes, in percent of the health cap."
                    }
                  }
                }
              }
            },
            "bag_base": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              },
              "description": "Places with nothing on the back: hands and belt (DECISIONS.md, 2026-10-23). A bag on the back gives its own `wear.places` instead."
            },
            "place_stack": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              },
              "description": "Things of one kind that share one place: a place holds a stack of up to this many (DECISIONS.md, 2026-10-23). Weight still counts every unit."
            },
            "load": {
              "additionalProperties": false,
              "description": "Weight and pace (DECISIONS.md, 2026-10-23): every item weighs, and the load against a limit from might sets one of three walking paces.",
              "type": "object",
              "required": [
                "base",
                "light_permille",
                "max_permille",
                "light_step_ms",
                "heavy_step_ms",
                "heavy_breath_permille"
              ],
              "properties": {
                "base": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The weight limit before might: the limit is this plus `per_point.might` for each point, plus a frame bag's `load_bonus`."
                },
                "light_permille": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Up to this share of the limit the pace is light; up to the whole limit it is steady; above it, heavy."
                },
                "max_permille": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Beyond this share of the limit nothing more is picked up."
                },
                "light_step_ms": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Milliseconds a tile at the light pace. The steady pace is `walk_step_ms`."
                },
                "heavy_step_ms": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Milliseconds a tile at the heavy pace."
                },
                "heavy_breath_permille": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "How fast breath (stamina) refills in a fight while heavy, against 1000."
                }
              }
            },
            "gait": {
              "additionalProperties": false,
              "description": "The three gaits (docs/design/ANIMATION.md, section 2): walk is the pace the load allows, run is faster and sneak slower, and each is heard at its own radius. The noise table is shared with the hunting rules; the radii only grow from still to run.",
              "type": "object",
              "required": [
                "run_div",
                "sneak_mul",
                "noise_still",
                "noise_sneak",
                "noise_walk",
                "noise_run"
              ],
              "properties": {
                "run_div": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "A run takes the pace the load allows divided by this: 2 is half the time a tile. Every pace divides evenly."
                },
                "sneak_mul": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "A sneak takes the pace the load allows times this: 2 is twice the time a tile."
                },
                "noise_still": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Tiles within which a creature that listens turns its head at a player standing still."
                },
                "noise_sneak": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The same, at a sneak."
                },
                "noise_walk": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The same, at a walk."
                },
                "noise_run": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The same, at a run."
                }
              }
            },
            "hunt": {
              "additionalProperties": false,
              "description": "Dressing a taken animal (docs/design/HUNTING.md sections 5-6): the time a step takes, how long a carcass keeps, and how often an animal is finer than plain. Nothing here prices a hunt: the hunt verb takes the effort weight of its tier like every verb (R6).",
              "type": "object",
              "required": [
                "dress_step_ms",
                "meat_spoil_d",
                "hide_spoil_d",
                "grade2_bp",
                "grade3_bp"
              ],
              "properties": {
                "dress_step_ms": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Game milliseconds one dressing step takes: a small animal takes 6 steps, a medium one 12, a large one 24 (HUNTING section 6), six steps to a part and one action a part, so six steps stay inside max_action_ms (the check)."
                },
                "meat_spoil_d": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Game days a carcass keeps its meat (its goods of kind \"food\"): dressed later, the meat is gone and the rest remains."
                },
                "hide_spoil_d": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Game days a carcass keeps anything: dressed later, nothing is left of it."
                },
                "grade2_bp": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Basis points of animals that appear fine (grade 2)."
                },
                "grade3_bp": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Basis points of animals that appear true (grade 3). With grade2_bp at most 10000; the rest are plain."
                }
              }
            },
            "wild": {
              "additionalProperties": false,
              "description": "The wild (docs/design/HUNTING.md sections 1-4 and 8; CREATURE_BEHAVIOUR sections 1-2 and 5.1; HUNT_FAMILIES section 3.1): the clock an animal thinks on, how far round a player animals are stepped, how long a flight, a den stay and a print last, where a den's bolt hole may lie, and the distances kept from a fire and the town. A kind's own character is its `behaviour`; these are the numbers every kind shares.",
              "type": "object",
              "required": [
                "tick_ms",
                "think_ticks",
                "wake_tiles",
                "awake_max",
                "catchup_max_steps",
                "flee_avoid_steps",
                "refill_clear_tiles",
                "spread_tiles",
                "print_ring",
                "print_fade_ms",
                "den_stay_steps",
                "bolt_min_tiles",
                "bolt_max_tiles",
                "throw_ms",
                "strike_noise",
                "fire_keep_day",
                "fire_keep_night",
                "town_keep_day",
                "town_keep_night"
              ],
              "properties": {
                "tick_ms": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Game milliseconds of one world tick (COMBAT section 4.9). Fixed: every duration below is whole ticks or whole think steps."
                },
                "think_ticks": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Ticks between two think steps of one animal, staggered by its id (HUNTING section 1): 8 ticks of 100 ms, a think step of 800 ms."
                },
                "wake_tiles": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "An animal is stepped only while a player is within this many tiles; beyond it its group is kept as an anchor and a count (HUNTING section 1)."
                },
                "awake_max": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The most animals stepped round one player at once (BESTIARY section 1)."
                },
                "catchup_max_steps": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "A gap longer than this many think steps settles each animal to its rest pose at once instead of stepping it, so one action never pays for an hour of the wild."
                },
                "flee_avoid_steps": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Think steps a fled animal keeps away from the tile it fled (HUNTING section 2, FLEE); a flushed burrower comes back to its den after them (HUNT_FAMILIES section 3.1)."
                },
                "refill_clear_tiles": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "A lost member comes back to its anchor only with no player within this many tiles (HUNTING section 8)."
                },
                "spread_tiles": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "ALERT spreads to the group members within this many tiles, and a member that sees one of its group in FLEE this close flees too (HUNTING section 2; CREATURE_BEHAVIOUR section 5.1.3)."
                },
                "print_ring": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Tiles of fresh prints a fleeing animal leaves behind it on ground that holds prints, its last tiles, oldest first out (HUNT_FAMILIES section 3.1, the line home)."
                },
                "print_fade_ms": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Game milliseconds a fresh print stays readable (one game hour is day_length_ms / 24)."
                },
                "den_stay_steps": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Think steps a burrower stays in its den after a flight once no player is near the mouth, then WANDER (CREATURE_BEHAVIOUR section 1, flight burrow; HUNT_FAMILIES section 3.1, inside while watched)."
                },
                "bolt_min_tiles": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The nearest a den's bolt hole lies to its mouth, by the squared distance (HUNT_FAMILIES section 3.1: 64 <= d2 <= 100). Far enough that a flusher beside the mouth stands beyond a burrower's sight of the bolt hole (the check)."
                },
                "bolt_max_tiles": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The farthest a den's bolt hole lies from its mouth, by the squared distance."
                },
                "throw_ms": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Game milliseconds a thrown stone takes to leave the hand (HUNTING section 5, T0)."
                },
                "strike_noise": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Tiles within which a strike is heard (HUNTING section 4; COMBAT section 4.9)."
                },
                "fire_keep_day": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Tiles a prey kind keeps from a burning fire on the ground by day (CREATURE_BEHAVIOUR section 2)."
                },
                "fire_keep_night": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The same by night: a lit circle nothing grazes in. At least the fire's safe ring and one tile more (the check)."
                },
                "town_keep_day": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Tiles a wild kind keeps from the town's cobbles by day (CREATURE_BEHAVIOUR section 2)."
                },
                "town_keep_night": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The same at night, when the lamps push the wall out; never under the day's (the check)."
                }
              }
            },
            "starting_gold_mg": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "value",
                "min",
                "max",
                "step"
              ],
              "properties": {
                "value": {
                  "type": "integer"
                },
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                },
                "step": {
                  "minimum": 0,
                  "description": "Largest change one decision may make.",
                  "type": "integer"
                }
              }
            },
            "per_point": {
              "additionalProperties": false,
              "description": "What one point in an attribute adds: weight limit (might), tiles of dig-hint distance (senses), percent wider timing windows in every minigame (agility), permille on rare and legendary finds inside the power cap and on keeping one unit after a missed gather (luck), and fullness (stamina). Spirit is defined and read by no rule yet: its first readers come with the night layer and the tree.",
              "type": "object",
              "required": [
                "might",
                "senses",
                "agility_pct",
                "luck_permille",
                "stamina",
                "spirit"
              ],
              "properties": {
                "might": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  }
                },
                "senses": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  }
                },
                "agility_pct": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  }
                },
                "luck_permille": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  }
                },
                "stamina": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  }
                },
                "spirit": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  }
                }
              }
            },
            "growth": {
              "additionalProperties": false,
              "description": "The skill tree: what a node costs and when the build's slots open. Left out: the tree is closed.",
              "type": "object",
              "required": [
                "cost",
                "nodes_max",
                "slot_levels"
              ],
              "properties": {
                "cost": {
                  "additionalProperties": false,
                  "description": "Skill points a node costs, by its size (GROWTH section 3.6): the value is the price, and a node that names its own `cost` stays between min and max.",
                  "type": "object",
                  "required": [
                    "small",
                    "major",
                    "corner",
                    "keystone"
                  ],
                  "properties": {
                    "small": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "value",
                        "min",
                        "max",
                        "step"
                      ],
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "min": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        },
                        "step": {
                          "minimum": 0,
                          "description": "Largest change one decision may make.",
                          "type": "integer"
                        }
                      }
                    },
                    "major": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "value",
                        "min",
                        "max",
                        "step"
                      ],
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "min": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        },
                        "step": {
                          "minimum": 0,
                          "description": "Largest change one decision may make.",
                          "type": "integer"
                        }
                      }
                    },
                    "corner": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "value",
                        "min",
                        "max",
                        "step"
                      ],
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "min": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        },
                        "step": {
                          "minimum": 0,
                          "description": "Largest change one decision may make.",
                          "type": "integer"
                        }
                      }
                    },
                    "keystone": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "value",
                        "min",
                        "max",
                        "step"
                      ],
                      "properties": {
                        "value": {
                          "type": "integer"
                        },
                        "min": {
                          "type": "integer"
                        },
                        "max": {
                          "type": "integer"
                        },
                        "step": {
                          "minimum": 0,
                          "description": "Largest change one decision may make.",
                          "type": "integer"
                        }
                      }
                    }
                  }
                },
                "nodes_max": {
                  "minimum": 1,
                  "maximum": 400,
                  "description": "At most this many nodes a player of one race sees in one school: its paths and that race's corner.",
                  "type": "integer"
                },
                "slot_levels": {
                  "minItems": 1,
                  "maxItems": 8,
                  "description": "The character level each active slot of the build opens at, from the first slot, never falling (GROWTH section 3.3: two at level 1, then one more at 5, 10 and 15).",
                  "type": "array",
                  "items": {
                    "minimum": 1,
                    "maximum": 40,
                    "type": "integer"
                  }
                }
              }
            },
            "difficulty": {
              "additionalProperties": false,
              "description": "The difficulty reckoning (pnpm map:difficulty): it recommends, never places.",
              "type": "object",
              "required": [
                "ratio_permille",
                "hazard_bp",
                "grind_passes_max",
                "quest_share_pct",
                "quest_minutes_max",
                "quests_max"
              ],
              "properties": {
                "ratio_permille": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Each step of the scale multiplies effort and the health a hazard takes by this, in permille (9.10: 1500). The growth curve of a school uses the same ratio (PROGRESSION 3.2)."
                },
                "hazard_bp": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "How often a hazard shows grows by this per step of d: the chance in a game minute after dark or in the mist is hazard_bp * d basis points (SETTLEMENT 3.5). A spawn model; today hazards are authored."
                },
                "grind_passes_max": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The reckoning: how many times a player is assumed to work each nearer node on the way (each pass waits for the node to grow back)."
                },
                "quest_share_pct": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The reckoning: the share of the next level's active time a place's quests should pay, in percent; the rest is grinding."
                },
                "quest_minutes_max": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The reckoning: the longest active time one recommended quest asks, in real minutes."
                },
                "quests_max": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The reckoning: the most quests it recommends at one place."
                }
              }
            },
            "balance": {
              "additionalProperties": false,
              "description": "The balance model (PROGRESSION 4.7): two red lines, rising ceilings, walls proven both ways. Read by tools only, until the kernel reads it.",
              "type": "object",
              "required": [
                "t1_ms",
                "school_steps",
                "band_size",
                "catalogue_hours_min",
                "mentor_bonus_pct",
                "group_share_max_permille",
                "loop_profit_max_mg",
                "ceilings",
                "raise_step",
                "raisers_max",
                "wall_margin_bp",
                "wall_cheese_gap",
                "top_builds_n",
                "top_over_median_max_permille",
                "proof_enum_max",
                "rarity_effects_max",
                "rarity_band_permille",
                "trait_strength_by_tier",
                "master_step",
                "dish_place_step",
                "dish_slots",
                "grade_walk_permille",
                "grade_drain_pct"
              ],
              "properties": {
                "t1_ms": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "A school's first step, in active milliseconds; step i takes t1 * ratio^(i-1) (PROGRESSION 3.2). The bounds are the owner's range, 25 to 30 seconds."
                },
                "school_steps": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Steps from level 1 to mastery (PROGRESSION 3.2: twenty, so mastery is level 21)."
                },
                "band_size": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Levels in one band (PROGRESSION 3.3)."
                },
                "catalogue_hours_min": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The fewest active hours that master every school of the catalogue (PROGRESSION 3.2: 800)."
                },
                "mentor_bonus_pct": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The one experience source beside the work itself: the mentor's bonus, in percent (PROGRESSION 3.7, red line B)."
                },
                "group_share_max_permille": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The most a newcomer takes from the experience the other members already earned, in permille of the newcomer's own: a split, never new experience (PROGRESSION 3.7, Q7)."
                },
                "loop_profit_max_mg": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The most gold any buy, craft and sell cycle at a vendor may make, in milligold (red line A: 0)."
                },
                "ceilings": {
                  "additionalProperties": false,
                  "description": "Each category's ceiling by the wearer's band (PROGRESSION 4.7): every source adds and the sum stops here. Never falls with the band.",
                  "type": "object",
                  "required": [
                    "work",
                    "move",
                    "carry",
                    "health",
                    "sustain",
                    "find",
                    "sense",
                    "place"
                  ],
                  "properties": {
                    "work": {
                      "minItems": 5,
                      "maxItems": 5,
                      "description": "Time off a school's work, all sources, in percent.",
                      "type": "array",
                      "items": {
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer"
                      }
                    },
                    "move": {
                      "minItems": 5,
                      "maxItems": 5,
                      "description": "Time off a step, a crossing or an oar stroke, in percent.",
                      "type": "array",
                      "items": {
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer"
                      }
                    },
                    "carry": {
                      "minItems": 5,
                      "maxItems": 5,
                      "description": "The weight limit above might's and the bag's, in percent.",
                      "type": "array",
                      "items": {
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer"
                      }
                    },
                    "health": {
                      "minItems": 5,
                      "maxItems": 5,
                      "description": "The health cap above the attributes' cap, in percent.",
                      "type": "array",
                      "items": {
                        "minimum": 0,
                        "maximum": 200,
                        "type": "integer"
                      }
                    },
                    "sustain": {
                      "minItems": 5,
                      "maxItems": 5,
                      "description": "Health back a game hour while a place drains, regen and food together, in points of the base cap of 100 (the drain's unit).",
                      "type": "array",
                      "items": {
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer"
                      }
                    },
                    "find": {
                      "minItems": 5,
                      "maxItems": 5,
                      "description": "Rare-find points.",
                      "type": "array",
                      "items": {
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer"
                      }
                    },
                    "sense": {
                      "minItems": 5,
                      "maxItems": 5,
                      "description": "Tiles of sight in the dark or of a trail.",
                      "type": "array",
                      "items": {
                        "minimum": 0,
                        "maximum": 20,
                        "type": "integer"
                      }
                    },
                    "place": {
                      "minItems": 5,
                      "maxItems": 5,
                      "description": "Answer steps on one condition: garment, master step and dish together.",
                      "type": "array",
                      "items": {
                        "minimum": 0,
                        "maximum": 4,
                        "type": "integer"
                      }
                    }
                  }
                },
                "raise_step": {
                  "additionalProperties": false,
                  "description": "What one ceiling raiser adds to its category's top-band ceiling (PROGRESSION 4.7).",
                  "type": "object",
                  "required": [
                    "work",
                    "move",
                    "carry",
                    "health",
                    "sustain",
                    "find",
                    "sense",
                    "place"
                  ],
                  "properties": {
                    "work": {
                      "minimum": 0,
                      "maximum": 100,
                      "type": "integer"
                    },
                    "move": {
                      "minimum": 0,
                      "maximum": 100,
                      "type": "integer"
                    },
                    "carry": {
                      "minimum": 0,
                      "maximum": 100,
                      "type": "integer"
                    },
                    "health": {
                      "minimum": 0,
                      "maximum": 100,
                      "type": "integer"
                    },
                    "sustain": {
                      "minimum": 0,
                      "maximum": 100,
                      "type": "integer"
                    },
                    "find": {
                      "minimum": 0,
                      "maximum": 100,
                      "type": "integer"
                    },
                    "sense": {
                      "minimum": 0,
                      "maximum": 100,
                      "type": "integer"
                    },
                    "place": {
                      "minimum": 0,
                      "maximum": 100,
                      "type": "integer"
                    }
                  }
                },
                "raisers_max": {
                  "additionalProperties": false,
                  "description": "How many raisers of each category exist (PROGRESSION 4.7: ten in all today).",
                  "type": "object",
                  "required": [
                    "work",
                    "move",
                    "carry",
                    "health",
                    "sustain",
                    "find",
                    "sense",
                    "place"
                  ],
                  "properties": {
                    "work": {
                      "minimum": 0,
                      "maximum": 10,
                      "type": "integer"
                    },
                    "move": {
                      "minimum": 0,
                      "maximum": 10,
                      "type": "integer"
                    },
                    "carry": {
                      "minimum": 0,
                      "maximum": 10,
                      "type": "integer"
                    },
                    "health": {
                      "minimum": 0,
                      "maximum": 10,
                      "type": "integer"
                    },
                    "sustain": {
                      "minimum": 0,
                      "maximum": 10,
                      "type": "integer"
                    },
                    "find": {
                      "minimum": 0,
                      "maximum": 10,
                      "type": "integer"
                    },
                    "sense": {
                      "minimum": 0,
                      "maximum": 10,
                      "type": "integer"
                    },
                    "place": {
                      "minimum": 0,
                      "maximum": 10,
                      "type": "integer"
                    }
                  }
                },
                "wall_margin_bp": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "A wall's witness ends with at least this share of the health cap and of the timer to spare, in basis points."
                },
                "wall_cheese_gap": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Nothing from this many bands below a wall's band passes it (PROGRESSION 4.7: 2)."
                },
                "top_builds_n": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "How many strongest builds a band the prover lists (a warning, never a failure)."
                },
                "top_over_median_max_permille": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "The strongest build of a band warns when its score stands more than this above the band's median, in permille."
                },
                "proof_enum_max": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "Combinations walked in full; above it the proof walks each slot's Pareto front."
                },
                "rarity_effects_max": {
                  "minItems": 4,
                  "maxItems": 4,
                  "description": "The most effects a thing carries by rarity, common, uncommon, rare, legendary (PROGRESSION 5.7); a trait and a dish effect count.",
                  "type": "array",
                  "items": {
                    "minimum": 0,
                    "maximum": 4,
                    "type": "integer"
                  }
                },
                "rarity_band_permille": {
                  "minItems": 4,
                  "maxItems": 4,
                  "description": "Each rarity's share of the tier's full strength, low and high, in permille (PROGRESSION 5.7); common is the smallest step, read as 0 here.",
                  "type": "array",
                  "items": {
                    "minItems": 2,
                    "maxItems": 2,
                    "type": "array",
                    "items": {
                      "minimum": 0,
                      "maximum": 1000,
                      "type": "integer"
                    }
                  }
                },
                "trait_strength_by_tier": {
                  "minItems": 1,
                  "maxItems": 10,
                  "description": "A garment trait's strength by the tier it counts as, from tier 1 (BODY_CLOCKS 6.2).",
                  "type": "array",
                  "items": {
                    "minimum": 0,
                    "maximum": 4,
                    "type": "integer"
                  }
                },
                "master_step": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "What a master piece adds to its trait (BODY_CLOCKS 6.2)."
                },
                "dish_place_step": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "What a dish of the same condition adds; only one dish counts (BODY_CLOCKS 6.2)."
                },
                "dish_slots": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "value",
                    "min",
                    "max",
                    "step"
                  ],
                  "properties": {
                    "value": {
                      "type": "integer"
                    },
                    "min": {
                      "type": "integer"
                    },
                    "max": {
                      "type": "integer"
                    },
                    "step": {
                      "minimum": 0,
                      "description": "Largest change one decision may make.",
                      "type": "integer"
                    }
                  },
                  "description": "How many dishes act at once (BODY_CLOCKS 7)."
                },
                "grade_walk_permille": {
                  "minItems": 5,
                  "maxItems": 5,
                  "description": "The slow of a remaining grade 0 to 4, in permille of a step's time (BODY_CLOCKS 5.2).",
                  "type": "array",
                  "items": {
                    "minimum": 1000,
                    "maximum": 5000,
                    "type": "integer"
                  }
                },
                "grade_drain_pct": {
                  "minItems": 5,
                  "maxItems": 5,
                  "description": "The drain of a remaining grade 0 to 4, in points of the base cap of 100 a game hour, the same for every cap (BODY_CLOCKS 5.2).",
                  "type": "array",
                  "items": {
                    "minimum": 0,
                    "maximum": 100,
                    "type": "integer"
                  }
                }
              }
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "kit",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "kit"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "kit",
          "type": "string"
        },
        "kit": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "sprites"
          ],
          "properties": {
            "palette": {
              "minItems": 2,
              "maxItems": 32,
              "description": "Only the core kit sets the palette. Every sprite of every kit must use these colors.",
              "type": "array",
              "items": {
                "pattern": "^#[0-9a-f]{6}$",
                "type": "string"
              }
            },
            "ramps": {
              "maxItems": 4,
              "description": "The ramps this kit draws with (ART_SYSTEM section 3). A sprite that names none uses the core palette only, so every kit written before this field stays valid.",
              "type": "array",
              "items": {
                "additionalProperties": false,
                "description": "A material or region ramp, declared by the kit that draws with it.",
                "type": "object",
                "required": [
                  "id",
                  "steps"
                ],
                "properties": {
                  "id": {
                    "pattern": "^[a-z0-9][a-z0-9-]*$",
                    "maxLength": 32,
                    "description": "Names the ramp so a sprite of this kit can cite it, for example \"meadow\" or \"wet-stone\".",
                    "type": "string"
                  },
                  "for": {
                    "anyOf": [
                      {
                        "const": "ground",
                        "type": "string"
                      },
                      {
                        "const": "vegetation",
                        "type": "string"
                      },
                      {
                        "const": "stone",
                        "type": "string"
                      },
                      {
                        "const": "wood",
                        "type": "string"
                      },
                      {
                        "const": "water",
                        "type": "string"
                      },
                      {
                        "const": "cloth",
                        "type": "string"
                      },
                      {
                        "const": "skin",
                        "type": "string"
                      },
                      {
                        "const": "light",
                        "type": "string"
                      }
                    ]
                  },
                  "steps": {
                    "minItems": 5,
                    "maxItems": 6,
                    "description": "Five or six steps from dark to light, the hue moving between them: a ramp that only changes value is not a ramp and the check says so.",
                    "type": "array",
                    "items": {
                      "pattern": "^#[0-9a-f]{6}$",
                      "description": "One step of the ramp, dark to light.",
                      "type": "string"
                    }
                  }
                }
              }
            },
            "sprites": {
              "description": "Sprite name (group/name) to its PNG. Groups: tiles, items, chars, props, ui.",
              "type": "object",
              "patternProperties": {
                "^[a-z0-9_-]+/[a-z0-9_/-]+$": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "file",
                    "w",
                    "h"
                  ],
                  "properties": {
                    "file": {
                      "pattern": "^[a-z0-9_/-]+\\.png$",
                      "description": "PNG next to this file.",
                      "type": "string"
                    },
                    "w": {
                      "minimum": 8,
                      "maximum": 384,
                      "description": "Frame width in pixels. At density 1 (16 art pixels per tile) tiles/ and items/ are 16, chars/ are 16, a work pose 32 and props up to 64. At density 4 (64 art pixels per tile) tiles/ and items/ are 64, chars/ are 64, a work pose 128 and props up to 384.",
                      "type": "integer"
                    },
                    "h": {
                      "minimum": 8,
                      "maximum": 384,
                      "description": "Frame height in pixels. At density 1 tiles/ and items/ are 16, chars/ are 24, a work pose 32 and props up to 64. At density 4 tiles/ and items/ are 64, chars/ are 96, a work pose 128 and props up to 384.",
                      "type": "integer"
                    },
                    "frames": {
                      "minimum": 1,
                      "maximum": 16,
                      "description": "Animation frames laid side by side. Default 1.",
                      "type": "integer"
                    },
                    "ramps": {
                      "minItems": 1,
                      "maxItems": 4,
                      "description": "The ramps of this kit the sprite draws in, in order. The outline is the one-pixel ink line #1b1a2e on every sprite, whatever the ramps. A sprite that names none uses the core palette only.",
                      "type": "array",
                      "items": {
                        "pattern": "^[a-z0-9][a-z0-9-]*$",
                        "maxLength": 32,
                        "type": "string"
                      }
                    },
                    "density": {
                      "description": "Art pixels per tile side: 1 means 16 per tile (the 16x16 grid, the default), 4 means 64 per tile (the 64x64 grid, the standard for new art). The size rules of w and h follow it.",
                      "anyOf": [
                        {
                          "const": 1,
                          "type": "number"
                        },
                        {
                          "const": 4,
                          "type": "number"
                        }
                      ]
                    },
                    "stride": {
                      "additionalProperties": false,
                      "description": "A walk cycle that follows the distance walked (tools/anim). Its frames are the poses of the cycle in order and, last, the standing pose; `frames` is the poses plus one. Left out for a sheet whose frames are time-based.",
                      "type": "object",
                      "required": [
                        "travel_mpx",
                        "anchor"
                      ],
                      "properties": {
                        "travel_mpx": {
                          "minimum": 1000,
                          "maximum": 128000,
                          "description": "Thousandths of an art pixel (64 per tile) the body walks for one pose of the cycle: the pose shown is the nearest to art pixels walked / (travel_mpx / 1000), so a planted foot is where the ground is.",
                          "type": "integer"
                        },
                        "anchor": {
                          "description": "The point of the ground under the feet, in pixels of the frame: the sprite is drawn with it on the body's feet.",
                          "type": "array",
                          "items": [
                            {
                              "minimum": 0,
                              "maximum": 384,
                              "type": "integer"
                            },
                            {
                              "minimum": 0,
                              "maximum": 384,
                              "type": "integer"
                            }
                          ],
                          "additionalItems": false,
                          "minItems": 2,
                          "maxItems": 2
                        },
                        "bob": {
                          "description": "false when the sheet carries its own head travel and the feel layer's code bob is dropped for it (DECISIONS 2026-10-29: the bob is dropped for a clip that carries its own); left out, the bob plays on top of the poses.",
                          "type": "boolean"
                        }
                      }
                    },
                    "scale": {
                      "minimum": 0.1,
                      "maximum": 40,
                      "description": "The sprite's height in characters (NATURE section 1): a person is 1.0, a house 3.5. The check warns when a kind in NATURE section 1 or STYLE 1b falls under its floor. Left out when the sprite has no true size.",
                      "type": "number"
                    },
                    "source": {
                      "additionalProperties": false,
                      "description": "Where a generated sprite came from (ASSET_PATH section 4, item 2). Left out for a hand-drawn one.",
                      "type": "object",
                      "required": [
                        "spec",
                        "manifest"
                      ],
                      "properties": {
                        "spec": {
                          "minLength": 1,
                          "description": "The pnpm kit:gen spec file this sprite was made from, as a path from the repository root.",
                          "type": "string"
                        },
                        "manifest": {
                          "minLength": 1,
                          "description": "The manifest.jsonl line of that run, as <path>:<line number>.",
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "map",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "map"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "map",
          "type": "string"
        },
        "map": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "width",
            "height",
            "legend",
            "ground",
            "props",
            "spawn",
            "slots"
          ],
          "properties": {
            "climate": {
              "description": "The climate this region declares (NATURE section 2): which ramp set, densities, kinds, air and sound beds the engine reads. Left out, the region is read as temperate.",
              "anyOf": [
                {
                  "const": "temperate",
                  "type": "string"
                },
                {
                  "const": "dry",
                  "type": "string"
                },
                {
                  "const": "cold",
                  "type": "string"
                },
                {
                  "const": "wet",
                  "type": "string"
                }
              ]
            },
            "creatures": {
              "additionalProperties": false,
              "description": "Ambient life for the client to draw behind ?rich= motion (no client draws it yet): kinds, and the spots where each sits until the player comes close. A kind with \"hunt\" is the kernel's too: it dresses its carcasses and the value table prices its goods, with its spots as the source's capacity (HUNTING sections 5-9). A kind with \"behaviour\" carries its character as content, the fields the wild reads (CREATURE_BEHAVIOUR sections 1 and 5.1); its spots are its anchors, a den's mouth for a kind that sleeps in a den (HUNT_FAMILIES section 3.1).",
              "type": "object",
              "required": [
                "kinds",
                "spots"
              ],
              "properties": {
                "kinds": {
                  "type": "object",
                  "patternProperties": {
                    "^[a-z0-9_-]+/[a-z0-9_-]+$": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "look",
                        "perch",
                        "alert_tiles",
                        "return_s"
                      ],
                      "properties": {
                        "look": {
                          "description": "The drawing and the way it leaves: a bird takes off, a fish darts, a rabbit bolts, a deer walks off, a canid lopes away.",
                          "anyOf": [
                            {
                              "const": "bird",
                              "type": "string"
                            },
                            {
                              "const": "fish",
                              "type": "string"
                            },
                            {
                              "const": "rabbit",
                              "type": "string"
                            },
                            {
                              "const": "deer",
                              "type": "string"
                            },
                            {
                              "const": "canid",
                              "type": "string"
                            }
                          ]
                        },
                        "perch": {
                          "description": "What its spots stand on: a non-solid ground tile, a forest tile, or a water tile.",
                          "anyOf": [
                            {
                              "const": "ground",
                              "type": "string"
                            },
                            {
                              "const": "tree",
                              "type": "string"
                            },
                            {
                              "const": "water",
                              "type": "string"
                            }
                          ]
                        },
                        "alert_tiles": {
                          "minimum": 1,
                          "maximum": 8,
                          "description": "It leaves when the player comes within this many tiles.",
                          "type": "integer"
                        },
                        "return_s": {
                          "minimum": 5,
                          "maximum": 600,
                          "description": "Real seconds before it comes back to its spot once the player is away.",
                          "type": "integer"
                        },
                        "hours": {
                          "description": "The game hours it is out, from (inclusive) to (exclusive); a night kind wraps midnight, from after to (the wolf's 20 to 5). Outside them it has gone to ground (BESTIARY section 1). Absent: out at every hour, the night included, as the wild brain's `awake` reads it, so a day kind names its hours; every kind of the core does.",
                          "type": "array",
                          "items": [
                            {
                              "minimum": 0,
                              "maximum": 23,
                              "type": "integer"
                            },
                            {
                              "minimum": 1,
                              "maximum": 24,
                              "type": "integer"
                            }
                          ],
                          "additionalItems": false,
                          "minItems": 2,
                          "maxItems": 2
                        },
                        "behaviour": {
                          "additionalProperties": false,
                          "type": "object",
                          "required": [
                            "sight",
                            "ear",
                            "weights",
                            "roam",
                            "flight",
                            "sleeps",
                            "alarm",
                            "fire",
                            "water",
                            "weather",
                            "town",
                            "habitat",
                            "group",
                            "family"
                          ],
                          "properties": {
                            "sight": {
                              "minimum": 1,
                              "maximum": 12,
                              "description": "Tiles it sees in its cone, by the squared distance (HUNTING sections 3 and 8).",
                              "type": "integer"
                            },
                            "ear": {
                              "description": "HUNTING section 3's ear: -2 dull, 0 normal, +2 keen. A noise of radius n is heard within n + ear tiles.",
                              "anyOf": [
                                {
                                  "const": -2,
                                  "type": "number"
                                },
                                {
                                  "const": 0,
                                  "type": "number"
                                },
                                {
                                  "const": 2,
                                  "type": "number"
                                }
                              ]
                            },
                            "smell": {
                              "minimum": 0,
                              "maximum": 12,
                              "description": "Its nose (HUNTING section 8). Stored, read by nothing until the wind is built (HUNTING section 11, question 6).",
                              "type": "integer"
                            },
                            "weights": {
                              "additionalProperties": false,
                              "description": "Shares of 100 for where a quiet state goes when it ends (CREATURE_BEHAVIOUR section 1): a heron stands (60/30/10), a fox ranges (10/20/70). For a hunter GRAZE is the rest at its den.",
                              "type": "object",
                              "required": [
                                "idle",
                                "graze",
                                "wander"
                              ],
                              "properties": {
                                "idle": {
                                  "minimum": 0,
                                  "maximum": 100,
                                  "type": "integer"
                                },
                                "graze": {
                                  "minimum": 0,
                                  "maximum": 100,
                                  "type": "integer"
                                },
                                "wander": {
                                  "minimum": 0,
                                  "maximum": 100,
                                  "type": "integer"
                                }
                              }
                            },
                            "roam": {
                              "minimum": 6,
                              "maximum": 14,
                              "description": "Tiles round its anchor WANDER picks in, and how far RETURN walks (HUNTING section 2).",
                              "type": "integer"
                            },
                            "flight": {
                              "description": "How FLEE runs and ends: ground runs the distance map, fly lands sight + 4 away, swim ends under water, climb up a tree or a rock, burrow into its anchor (CREATURE_BEHAVIOUR section 1).",
                              "anyOf": [
                                {
                                  "const": "ground",
                                  "type": "string"
                                },
                                {
                                  "const": "fly",
                                  "type": "string"
                                },
                                {
                                  "const": "swim",
                                  "type": "string"
                                },
                                {
                                  "const": "climb",
                                  "type": "string"
                                },
                                {
                                  "const": "burrow",
                                  "type": "string"
                                }
                              ]
                            },
                            "sleeps": {
                              "description": "What its anchor stands on and where it goes to ground out of its hours: a form in the grass, a perch, the river bed, a warm rock, the reeds, a den, a roof beam. A day kind leaves the map there at night rather than sleeping in view, a few stragglers at most (DECISIONS 2026-10-30: the night empties the day kinds).",
                              "anyOf": [
                                {
                                  "const": "ground",
                                  "type": "string"
                                },
                                {
                                  "const": "tree",
                                  "type": "string"
                                },
                                {
                                  "const": "water",
                                  "type": "string"
                                },
                                {
                                  "const": "rock",
                                  "type": "string"
                                },
                                {
                                  "const": "reeds",
                                  "type": "string"
                                },
                                {
                                  "const": "den",
                                  "type": "string"
                                },
                                {
                                  "const": "roof",
                                  "type": "string"
                                }
                              ]
                            },
                            "alarm": {
                              "minimum": 0,
                              "maximum": 12,
                              "description": "The noise its ALERT tell makes, in HUNTING section 4's table, so other animals hear it; 0 is a silent kind (CREATURE_BEHAVIOUR section 1).",
                              "type": "integer"
                            },
                            "looks_back": {
                              "description": "At FLEE's end it takes two steps of ALERT at sight + 2, facing the player, before WANDER: the deer's stop and turn. Absent: no.",
                              "type": "boolean"
                            },
                            "drinks": {
                              "description": "Twice a day its WANDER target is the nearest water or riverbank tile within roam, at dawn and at dusk (CREATURE_BEHAVIOUR section 4). Absent: no.",
                              "type": "boolean"
                            },
                            "carrion": {
                              "description": "It comes to a carcass within sight + 4 after 30 think steps (CREATURE_BEHAVIOUR section 2). Absent: no.",
                              "type": "boolean"
                            },
                            "fire": {
                              "description": "A fire on the ground: prey keeps away, a hunter holds at the edge of its safe ring, a moth is drawn to a lamp, or it ignores the fire (CREATURE_BEHAVIOUR section 2).",
                              "anyOf": [
                                {
                                  "const": "keeps_away",
                                  "type": "string"
                                },
                                {
                                  "const": "holds_edge",
                                  "type": "string"
                                },
                                {
                                  "const": "drawn_to_lamp",
                                  "type": "string"
                                },
                                {
                                  "const": "ignores",
                                  "type": "string"
                                }
                              ]
                            },
                            "water": {
                              "description": "A water tile: a wall, crossed in the air, swum, lived in, or the edge it lives along (CREATURE_BEHAVIOUR section 1).",
                              "anyOf": [
                                {
                                  "const": "wall",
                                  "type": "string"
                                },
                                {
                                  "const": "crosses",
                                  "type": "string"
                                },
                                {
                                  "const": "swims",
                                  "type": "string"
                                },
                                {
                                  "const": "lives_in",
                                  "type": "string"
                                },
                                {
                                  "const": "edge",
                                  "type": "string"
                                }
                              ]
                            },
                            "weather": {
                              "description": "Rain and storm: cover under the trees in rain, roost in a storm, stay in while it rains, or no change beyond the shorter hearing (CREATURE_BEHAVIOUR section 2).",
                              "anyOf": [
                                {
                                  "const": "cover",
                                  "type": "string"
                                },
                                {
                                  "const": "roosts",
                                  "type": "string"
                                },
                                {
                                  "const": "stays_in",
                                  "type": "string"
                                },
                                {
                                  "const": "none",
                                  "type": "string"
                                }
                              ]
                            },
                            "town": {
                              "description": "The town: a wall it never crosses, its own ground (a town kind: the sparrow, the crow, the rat under a roof), or nothing (CREATURE_BEHAVIOUR sections 2 and 5).",
                              "anyOf": [
                                {
                                  "const": "avoids",
                                  "type": "string"
                                },
                                {
                                  "const": "town_kind",
                                  "type": "string"
                                },
                                {
                                  "const": "none",
                                  "type": "string"
                                }
                              ]
                            },
                            "habitat": {
                              "minItems": 1,
                              "maxItems": 8,
                              "uniqueItems": true,
                              "description": "The tile tags it lives on: where the spawner lays it and WANDER picks, and the tiles its spawn cap is read over (CREATURE_BEHAVIOUR section 3). A wild kind never names town.",
                              "type": "array",
                              "items": {
                                "anyOf": [
                                  {
                                    "const": "grove",
                                    "type": "string"
                                  },
                                  {
                                    "const": "meadow",
                                    "type": "string"
                                  },
                                  {
                                    "const": "riverbank",
                                    "type": "string"
                                  },
                                  {
                                    "const": "ruin",
                                    "type": "string"
                                  },
                                  {
                                    "const": "town",
                                    "type": "string"
                                  },
                                  {
                                    "const": "water",
                                    "type": "string"
                                  },
                                  {
                                    "const": "path",
                                    "type": "string"
                                  },
                                  {
                                    "const": "interior",
                                    "type": "string"
                                  }
                                ]
                              }
                            },
                            "group": {
                              "additionalProperties": false,
                              "description": "Who goes with whom (CREATURE_BEHAVIOUR section 5.1): the class says how the group moves and flees, the bands how many stand at one anchor. The size is drawn on the server, once a group period, while nobody can see the group.",
                              "type": "object",
                              "required": [
                                "class",
                                "bands"
                              ],
                              "properties": {
                                "class": {
                                  "description": "solitary: one at its anchor; herd: scatters on FLEE and regroups at the anchor; flock: lifts and lands as one; pack: keeps within 2 of its leader on every move (CREATURE_BEHAVIOUR section 5.1.3).",
                                  "anyOf": [
                                    {
                                      "const": "solitary",
                                      "type": "string"
                                    },
                                    {
                                      "const": "herd",
                                      "type": "string"
                                    },
                                    {
                                      "const": "flock",
                                      "type": "string"
                                    },
                                    {
                                      "const": "pack",
                                      "type": "string"
                                    }
                                  ]
                                },
                                "bands": {
                                  "description": "Weights over the bands solo (1), pair (2), small (3 to 4) and large (5 to 12) that sum to 100: how many stand at one anchor, drawn once a group period.",
                                  "type": "array",
                                  "items": [
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    }
                                  ],
                                  "additionalItems": false,
                                  "minItems": 4,
                                  "maxItems": 4
                                },
                                "sentinel": {
                                  "description": "Only on a herd or a flock: its leader never GRAZEs while the others do, and its ALERT is its alarm noise (CREATURE_BEHAVIOUR section 5.1.1). Absent: no.",
                                  "type": "boolean"
                                },
                                "season": {
                                  "additionalProperties": false,
                                  "minProperties": 1,
                                  "description": "Bands that replace the plain ones in one season: pairs in spring, larger groups in winter (CREATURE_BEHAVIOUR section 5.1.4). Read by nothing until seasons are built; the cap and the refill never move with it.",
                                  "type": "object",
                                  "properties": {
                                    "spring": {
                                      "description": "Weights over the bands solo (1), pair (2), small (3 to 4) and large (5 to 12) that sum to 100: how many stand at one anchor, drawn once a group period.",
                                      "type": "array",
                                      "items": [
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        },
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        },
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        },
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        }
                                      ],
                                      "additionalItems": false,
                                      "minItems": 4,
                                      "maxItems": 4
                                    },
                                    "summer": {
                                      "description": "Weights over the bands solo (1), pair (2), small (3 to 4) and large (5 to 12) that sum to 100: how many stand at one anchor, drawn once a group period.",
                                      "type": "array",
                                      "items": [
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        },
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        },
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        },
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        }
                                      ],
                                      "additionalItems": false,
                                      "minItems": 4,
                                      "maxItems": 4
                                    },
                                    "autumn": {
                                      "description": "Weights over the bands solo (1), pair (2), small (3 to 4) and large (5 to 12) that sum to 100: how many stand at one anchor, drawn once a group period.",
                                      "type": "array",
                                      "items": [
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        },
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        },
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        },
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        }
                                      ],
                                      "additionalItems": false,
                                      "minItems": 4,
                                      "maxItems": 4
                                    },
                                    "winter": {
                                      "description": "Weights over the bands solo (1), pair (2), small (3 to 4) and large (5 to 12) that sum to 100: how many stand at one anchor, drawn once a group period.",
                                      "type": "array",
                                      "items": [
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        },
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        },
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        },
                                        {
                                          "minimum": 0,
                                          "maximum": 100,
                                          "type": "integer"
                                        }
                                      ],
                                      "additionalItems": false,
                                      "minItems": 4,
                                      "maxItems": 4
                                    }
                                  }
                                }
                              }
                            },
                            "family": {
                              "description": "Its hunt family (HUNT_FAMILIES section 2): one body, one hunting style, one shape of drops.",
                              "anyOf": [
                                {
                                  "const": "burrowers",
                                  "type": "string"
                                },
                                {
                                  "const": "deer",
                                  "type": "string"
                                },
                                {
                                  "const": "wild-goats",
                                  "type": "string"
                                },
                                {
                                  "const": "pigs",
                                  "type": "string"
                                },
                                {
                                  "const": "canids",
                                  "type": "string"
                                },
                                {
                                  "const": "cats",
                                  "type": "string"
                                },
                                {
                                  "const": "bears",
                                  "type": "string"
                                },
                                {
                                  "const": "game-birds",
                                  "type": "string"
                                },
                                {
                                  "const": "perching-birds",
                                  "type": "string"
                                },
                                {
                                  "const": "waterfowl",
                                  "type": "string"
                                },
                                {
                                  "const": "raptors",
                                  "type": "string"
                                },
                                {
                                  "const": "mustelids",
                                  "type": "string"
                                },
                                {
                                  "const": "fish",
                                  "type": "string"
                                },
                                {
                                  "const": "reptiles",
                                  "type": "string"
                                },
                                {
                                  "const": "night-fliers",
                                  "type": "string"
                                },
                                {
                                  "const": "horned-beasts",
                                  "type": "string"
                                },
                                {
                                  "const": "spiders",
                                  "type": "string"
                                },
                                {
                                  "const": "drakes",
                                  "type": "string"
                                }
                              ]
                            },
                            "flush": {
                              "description": "Its found den may be flushed (HUNT_FAMILIES section 3.1, the line home): a smoke bundle at the mouth bolts it out of the bolt hole, a second tile 8 to 10 tiles away the server draws once per anchor and never moves. Only a burrower that sleeps in a den at danger 0. Absent: no.",
                              "type": "boolean"
                            }
                          }
                        },
                        "hunt": {
                          "additionalProperties": false,
                          "type": "object",
                          "required": [
                            "size",
                            "tier",
                            "danger",
                            "take_s",
                            "drops",
                            "spawn"
                          ],
                          "properties": {
                            "size": {
                              "description": "Small dresses in 6 steps, medium in 12, large in 24: parts of six, one action a part, the goods with the last.",
                              "anyOf": [
                                {
                                  "const": "small",
                                  "type": "string"
                                },
                                {
                                  "const": "medium",
                                  "type": "string"
                                },
                                {
                                  "const": "large",
                                  "type": "string"
                                }
                              ]
                            },
                            "tier": {
                              "minimum": 1,
                              "maximum": 20,
                              "description": "How far up the ladder it is. Every good it drops sits at this tier or below.",
                              "type": "integer"
                            },
                            "danger": {
                              "description": "What it does when the player comes close (HUNTING section 8): 0 flees, 1 stands and defends, 2 hunts the player. A danger 2 kind is medium or large.",
                              "anyOf": [
                                {
                                  "const": 0,
                                  "type": "number"
                                },
                                {
                                  "const": 1,
                                  "type": "number"
                                },
                                {
                                  "const": 2,
                                  "type": "number"
                                }
                              ]
                            },
                            "take_s": {
                              "minimum": 5,
                              "maximum": 600,
                              "description": "Real seconds a fair hunt of it takes, from first sight to the carcass: the active time the value table prices, before the dressing.",
                              "type": "integer"
                            },
                            "drops": {
                              "minItems": 1,
                              "maxItems": 5,
                              "description": "What dressing it gives: plain goods of the five goods classes, each class once.",
                              "type": "array",
                              "items": {
                                "additionalProperties": false,
                                "type": "object",
                                "required": [
                                  "item",
                                  "qty",
                                  "share_pct"
                                ],
                                "properties": {
                                  "item": {
                                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                                    "maxLength": 80,
                                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                                    "type": "string"
                                  },
                                  "qty": {
                                    "minimum": 1,
                                    "maximum": 20,
                                    "description": "Units a carcass gives. A finer animal gives the same count of the finer grade.",
                                    "type": "integer"
                                  },
                                  "share_pct": {
                                    "minimum": 1,
                                    "maximum": 100,
                                    "description": "Part of the hunt its value carries. Shares of one kind sum to 100.",
                                    "type": "integer"
                                  }
                                }
                              }
                            },
                            "ways": {
                              "minItems": 1,
                              "maxItems": 3,
                              "uniqueItems": true,
                              "description": "How it can be taken, for the client to show. Absent: the client decides.",
                              "type": "array",
                              "items": {
                                "anyOf": [
                                  {
                                    "const": "stalk",
                                    "type": "string"
                                  },
                                  {
                                    "const": "snare",
                                    "type": "string"
                                  },
                                  {
                                    "const": "lure",
                                    "type": "string"
                                  },
                                  {
                                    "const": "fish",
                                    "type": "string"
                                  },
                                  {
                                    "const": "fight",
                                    "type": "string"
                                  }
                                ]
                              }
                            },
                            "spawn": {
                              "additionalProperties": false,
                              "type": "object",
                              "required": [
                                "cap_per_1000",
                                "refill_d"
                              ],
                              "properties": {
                                "cap_per_1000": {
                                  "minimum": 1,
                                  "maximum": 20,
                                  "description": "The most of this kind alive per 1000 tiles of its habitat (HUNTING section 8: at most 12 at danger 0, 8 at 1, 5 at 2). The kind's danger stands for its region's band until a spot carries a region.",
                                  "type": "integer"
                                },
                                "refill_d": {
                                  "minimum": 1,
                                  "maximum": 30,
                                  "description": "Game days before a taken animal is replaced at its spot: the respawn the value table counts.",
                                  "type": "integer"
                                }
                              }
                            }
                          }
                        },
                        "protected": {
                          "description": "A pet, a young animal or a person (HUNTING section 10): seen, never taken, never dressed. A protected kind carries no \"hunt\".",
                          "type": "boolean"
                        }
                      }
                    }
                  }
                },
                "spots": {
                  "maxItems": 200,
                  "type": "array",
                  "items": {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "kind",
                      "x",
                      "y"
                    ],
                    "properties": {
                      "kind": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      },
                      "x": {
                        "minimum": 0,
                        "maximum": 1023,
                        "type": "integer"
                      },
                      "y": {
                        "minimum": 0,
                        "maximum": 1023,
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            },
            "width": {
              "minimum": 8,
              "maximum": 1024,
              "type": "integer"
            },
            "height": {
              "minimum": 8,
              "maximum": 1024,
              "type": "integer"
            },
            "legend": {
              "type": "object",
              "patternProperties": {
                "^.$": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "sprite",
                    "solid",
                    "tag"
                  ],
                  "properties": {
                    "sprite": {
                      "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                      "maxLength": 120,
                      "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                      "type": "string"
                    },
                    "solid": {
                      "type": "boolean"
                    },
                    "tag": {
                      "anyOf": [
                        {
                          "const": "grove",
                          "type": "string"
                        },
                        {
                          "const": "meadow",
                          "type": "string"
                        },
                        {
                          "const": "riverbank",
                          "type": "string"
                        },
                        {
                          "const": "ruin",
                          "type": "string"
                        },
                        {
                          "const": "town",
                          "type": "string"
                        },
                        {
                          "const": "water",
                          "type": "string"
                        },
                        {
                          "const": "path",
                          "type": "string"
                        },
                        {
                          "const": "interior",
                          "type": "string"
                        }
                      ]
                    }
                  }
                }
              }
            },
            "ground": {
              "minItems": 8,
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "levels": {
              "minItems": 8,
              "description": "The height layer beside the ground layer (STYLE section 6): one digit 0 to 9 per tile, integers only. Absent: every tile is level 0.",
              "type": "array",
              "items": {
                "pattern": "^[0-9]+$",
                "description": "One level digit (0 to 9) per tile, rows like the ground (STYLE section 6).",
                "type": "string"
              }
            },
            "plan": {
              "additionalProperties": false,
              "type": "object",
              "properties": {
                "regions": {
                  "maxItems": 64,
                  "type": "array",
                  "items": {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "id",
                      "purpose",
                      "cells"
                    ],
                    "properties": {
                      "id": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      },
                      "purpose": {
                        "anyOf": [
                          {
                            "const": "stop",
                            "type": "string"
                          },
                          {
                            "const": "passage",
                            "type": "string"
                          }
                        ]
                      },
                      "cells": {
                        "minItems": 1,
                        "maxItems": 8,
                        "description": "The rectangles the region covers: x, y, width, height.",
                        "type": "array",
                        "items": {
                          "type": "array",
                          "items": [
                            {
                              "minimum": 0,
                              "maximum": 1023,
                              "type": "integer"
                            },
                            {
                              "minimum": 0,
                              "maximum": 1023,
                              "type": "integer"
                            },
                            {
                              "minimum": 1,
                              "maximum": 1024,
                              "type": "integer"
                            },
                            {
                              "minimum": 1,
                              "maximum": 1024,
                              "type": "integer"
                            }
                          ],
                          "additionalItems": false,
                          "minItems": 4,
                          "maxItems": 4
                        }
                      },
                      "height": {
                        "minimum": 0,
                        "maximum": 9,
                        "description": "The level the region stands on, read against `levels`.",
                        "type": "integer"
                      },
                      "tags": {
                        "maxItems": 8,
                        "uniqueItems": true,
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9:_-]+$",
                          "maxLength": 40,
                          "type": "string"
                        }
                      },
                      "cover": {
                        "description": "The scatter line: per mille of the walkable tiles each small prop covers.",
                        "type": "object",
                        "patternProperties": {
                          "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$": {
                            "minimum": 0,
                            "maximum": 300,
                            "type": "integer"
                          }
                        }
                      },
                      "opens_by": {
                        "description": "\"wake\": open from the first wake. Otherwise the quest whose reward opens the way in (decision 25 of 22.10); a quest not yet written is a warning, never an error. Left out: ground nobody walks (water, the woods, a mountain band).",
                        "anyOf": [
                          {
                            "const": "wake",
                            "type": "string"
                          },
                          {
                            "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                            "maxLength": 80,
                            "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                            "type": "string"
                          }
                        ]
                      }
                    }
                  }
                },
                "paths": {
                  "maxItems": 64,
                  "type": "array",
                  "items": {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "id",
                      "width",
                      "via"
                    ],
                    "properties": {
                      "id": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      },
                      "width": {
                        "minimum": 1,
                        "maximum": 2,
                        "description": "1 for feet, 2 for a barrow or a cart.",
                        "type": "integer"
                      },
                      "via": {
                        "minItems": 2,
                        "maxItems": 32,
                        "description": "The corners of the path, each pair on one row or one column; every tile between is a path tile or a mended crossing (G69).",
                        "type": "array",
                        "items": {
                          "description": "A tile: x, then y.",
                          "type": "array",
                          "items": [
                            {
                              "minimum": 0,
                              "maximum": 1023,
                              "type": "integer"
                            },
                            {
                              "minimum": 0,
                              "maximum": 1023,
                              "type": "integer"
                            }
                          ],
                          "additionalItems": false,
                          "minItems": 2,
                          "maxItems": 2
                        }
                      }
                    }
                  }
                },
                "routes": {
                  "maxItems": 32,
                  "type": "array",
                  "items": {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "id",
                      "who",
                      "hours",
                      "via"
                    ],
                    "properties": {
                      "id": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      },
                      "who": {
                        "minLength": 1,
                        "maxLength": 40,
                        "type": "string"
                      },
                      "hours": {
                        "description": "From (inclusive) to (exclusive), game hours; a later from than to runs over midnight.",
                        "type": "array",
                        "items": [
                          {
                            "minimum": 0,
                            "maximum": 23,
                            "type": "integer"
                          },
                          {
                            "minimum": 1,
                            "maximum": 24,
                            "type": "integer"
                          }
                        ],
                        "additionalItems": false,
                        "minItems": 2,
                        "maxItems": 2
                      },
                      "via": {
                        "minItems": 1,
                        "maxItems": 48,
                        "description": "Points a person walks through, each a tile a foot can stand on.",
                        "type": "array",
                        "items": {
                          "description": "A tile: x, then y.",
                          "type": "array",
                          "items": [
                            {
                              "minimum": 0,
                              "maximum": 1023,
                              "type": "integer"
                            },
                            {
                              "minimum": 0,
                              "maximum": 1023,
                              "type": "integer"
                            }
                          ],
                          "additionalItems": false,
                          "minItems": 2,
                          "maxItems": 2
                        }
                      }
                    }
                  }
                },
                "rests": {
                  "maxItems": 32,
                  "type": "array",
                  "items": {
                    "additionalProperties": false,
                    "description": "The top-left tile of a plain 3x3 the scatter leaves empty.",
                    "type": "object",
                    "required": [
                      "x",
                      "y"
                    ],
                    "properties": {
                      "x": {
                        "minimum": 0,
                        "maximum": 1023,
                        "type": "integer"
                      },
                      "y": {
                        "minimum": 0,
                        "maximum": 1023,
                        "type": "integer"
                      }
                    }
                  }
                },
                "wakes": {
                  "maxItems": 16,
                  "description": "The spawn pool: every wake the seed may draw, with its first task.",
                  "type": "array",
                  "items": {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "pocket",
                      "quest"
                    ],
                    "properties": {
                      "pocket": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "A wake pocket of the pool (content/core/pockets): its file holds the wake tile and the tools on the ground.",
                        "type": "string"
                      },
                      "quest": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "The first task of that pocket: a quest whose `pocket` names it.",
                        "type": "string"
                      }
                    }
                  }
                },
                "exits": {
                  "maxItems": 16,
                  "description": "The ways out of the first region: the first night is held by the length of the road beyond each one, never by the gate (DECISIONS.md, 2026-10-23).",
                  "type": "array",
                  "items": {
                    "additionalProperties": false,
                    "type": "object",
                    "required": [
                      "id",
                      "to",
                      "min_road_steps"
                    ],
                    "properties": {
                      "id": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      },
                      "slot": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "The gate: the slot a region quest opens (decision 25). Give this or `at`.",
                        "type": "string"
                      },
                      "at": {
                        "description": "A way out with no gate slot (the jetty where the boat waits): the last tile a foot stands on.",
                        "type": "array",
                        "items": [
                          {
                            "minimum": 0,
                            "maximum": 1023,
                            "type": "integer"
                          },
                          {
                            "minimum": 0,
                            "maximum": 1023,
                            "type": "integer"
                          }
                        ],
                        "additionalItems": false,
                        "minItems": 2,
                        "maxItems": 2
                      },
                      "to": {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "The plan region the way leads into.",
                        "type": "string"
                      },
                      "min_road_steps": {
                        "minimum": 1,
                        "maximum": 100000,
                        "description": "G71: the fewest steps the road beyond this exit must run before the next region's ground, so that no wake reaches that ground before the first dusk with every gate open. The daylight in steps at the fastest wake, less its steps to this exit, plus a margin; the check refuses a smaller number and any stitched map whose road beyond is shorter.",
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            },
            "props": {
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "sprite",
                  "x",
                  "y",
                  "w",
                  "h",
                  "solid"
                ],
                "properties": {
                  "id": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "description": "Names the prop so a quest riddle can cite it. Unique on the map.",
                    "type": "string"
                  },
                  "sprite": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                    "maxLength": 120,
                    "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                    "type": "string"
                  },
                  "x": {
                    "minimum": 0,
                    "maximum": 1023,
                    "description": "Left tile of the footprint.",
                    "type": "integer"
                  },
                  "y": {
                    "minimum": 0,
                    "maximum": 1023,
                    "description": "Top tile of the footprint.",
                    "type": "integer"
                  },
                  "w": {
                    "minimum": 1,
                    "maximum": 8,
                    "description": "Footprint width in tiles. The sprite is drawn bottom-centered on it.",
                    "type": "integer"
                  },
                  "h": {
                    "minimum": 1,
                    "maximum": 8,
                    "description": "Footprint height in tiles.",
                    "type": "integer"
                  },
                  "solid": {
                    "type": "boolean"
                  },
                  "cover": {
                    "description": "Hides a player standing on this non-solid prop from a predator's eyes, but never from an adjacent predator.",
                    "type": "boolean"
                  },
                  "fire": {
                    "description": "A fire the player may feed by hand. It needs an id.",
                    "const": true,
                    "type": "boolean"
                  },
                  "crossing": {
                    "description": "Steps or a ramp: the way across a change of level (G68), on the lower tile, facing the face.",
                    "const": true,
                    "type": "boolean"
                  },
                  "lit_by": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Id of the fire prop while whose fire burns this prop's note and sign may be read.",
                    "type": "string"
                  },
                  "sign": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Makes the prop a readable sign that names whoever filled this slot, or says it awaits restoration.",
                    "type": "string"
                  },
                  "note": {
                    "minLength": 1,
                    "maxLength": 240,
                    "type": "string",
                    "description": "A line shown when the player looks at this prop, for example an old inscription."
                  },
                  "hazard": {
                    "additionalProperties": false,
                    "description": "Makes the prop a hazard (G30-G35). It needs an id.",
                    "type": "object",
                    "required": [
                      "kind",
                      "window"
                    ],
                    "properties": {
                      "kind": {
                        "description": "A predator hunts the player in its reach and bites until the player stands somewhere safe; a pit, a trap or deep water hurts once each time the player steps onto its footprint.",
                        "anyOf": [
                          {
                            "const": "predator",
                            "type": "string"
                          },
                          {
                            "const": "pit",
                            "type": "string"
                          },
                          {
                            "const": "trap",
                            "type": "string"
                          },
                          {
                            "const": "deep_water",
                            "type": "string"
                          }
                        ]
                      },
                      "window": {
                        "minItems": 1,
                        "uniqueItems": true,
                        "description": "When it acts (G30). Outside its window it is harmless and shows.",
                        "type": "array",
                        "items": {
                          "const": "night",
                          "type": "string"
                        }
                      },
                      "reach": {
                        "minimum": 1,
                        "maximum": 12,
                        "description": "A predator only: how many tiles from its footprint it notices the player. The params hold the default.",
                        "type": "integer"
                      },
                      "round": {
                        "minItems": 4,
                        "maxItems": 64,
                        "description": "A predator only: the closed, four-adjacent loop of tiles it walks after dark. The first tile is inside its prop footprint.",
                        "type": "array",
                        "items": {
                          "type": "array",
                          "items": [
                            {
                              "minimum": 0,
                              "maximum": 1023,
                              "type": "integer"
                            },
                            {
                              "minimum": 0,
                              "maximum": 1023,
                              "type": "integer"
                            }
                          ],
                          "additionalItems": false,
                          "minItems": 2,
                          "maxItems": 2
                        }
                      },
                      "pace_ms": {
                        "minimum": 100,
                        "maximum": 10000,
                        "description": "A predator on a round only: game milliseconds per tile. The params hold the default.",
                        "type": "integer"
                      },
                      "eyes": {
                        "minimum": 1,
                        "maximum": 12,
                        "description": "A predator on a round only: how many tiles ahead it sees. The params hold the default.",
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            },
            "spawn": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "x",
                "y"
              ],
              "properties": {
                "x": {
                  "minimum": 0,
                  "maximum": 1023,
                  "type": "integer"
                },
                "y": {
                  "minimum": 0,
                  "maximum": 1023,
                  "type": "integer"
                }
              }
            },
            "world": {
              "pattern": "^[a-z0-9_-]+$",
              "minLength": 1,
              "maxLength": 40,
              "type": "string",
              "description": "The world this map belongs to, for a race's grade. Left out: \"valley\", neutral to every race."
            },
            "regions": {
              "maxItems": 32,
              "description": "Named rectangles of the map. A tile in none is the map's own region. The first region that holds a tile wins.",
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "id",
                  "x",
                  "y",
                  "w",
                  "h"
                ],
                "properties": {
                  "id": {
                    "pattern": "^[a-z0-9_-]+$",
                    "minLength": 1,
                    "maxLength": 40,
                    "type": "string",
                    "description": "The region's name, unique on the map. Its weather is drawn per region."
                  },
                  "x": {
                    "minimum": 0,
                    "maximum": 1023,
                    "type": "integer"
                  },
                  "y": {
                    "minimum": 0,
                    "maximum": 1023,
                    "type": "integer"
                  },
                  "w": {
                    "minimum": 1,
                    "maximum": 1024,
                    "type": "integer"
                  },
                  "h": {
                    "minimum": 1,
                    "maximum": 1024,
                    "type": "integer"
                  },
                  "world": {
                    "pattern": "^[a-z0-9_-]+$",
                    "minLength": 1,
                    "maxLength": 40,
                    "type": "string",
                    "description": "The world the region belongs to. Left out: the map's world."
                  }
                }
              }
            },
            "slots": {
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "id",
                  "kind",
                  "x",
                  "y",
                  "w",
                  "h"
                ],
                "properties": {
                  "id": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "kind": {
                    "anyOf": [
                      {
                        "const": "resource",
                        "type": "string"
                      },
                      {
                        "const": "ruin",
                        "type": "string"
                      },
                      {
                        "const": "npc",
                        "type": "string"
                      },
                      {
                        "const": "building",
                        "type": "string"
                      },
                      {
                        "const": "restore",
                        "type": "string"
                      },
                      {
                        "const": "camp",
                        "type": "string"
                      },
                      {
                        "const": "area",
                        "type": "string"
                      }
                    ]
                  },
                  "x": {
                    "minimum": 0,
                    "maximum": 1023,
                    "type": "integer"
                  },
                  "y": {
                    "minimum": 0,
                    "maximum": 1023,
                    "type": "integer"
                  },
                  "w": {
                    "minimum": 1,
                    "maximum": 64,
                    "type": "integer"
                  },
                  "h": {
                    "minimum": 1,
                    "maximum": 64,
                    "type": "integer"
                  },
                  "tag": {
                    "anyOf": [
                      {
                        "const": "grove",
                        "type": "string"
                      },
                      {
                        "const": "meadow",
                        "type": "string"
                      },
                      {
                        "const": "riverbank",
                        "type": "string"
                      },
                      {
                        "const": "ruin",
                        "type": "string"
                      },
                      {
                        "const": "town",
                        "type": "string"
                      },
                      {
                        "const": "water",
                        "type": "string"
                      },
                      {
                        "const": "path",
                        "type": "string"
                      },
                      {
                        "const": "interior",
                        "type": "string"
                      }
                    ]
                  },
                  "label": {
                    "minLength": 1,
                    "maxLength": 40,
                    "type": "string"
                  },
                  "safe": {
                    "description": "A safe area (G30): no hazard acts inside it at any hour, a chase ends when the player stands in it, and what the player carries in is theirs. The cobbles of the town and the camp once a part stands are safe without this.",
                    "type": "boolean"
                  }
                }
              }
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "pocket",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "pocket"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "pocket",
          "type": "string"
        },
        "pocket": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "name",
            "wake",
            "ground"
          ],
          "properties": {
            "name": {
              "minLength": 1,
              "maxLength": 40,
              "type": "string"
            },
            "wake": {
              "additionalProperties": false,
              "description": "The tile the character wakes on.",
              "type": "object",
              "required": [
                "x",
                "y"
              ],
              "properties": {
                "x": {
                  "minimum": 0,
                  "maximum": 1023,
                  "type": "integer"
                },
                "y": {
                  "minimum": 0,
                  "maximum": 1023,
                  "type": "integer"
                }
              }
            },
            "pocket": {
              "minimum": 1,
              "maximum": 32,
              "description": "Tiles of the mist's clear pocket around the wake tile (B2, NEXT 16). Left out: 5. The kernel does not read it; the client draws it.",
              "type": "integer"
            },
            "pocket_grown": {
              "minimum": 1,
              "maximum": 32,
              "description": "Tiles of that pocket once the pocket's first task is done. Left out: 7. Never less than `pocket`.",
              "type": "integer"
            },
            "intro": {
              "minItems": 1,
              "maxItems": 4,
              "description": "What the narrator says as the character wakes, one box a line. Left out: the game's own waking lines.",
              "type": "array",
              "items": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            },
            "ground": {
              "minItems": 1,
              "maxItems": 3,
              "description": "What lies on the ground by the wake tile: each stack is given by one of the pocket's own spots (a treasure whose `pocket` names this pocket, picked up by hand). A reward's `instead` reads it.",
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "item",
                  "qty"
                ],
                "properties": {
                  "item": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "qty": {
                    "minimum": 1,
                    "maximum": 999,
                    "type": "integer"
                  }
                }
              }
            }
          }
        }
      }
    },
    {
      "additionalProperties": false,
      "$id": "school",
      "type": "object",
      "required": [
        "format",
        "id",
        "title",
        "creator",
        "type",
        "school"
      ],
      "properties": {
        "$schema": {
          "type": "string"
        },
        "format": {
          "description": "Format version of this file.",
          "const": 1,
          "type": "number"
        },
        "id": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "title": {
          "minLength": 1,
          "maxLength": 40,
          "type": "string"
        },
        "creator": {
          "pattern": "^[a-z0-9_-]+$",
          "maxLength": 39,
          "description": "GitHub handle, lowercase.",
          "type": "string"
        },
        "description": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "ai_tools": {
          "maxItems": 8,
          "description": "AI tools used to make this file or its art. Required when any were used.",
          "type": "array",
          "items": {
            "minLength": 1,
            "maxLength": 60,
            "type": "string"
          }
        },
        "items": {
          "maxItems": 40,
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "name",
              "kind",
              "rarity",
              "sprite",
              "weight"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "material",
                    "type": "string"
                  },
                  {
                    "const": "food",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "fragment",
                    "type": "string"
                  },
                  {
                    "const": "treasure",
                    "type": "string"
                  }
                ]
              },
              "rarity": {
                "description": "Declared rarity. The check compares it with the rarity computed from the data.",
                "anyOf": [
                  {
                    "const": "common",
                    "type": "string"
                  },
                  {
                    "const": "uncommon",
                    "type": "string"
                  },
                  {
                    "const": "rare",
                    "type": "string"
                  },
                  {
                    "const": "legendary",
                    "type": "string"
                  }
                ]
              },
              "class": {
                "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  },
                  {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                ]
              },
              "family": {
                "pattern": "^[a-z0-9_-]+$",
                "maxLength": 24,
                "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
                "type": "string"
              },
              "tags": {
                "maxItems": 8,
                "uniqueItems": true,
                "description": "Open words that describe it, for example \"sweet\" or \"night\".",
                "type": "array",
                "items": {
                  "pattern": "^[a-z0-9_-]+$",
                  "maxLength": 24,
                  "description": "A lowercase word.",
                  "type": "string"
                }
              },
              "tier": {
                "minimum": 1,
                "maximum": 20,
                "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
                "type": "integer"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "weight": {
                "minimum": 0,
                "maximum": 20,
                "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
                "type": "integer"
              },
              "food": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "satiety"
                ],
                "properties": {
                  "satiety": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                    "type": "integer"
                  },
                  "hurt": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                    "type": "integer"
                  }
                }
              },
              "tool": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "tier"
                ],
                "properties": {
                  "kind": {
                    "anyOf": [
                      {
                        "const": "basket",
                        "type": "string"
                      },
                      {
                        "const": "trowel",
                        "type": "string"
                      },
                      {
                        "const": "axe",
                        "type": "string"
                      },
                      {
                        "const": "staff",
                        "type": "string"
                      },
                      {
                        "const": "hammer",
                        "type": "string"
                      },
                      {
                        "const": "pick",
                        "type": "string"
                      },
                      {
                        "const": "rod",
                        "type": "string"
                      }
                    ]
                  },
                  "tier": {
                    "minimum": 1,
                    "maximum": 5,
                    "description": "Better tools work faster. The calculator clamps the total.",
                    "type": "integer"
                  },
                  "uses": {
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                    "type": "integer"
                  }
                }
              },
              "fragment": {
                "additionalProperties": false,
                "description": "A found scrap that teaches a recipe.",
                "type": "object",
                "required": [
                  "unlocks"
                ],
                "properties": {
                  "unlocks": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              "wear": {
                "additionalProperties": false,
                "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
                "type": "object",
                "required": [
                  "slot"
                ],
                "properties": {
                  "slot": {
                    "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                    "anyOf": [
                      {
                        "const": "head",
                        "type": "string"
                      },
                      {
                        "const": "top",
                        "type": "string"
                      },
                      {
                        "const": "legs",
                        "type": "string"
                      },
                      {
                        "const": "outfit",
                        "type": "string"
                      },
                      {
                        "const": "feet",
                        "type": "string"
                      },
                      {
                        "const": "jewelry",
                        "type": "string"
                      },
                      {
                        "const": "hand",
                        "type": "string"
                      },
                      {
                        "const": "back",
                        "type": "string"
                      }
                    ]
                  },
                  "look": {
                    "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                    "type": "boolean"
                  },
                  "effects": {
                    "minItems": 1,
                    "maxItems": 2,
                    "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                    "type": "array",
                    "items": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "effect",
                        "value"
                      ],
                      "properties": {
                        "effect": {
                          "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                          "anyOf": [
                            {
                              "const": "gather_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "dig_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_speed_pct",
                              "type": "string"
                            },
                            {
                              "const": "craft_chance_pct",
                              "type": "string"
                            },
                            {
                              "const": "rare_find_pct",
                              "type": "string"
                            }
                          ]
                        },
                        "value": {
                          "minimum": 1,
                          "maximum": 100,
                          "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "set": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                    "type": "string"
                  },
                  "places": {
                    "minimum": 1,
                    "maximum": 99,
                    "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                    "type": "integer"
                  },
                  "load_bonus": {
                    "minimum": 1,
                    "maximum": 100,
                    "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                    "type": "integer"
                  }
                }
              },
              "grade": {
                "additionalProperties": false,
                "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
                "type": "object",
                "required": [
                  "of",
                  "step"
                ],
                "properties": {
                  "of": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "step": {
                    "minimum": 2,
                    "maximum": 3,
                    "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                    "type": "integer"
                  }
                }
              },
              "premium_later": {
                "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
                "type": "boolean"
              }
            }
          }
        },
        "classes": {
          "maxItems": 10,
          "description": "Classes of your own, each under one base class.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
            "type": "object",
            "required": [
              "id",
              "name",
              "base"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "name": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "base": {
                "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
                "anyOf": [
                  {
                    "const": "plant",
                    "type": "string"
                  },
                  {
                    "const": "wood",
                    "type": "string"
                  },
                  {
                    "const": "fibre",
                    "type": "string"
                  },
                  {
                    "const": "stone",
                    "type": "string"
                  },
                  {
                    "const": "clay",
                    "type": "string"
                  },
                  {
                    "const": "mineral",
                    "type": "string"
                  },
                  {
                    "const": "metal",
                    "type": "string"
                  },
                  {
                    "const": "animal",
                    "type": "string"
                  },
                  {
                    "const": "meal",
                    "type": "string"
                  },
                  {
                    "const": "tool",
                    "type": "string"
                  },
                  {
                    "const": "garment",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "relic",
                    "type": "string"
                  }
                ]
              },
              "description": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            }
          }
        },
        "type": {
          "const": "school",
          "type": "string"
        },
        "school": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "name",
            "profession",
            "nodes"
          ],
          "properties": {
            "name": {
              "minLength": 1,
              "maxLength": 40,
              "type": "string"
            },
            "profession": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
              "maxLength": 80,
              "description": "The profession whose wing this is. The profession keeps its levels; the school is what the points buy.",
              "type": "string"
            },
            "nodes": {
              "minItems": 1,
              "maxItems": 400,
              "description": "The paths and the corners. At most `growth.nodes_max` nodes for each race (the paths and that race's corner).",
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "id",
                  "name",
                  "text",
                  "size",
                  "effect",
                  "value"
                ],
                "properties": {
                  "id": {
                    "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
                    "maxLength": 40,
                    "description": "The node's name inside its school, for example \"steady\". A learned node is kept as \"<school id>/<node id>\".",
                    "type": "string"
                  },
                  "name": {
                    "minLength": 1,
                    "maxLength": 40,
                    "type": "string",
                    "description": "A thing the player knows how to do, for example \"Depth sense\"."
                  },
                  "text": {
                    "minLength": 1,
                    "maxLength": 240,
                    "type": "string",
                    "description": "One sentence in words, never a number or a percentage (the number stays in the code)."
                  },
                  "size": {
                    "anyOf": [
                      {
                        "const": "small",
                        "type": "string"
                      },
                      {
                        "const": "major",
                        "type": "string"
                      },
                      {
                        "const": "corner",
                        "type": "string"
                      },
                      {
                        "const": "keystone",
                        "type": "string"
                      }
                    ]
                  },
                  "needs": {
                    "maxItems": 3,
                    "uniqueItems": true,
                    "description": "The nodes learned before this one, all of them: its place on the path. Most are of the same school; a node of another school joins the two wings.",
                    "type": "array",
                    "items": {
                      "pattern": "^([a-z0-9]+(-[a-z0-9]+)*/[a-z0-9]+(-[a-z0-9]+)*/)?[a-z0-9]+(-[a-z0-9]+)*$",
                      "maxLength": 120,
                      "description": "A node of the same school by its id (\"steady\"), or of another school by \"<school id>/<node id>\" (\"core/digging-school/steady\").",
                      "type": "string"
                    }
                  },
                  "cost": {
                    "minimum": 1,
                    "maximum": 20,
                    "description": "Its price in skill points, inside the band of its size (params growth.cost, min to max). Left out: the size's price.",
                    "type": "integer"
                  },
                  "key": {
                    "additionalProperties": false,
                    "description": "Keys: the attribute pips the node asks for, for example { \"senses\": 3 }. A key opens the node; it is never spent.",
                    "type": "object",
                    "properties": {
                      "might": {
                        "minimum": 1,
                        "maximum": 10,
                        "type": "integer"
                      },
                      "senses": {
                        "minimum": 1,
                        "maximum": 10,
                        "type": "integer"
                      },
                      "agility": {
                        "minimum": 1,
                        "maximum": 10,
                        "type": "integer"
                      },
                      "luck": {
                        "minimum": 1,
                        "maximum": 10,
                        "type": "integer"
                      },
                      "stamina": {
                        "minimum": 1,
                        "maximum": 10,
                        "type": "integer"
                      },
                      "spirit": {
                        "minimum": 1,
                        "maximum": 10,
                        "type": "integer"
                      }
                    }
                  },
                  "effect": {
                    "description": "What a technique does. A closed list: the kernel knows how to apply each one. A craft effect of a technique (craft_speed_pct, craft_chance_pct) helps the recipes of its own profession.",
                    "anyOf": [
                      {
                        "const": "see_ripe",
                        "type": "string"
                      },
                      {
                        "const": "timing_window_pct",
                        "type": "string"
                      },
                      {
                        "const": "second_hint",
                        "type": "string"
                      },
                      {
                        "const": "rare_find_pct",
                        "type": "string"
                      },
                      {
                        "const": "craft_speed_pct",
                        "type": "string"
                      },
                      {
                        "const": "meal_fill_pct",
                        "type": "string"
                      },
                      {
                        "const": "gather_speed_pct",
                        "type": "string"
                      },
                      {
                        "const": "dig_speed_pct",
                        "type": "string"
                      },
                      {
                        "const": "craft_chance_pct",
                        "type": "string"
                      }
                    ]
                  },
                  "value": {
                    "minimum": 0,
                    "maximum": 100,
                    "description": "The number the effect moves. Shown only on a long press, never on the tree.",
                    "type": "integer"
                  },
                  "slot": {
                    "description": "A technique: it works only while it sits in an active slot of the build. Left out: always on once learned. Never on a small node.",
                    "type": "boolean"
                  },
                  "race": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A corner node only: the race whose node this is. Only a player of that race sees and learns it.",
                    "type": "string"
                  },
                  "icon": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "An item whose sprite pictures the node on the tree.",
                    "type": "string"
                  },
                  "cluster": {
                    "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
                    "maxLength": 40,
                    "description": "The cluster of its school it belongs to (the school's `clusters`): nodes that stand together.",
                    "type": "string"
                  },
                  "pos": {
                    "additionalProperties": false,
                    "description": "Where it stands on the tree, in steps from the trunk (x to the right, y down). A school gives every node a place or none; with none, the screen lays the school out from its paths.",
                    "type": "object",
                    "required": [
                      "x",
                      "y"
                    ],
                    "properties": {
                      "x": {
                        "minimum": -400,
                        "maximum": 400,
                        "type": "integer"
                      },
                      "y": {
                        "minimum": -400,
                        "maximum": 400,
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            },
            "tint": {
              "description": "The colour family of the core palette the wing is drawn in. Left out: wood.",
              "anyOf": [
                {
                  "const": "green",
                  "type": "string"
                },
                {
                  "const": "wood",
                  "type": "string"
                },
                {
                  "const": "skin",
                  "type": "string"
                },
                {
                  "const": "red",
                  "type": "string"
                },
                {
                  "const": "water",
                  "type": "string"
                },
                {
                  "const": "plum",
                  "type": "string"
                },
                {
                  "const": "fire",
                  "type": "string"
                },
                {
                  "const": "grey",
                  "type": "string"
                }
              ]
            },
            "angle": {
              "minimum": 0,
              "maximum": 359,
              "description": "The direction the wing grows from the trunk, in degrees clockwise from the right. Left out: the wings share the circle in the order of their ids.",
              "type": "integer"
            },
            "clusters": {
              "maxItems": 40,
              "description": "Named groups of the school's nodes, for example \"the trowel\". A node names its cluster.",
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "id",
                  "name"
                ],
                "properties": {
                  "id": {
                    "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
                    "maxLength": 40,
                    "description": "The node's name inside its school, for example \"steady\". A learned node is kept as \"<school id>/<node id>\".",
                    "type": "string"
                  },
                  "name": {
                    "minLength": 1,
                    "maxLength": 40,
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    }
  ],
  "$id": "https://github.com/Bobikobi/GAME/schema/json/extension.schema.json"
}
