{
  "$schema": "https://sous.pras.ai/schema/v0/domain.schema.json",
  "vocabulary": "operations",
  "version": "0.7.0",
  "description": "Closed vocabulary of cooking operations, each defined as a typed state transformer over the facets in vocab/state-facets.json. This file IS the PDDL domain: preconditions and effects translate directly to action schemas. See ADR 0003.",
  "condition_language": {
    "single_facet_precondition": {
      "is": "exact value",
      "in": "one of a list",
      "min": "rank >= rank(value); ordered facets only",
      "max": "rank <= rank(value); ordered facets only",
      "any": "facet may hold any value (documents that the op is insensitive to it)"
    },
    "multi_facet_precondition": {
      "has": "all listed values present",
      "lacks": "none of the listed values present"
    },
    "single_facet_effect": {
      "set": "assign value",
      "at_least": "raise to rank(value) if below; ordered facets only",
      "from_param": "value taken from the named operation parameter"
    },
    "multi_facet_effect": {
      "add": "add listed values",
      "remove": "remove listed values"
    }
  },
  "operations": {
    "cut": {
      "label": "Cut",
      "core": "cutting",
      "definition": "Reduce a material to a target form by knife or blade. Parameterised by target form rather than split into slice/dice/mince, mirroring the target-state action pattern in the PDDL cooking-robot literature.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          2
        ]
      },
      "params": {
        "form": {
          "type": "facet_value",
          "facet": "form",
          "required": true,
          "allowed": [
            "halved",
            "quartered",
            "chunked",
            "sliced",
            "julienned",
            "shredded",
            "chopped",
            "cubed",
            "diced",
            "minced"
          ]
        },
        "dimension": {
          "type": "quantity",
          "dimension": "length",
          "required": false,
          "note": "Characteristic size; overrides the facet's typical_dimension_mm."
        }
      },
      "preconditions": {
        "material": {
          "form": {
            "max": "chunked"
          },
          "affordances": {
            "has": [
              "cuttable"
            ]
          }
        },
        "equipment": {
          "class": "cutting_surface",
          "cleanliness": {
            "max": "soiled"
          }
        }
      },
      "effects": {
        "material": {
          "form": {
            "from_param": "form"
          }
        }
      },
      "produces_byproduct": {
        "optional": true,
        "label": "trimmings",
        "note": "Second output captures offcuts when the recipe reserves them."
      },
      "duration": {
        "typical_s": 120,
        "bound_s": 900,
        "scales_with": "input_mass"
      },
      "yield_factor": 1.0,
      "observables": [
        "form",
        "dimension"
      ],
      "hazards": [
        "blade"
      ],
      "removes_removal_affordances": {
        "above_form_rank": 2,
        "note": "You cannot peel diced onion. Gated on the TARGET form rank: variants targeting chunked (rank 3) or finer remove every removal_type affordance; halved and quartered do not, which preserves the halve-then-peel technique."
      },
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "chop",
        "chopping",
        "cube",
        "dice",
        "dicing",
        "halve",
        "julienne",
        "julienning",
        "mince",
        "mincing",
        "quarter",
        "slice",
        "slicing"
      ]
    },
    "grate": {
      "label": "Grate",
      "core": "cutting",
      "definition": "Reduce against a grating surface.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "coarseness": {
          "type": "enum",
          "values": [
            "fine",
            "medium",
            "coarse"
          ],
          "default": "medium"
        }
      },
      "preconditions": {
        "material": {
          "form": {
            "max": "chunked"
          },
          "texture": {
            "max": "tender"
          },
          "affordances": {
            "has": [
              "cuttable"
            ]
          }
        },
        "equipment": {
          "class": "grater"
        }
      },
      "effects": {
        "material": {
          "form": {
            "set": "grated"
          }
        }
      },
      "duration": {
        "typical_s": 90,
        "bound_s": 600,
        "scales_with": "input_mass"
      },
      "yield_factor": 0.95,
      "observables": [
        "form"
      ],
      "hazards": [
        "blade"
      ],
      "removes_removal_affordances": {
        "above_form_rank": 2,
        "note": "You cannot peel diced onion. Gated on the TARGET form rank: variants targeting chunked (rank 3) or finer remove every removal_type affordance; halved and quartered do not, which preserves the halve-then-peel technique."
      },
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "shred",
        "shredding",
        "zesting"
      ]
    },
    "crush": {
      "label": "Crush",
      "core": "cutting",
      "definition": "Reduce by compression rather than cutting. Covers smashing garlic, crushing tomatoes, mashing potatoes.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "form": {
          "type": "facet_value",
          "facet": "form",
          "default": "mashed",
          "allowed": [
            "chunked",
            "mashed",
            "pureed"
          ]
        }
      },
      "preconditions": {
        "material": {
          "texture": {
            "min": "al_dente"
          },
          "affordances": {
            "has": [
              "cuttable"
            ]
          }
        }
      },
      "effects": {
        "material": {
          "form": {
            "from_param": "form"
          }
        }
      },
      "duration": {
        "typical_s": 60,
        "bound_s": 600
      },
      "yield_factor": 1.0,
      "observables": [
        "form"
      ],
      "removes_removal_affordances": {
        "above_form_rank": 2,
        "note": "You cannot peel diced onion. Gated on the TARGET form rank: variants targeting chunked (rank 3) or finer remove every removal_type affordance; halved and quartered do not, which preserves the halve-then-peel technique."
      },
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "bruising",
        "pounding"
      ]
    },
    "grind": {
      "label": "Grind",
      "core": "cutting",
      "definition": "Mechanically reduce to a coarse particulate. Spices, coffee, meat.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "coarseness": {
          "type": "enum",
          "values": [
            "fine",
            "medium",
            "coarse"
          ],
          "default": "medium"
        }
      },
      "preconditions": {
        "material": {
          "form": {
            "max": "diced"
          },
          "affordances": {
            "has": [
              "cuttable"
            ]
          }
        },
        "equipment": {
          "class": "grinder"
        }
      },
      "effects": {
        "material": {
          "form": {
            "set": "ground"
          }
        }
      },
      "duration": {
        "typical_s": 60,
        "bound_s": 600
      },
      "yield_factor": 0.97,
      "observables": [
        "form"
      ],
      "removes_removal_affordances": {
        "above_form_rank": 2,
        "note": "You cannot peel diced onion. Gated on the TARGET form rank: variants targeting chunked (rank 3) or finer remove every removal_type affordance; halved and quartered do not, which preserves the halve-then-peel technique."
      },
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside."
    },
    "puree": {
      "label": "Puree",
      "core": "cutting",
      "definition": "Reduce to a smooth mass. Distinct from `blend` in that it takes a single input.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "material": {
          "texture": {
            "min": "tender"
          },
          "affordances": {
            "has": [
              "cuttable"
            ]
          }
        },
        "equipment": {
          "class": "blender"
        }
      },
      "effects": {
        "material": {
          "form": {
            "set": "pureed"
          },
          "structure": {
            "add": [
              "homogeneous"
            ]
          }
        }
      },
      "duration": {
        "typical_s": 60,
        "bound_s": 300
      },
      "yield_factor": 0.98,
      "observables": [
        "form",
        "texture"
      ],
      "hazards": [
        "blade",
        "hot_splash"
      ],
      "removes_removal_affordances": {
        "above_form_rank": 2,
        "note": "You cannot peel diced onion. Gated on the TARGET form rank: variants targeting chunked (rank 3) or finer remove every removal_type affordance; halved and quartered do not, which preserves the halve-then-peel technique."
      },
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "blending to a puree",
        "pureeing"
      ]
    },
    "blend": {
      "label": "Blend",
      "core": "mixing",
      "definition": "Combine two or more materials while reducing them to a smooth mass.",
      "arity": {
        "consumes": [
          2,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "equipment": {
          "class": "blender"
        },
        "material": {
          "affordances": {
            "has": [
              "cuttable"
            ]
          }
        },
        "applies_to": "any"
      },
      "effects": {
        "material": {
          "form": {
            "set": "pureed"
          },
          "structure": {
            "add": [
              "homogeneous"
            ]
          }
        }
      },
      "duration": {
        "typical_s": 90,
        "bound_s": 300
      },
      "yield_factor": 0.98,
      "observables": [
        "form",
        "texture"
      ],
      "hazards": [
        "blade",
        "hot_splash"
      ],
      "removes_removal_affordances": {
        "above_form_rank": 2,
        "note": "You cannot peel diced onion. Gated on the TARGET form rank: variants targeting chunked (rank 3) or finer remove every removal_type affordance; halved and quartered do not, which preserves the halve-then-peel technique."
      },
      "precondition_note": "At least one input must be worth reducing; blending fruit with water does not require the water to be cuttable.",
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside."
    },
    "peel": {
      "label": "Peel",
      "core": "preparing",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          2
        ]
      },
      "preconditions": {
        "material": {
          "affordances": {
            "has": [
              "peelable"
            ]
          }
        }
      },
      "effects": {
        "material": {
          "affordances": {
            "remove": [
              "peelable"
            ],
            "add": [
              "cuttable"
            ]
          }
        },
        "substance": {
          "via": "peel"
        }
      },
      "produces_byproduct": {
        "optional": true,
        "label": "peel",
        "note": "Reserved for zest, stock or garnish."
      },
      "duration": {
        "typical_s": 120,
        "bound_s": 900,
        "scales_with": "input_count"
      },
      "yield_factor": 0.8,
      "yield_note": "Highly ingredient-specific; the substance entry's ap_to_ep factor overrides this default.",
      "observables": [
        "affordances"
      ],
      "hazards": [
        "blade"
      ],
      "effect_note": "Removing `peelable` and adding `cuttable` is what makes peel-before-cut the only reachable plan. It is not a special prep rule -- `cut` simply requires `cuttable`, which a raw onion does not have.",
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside."
    },
    "trim": {
      "label": "Trim",
      "core": "preparing",
      "definition": "Remove unwanted parts. Parameterised by which affordance it clears, so one operation covers shelling, coring, deseeding, deboning and the rest.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          2
        ]
      },
      "params": {
        "clears": {
          "type": "facet_value_list",
          "facet": "affordances",
          "required": true,
          "allowed": [
            "trimmable",
            "corable",
            "deseedable",
            "destemmable",
            "deboneable",
            "shellable",
            "huskable",
            "descaleable",
            "guttable",
            "skinnable",
            "deveinable",
            "juiceable",
            "zestable"
          ],
          "note": "Which affordance this trim clears. The `cleared_by` / `grants` metadata in state-facets.json drives the effect, so there is no per-operation table."
        }
      },
      "preconditions": {
        "material": {
          "affordances": {
            "has": {
              "from_param": "clears"
            }
          }
        }
      },
      "effects": {
        "material": {
          "affordances": {
            "remove": {
              "from_param": "clears"
            },
            "add": {
              "from_grants": "clears"
            }
          }
        },
        "substance": {
          "via": "trim"
        }
      },
      "produces_byproduct": {
        "optional": true,
        "label": "trimmings",
        "note": "Bones and shells are commonly reserved for stock."
      },
      "duration": {
        "typical_s": 180,
        "bound_s": 1800
      },
      "yield_factor": 0.85,
      "observables": [
        "affordances"
      ],
      "hazards": [
        "blade"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside."
    },
    "sift": {
      "label": "Sift",
      "core": "preparing",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "material": {
          "form": {
            "min": "ground"
          },
          "hydration": {
            "max": "dry"
          },
          "affordances": {
            "has": [
              "siftable"
            ]
          }
        },
        "equipment": {
          "class": "sieve"
        }
      },
      "effects": {
        "material": {
          "structure": {
            "add": [
              "aerated"
            ]
          },
          "affordances": {
            "remove": [
              "siftable"
            ]
          }
        },
        "substance": {
          "via": "sift"
        }
      },
      "duration": {
        "typical_s": 60,
        "bound_s": 300
      },
      "yield_factor": 0.99,
      "observables": [
        "affordances"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside."
    },
    "strain": {
      "label": "Strain",
      "core": "preparing",
      "definition": "Separate solids from liquid. Two outputs: the retained solids and the passed liquid. Which is the wanted product is a recipe decision, so both are named.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          2,
          2
        ]
      },
      "preconditions": {
        "material": {
          "hydration": {
            "min": "wet"
          },
          "affordances": {
            "has": [
              "strainable"
            ]
          }
        },
        "equipment": {
          "class": "sieve"
        }
      },
      "effects": {
        "material": {
          "affordances": {
            "remove": [
              "strainable"
            ]
          }
        },
        "outputs": [
          {
            "role": "solids",
            "hydration": {
              "set": "drained"
            }
          },
          {
            "role": "liquid",
            "form": {
              "set": "liquid"
            }
          }
        ],
        "substance": {
          "via": "strain"
        }
      },
      "duration": {
        "typical_s": 120,
        "bound_s": 1800
      },
      "observables": [
        "hydration"
      ],
      "hazards": [
        "hot_liquid"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "sieving",
        "sifting through a sieve"
      ]
    },
    "drain": {
      "label": "Drain",
      "core": "preparing",
      "definition": "Remove free liquid, discarding it. Use `strain` when the liquid is kept.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "material": {
          "hydration": {
            "min": "wet"
          }
        }
      },
      "effects": {
        "material": {
          "hydration": {
            "set": "drained"
          }
        }
      },
      "duration": {
        "typical_s": 60,
        "bound_s": 600
      },
      "observables": [
        "hydration"
      ],
      "hazards": [
        "hot_liquid"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside."
    },
    "skim": {
      "label": "Skim",
      "core": "preparing",
      "definition": "Remove material from the surface of a liquid. Covers 'remove surface scum as required'.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          2
        ]
      },
      "preconditions": {
        "material": {
          "hydration": {
            "min": "wet"
          }
        }
      },
      "effects": {
        "material": {}
      },
      "produces_byproduct": {
        "optional": true,
        "label": "skimmings"
      },
      "repeatable": true,
      "duration": {
        "typical_s": 30,
        "bound_s": 120
      },
      "observables": [
        "surface_scum"
      ],
      "hazards": [
        "hot_liquid"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside."
    },
    "portion": {
      "label": "Portion",
      "core": "preparing",
      "definition": "Divide one material lot into several. This is how a 'divided' ingredient is expressed: one can of tomatoes portioned into two lots used in different tasks.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          2,
          null
        ]
      },
      "params": {
        "split": {
          "type": "split_spec",
          "required": true,
          "note": "Either fractions summing to 1.0, or explicit quantities per output. Mass balance is enforced by semantic rule SR-008."
        }
      },
      "preconditions": {},
      "effects": {
        "material": {}
      },
      "state_preserving": true,
      "duration": {
        "typical_s": 60,
        "bound_s": 600
      },
      "observables": [
        "mass"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "divide",
        "dividing",
        "portioning"
      ]
    },
    "mix": {
      "label": "Mix",
      "core": "mixing",
      "definition": "Combine materials into a uniform whole.",
      "arity": {
        "consumes": [
          2,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "intensity": {
          "type": "enum",
          "values": [
            "gentle",
            "moderate",
            "vigorous"
          ],
          "default": "moderate"
        },
        "max_strokes": {
          "type": "integer",
          "required": false,
          "note": "Upper bound on mechanical work. Covers 'using as few strokes as possible' where over-mixing develops gluten."
        }
      },
      "preconditions": {
        "equipment": {
          "class": "vessel"
        }
      },
      "effects": {
        "material": {
          "structure": {
            "add": [
              "homogeneous"
            ]
          }
        }
      },
      "duration": {
        "typical_s": 60,
        "bound_s": 600
      },
      "observables": [
        "structure"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "combine",
        "combining",
        "folding in",
        "stir",
        "stirring"
      ]
    },
    "fold": {
      "label": "Fold",
      "core": "mixing",
      "definition": "Combine gently so as to preserve incorporated gas.",
      "arity": {
        "consumes": [
          2,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "equipment": {
          "class": "vessel"
        }
      },
      "effects": {
        "material": {
          "structure": {
            "add": [
              "homogeneous"
            ]
          }
        }
      },
      "preserves": [
        "aerated"
      ],
      "duration": {
        "typical_s": 90,
        "bound_s": 300
      },
      "observables": [
        "structure"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside."
    },
    "whisk": {
      "label": "Whisk",
      "core": "mixing",
      "definition": "Incorporate gas by rapid mechanical action.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "equipment": {
          "class": "vessel"
        }
      },
      "effects": {
        "material": {
          "structure": {
            "add": [
              "aerated",
              "homogeneous"
            ]
          }
        }
      },
      "duration": {
        "typical_s": 180,
        "bound_s": 900
      },
      "observables": [
        "structure",
        "volume"
      ],
      "attended": 1.0,
      "attended_note": "Genuinely exclusive: the failure window is seconds, so a cook who looks away ruins it.",
      "aliases": [
        "beat",
        "beating",
        "whip",
        "whipping"
      ]
    },
    "knead": {
      "label": "Knead",
      "core": "mixing",
      "definition": "Develop gluten by mechanical work.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "material": {
          "hydration": {
            "min": "moist"
          }
        },
        "equipment": {
          "class": "work_surface"
        }
      },
      "effects": {
        "material": {
          "structure": {
            "add": [
              "kneaded",
              "homogeneous"
            ]
          },
          "texture": {
            "set": "firm"
          }
        }
      },
      "duration": {
        "typical_s": 600,
        "bound_s": 1800
      },
      "observables": [
        "structure",
        "texture"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "kneading"
      ]
    },
    "emulsify": {
      "label": "Emulsify",
      "core": "mixing",
      "definition": "Form a stable dispersion of immiscible phases.",
      "arity": {
        "consumes": [
          2,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "equipment": {
          "class": "vessel"
        }
      },
      "effects": {
        "material": {
          "structure": {
            "add": [
              "emulsified",
              "homogeneous"
            ]
          }
        }
      },
      "failure_states": [
        "separated",
        "curdled"
      ],
      "duration": {
        "typical_s": 240,
        "bound_s": 900
      },
      "observables": [
        "structure"
      ],
      "attended": 1.0,
      "attended_note": "Genuinely exclusive: the failure window is seconds, so a cook who looks away ruins it."
    },
    "toss": {
      "label": "Toss",
      "core": "mixing",
      "definition": "Combine by lifting and turning, coating the components lightly.",
      "arity": {
        "consumes": [
          2,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "equipment": {
          "class": "vessel"
        }
      },
      "effects": {
        "material": {
          "surface": {
            "add": [
              "coated"
            ]
          }
        }
      },
      "duration": {
        "typical_s": 30,
        "bound_s": 180
      },
      "observables": [
        "surface"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside."
    },
    "coat": {
      "label": "Coat",
      "core": "mixing",
      "definition": "Apply a covering to the exterior of a material.",
      "arity": {
        "consumes": [
          2,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "coating": {
          "type": "facet_value",
          "facet": "surface",
          "required": true,
          "allowed": [
            "floured",
            "breaded",
            "battered",
            "coated",
            "glazed",
            "oiled",
            "buttered"
          ]
        }
      },
      "preconditions": {},
      "effects": {
        "material": {
          "surface": {
            "add": {
              "from_param": "coating"
            }
          }
        }
      },
      "duration": {
        "typical_s": 120,
        "bound_s": 600
      },
      "observables": [
        "surface"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "battering",
        "breading",
        "dredge",
        "dredging"
      ]
    },
    "season": {
      "label": "Season",
      "core": "mixing",
      "definition": "Apply salt, pepper or spice. Split out from `coat` because it is the canonical site of to-taste quantities and of the assess/adjust feedback loop.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {},
      "effects": {
        "material": {
          "surface": {
            "add": [
              "seasoned"
            ]
          }
        }
      },
      "duration": {
        "typical_s": 20,
        "bound_s": 120
      },
      "observables": [
        "taste"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "salt",
        "sprinkle"
      ]
    },
    "layer": {
      "label": "Layer",
      "core": "pick_and_place",
      "definition": "Assemble materials in discrete strata. Order is significant and is given by the order of `consumes`.",
      "arity": {
        "consumes": [
          2,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "distribution": {
          "type": "enum",
          "values": [
            "even",
            "clustered",
            "scattered"
          ],
          "default": "even"
        }
      },
      "preconditions": {
        "equipment": {
          "class": "vessel"
        }
      },
      "effects": {
        "material": {
          "structure": {
            "add": [
              "layered"
            ]
          }
        }
      },
      "order_significant": true,
      "duration": {
        "typical_s": 180,
        "bound_s": 900
      },
      "observables": [
        "structure"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside."
    },
    "bundle": {
      "label": "Bundle",
      "core": "pick_and_place",
      "definition": "Enclose several materials together with a wrapping so they behave as a single lot and can be removed as one. A bouquet garni tied with string is the canonical case: three herbs go in as one object and come out as one object.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "equipment": {
          "class": "wrapping"
        }
      },
      "effects": {
        "material": {
          "containment": {
            "set": "wrapped"
          }
        }
      },
      "duration": {
        "typical_s": 60,
        "bound_s": 300
      },
      "state_preserving": true,
      "observables": [
        "containment"
      ],
      "note": "Added in response to the Beef Bourguignon example; the corpus demanded it and no existing operation covered it.",
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside."
    },
    "preheat": {
      "label": "Preheat",
      "core": "cook_and_cool",
      "definition": "Bring equipment to a commanded setpoint before use. Operates on equipment only; consumes and produces no material.",
      "arity": {
        "consumes": [
          0,
          0
        ],
        "produces": [
          0,
          0
        ]
      },
      "params": {
        "setpoint": {
          "type": "quantity",
          "dimension": "temperature",
          "required": true
        }
      },
      "preconditions": {
        "equipment": {
          "class": "heat_source",
          "power": {
            "is": "off"
          }
        }
      },
      "effects": {
        "equipment": {
          "power": {
            "set": "at_setpoint"
          },
          "thermal": {
            "set": "very_hot"
          }
        }
      },
      "duration": {
        "typical_s": 900,
        "bound_s": 2400
      },
      "observables": [
        "equipment_temperature"
      ],
      "hazards": [
        "hot_surface"
      ],
      "note": "Its absence can be load-bearing: 'place in a COLD oven' is expressed by omitting preheat and asserting equipment thermal=ambient as a task precondition.",
      "attended": 0.0,
      "aliases": [
        "heat",
        "warm"
      ]
    },
    "sear": {
      "label": "Sear",
      "core": "cook_and_cool",
      "definition": "Brown a surface rapidly at high heat without cooking the interior through.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "heat": {
          "type": "heat_level",
          "default": "high"
        }
      },
      "preconditions": {
        "material": {
          "hydration": {
            "max": "dry"
          },
          "doneness": {
            "max": "par_cooked"
          }
        },
        "equipment": {
          "class": "heated_vessel",
          "thermal": {
            "min": "very_hot"
          }
        }
      },
      "precondition_note": "hydration<=dry is culinary, not pedantic: a wet surface steams and will not brown.",
      "effects": {
        "material": {
          "browning": {
            "at_least": "deep_brown"
          },
          "thermal": {
            "set": "very_hot"
          }
        },
        "equipment": {
          "residue": {
            "add": [
              "fond"
            ]
          },
          "cleanliness": {
            "set": "soiled"
          }
        }
      },
      "effect_note": "The fond deposited on the vessel is why 'add the onions to the same pot' is a culinary instruction. It is consumed by `deglaze`.",
      "batching": {
        "limited_by": "vessel_surface_area",
        "reason": "Crowding drops vessel temperature and the material steams instead of browning."
      },
      "duration": {
        "typical_s": 240,
        "bound_s": 720
      },
      "yield_factor": 0.85,
      "observables": [
        "browning",
        "internal_temperature"
      ],
      "hazards": [
        "hot_surface",
        "spatter"
      ],
      "attended": 1.0,
      "attended_note": "Genuinely exclusive: the failure window is seconds, so a cook who looks away ruins it.",
      "aliases": [
        "brown"
      ]
    },
    "saute": {
      "label": "Saute",
      "core": "cook_and_cool",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "heat": {
          "type": "heat_level",
          "default": "medium_high"
        },
        "agitation": {
          "type": "enum",
          "values": [
            "none",
            "occasional",
            "frequent",
            "constant"
          ],
          "default": "frequent"
        }
      },
      "preconditions": {
        "equipment": {
          "class": "heated_vessel",
          "thermal": {
            "min": "hot"
          }
        }
      },
      "effects": {
        "material": {
          "doneness": {
            "at_least": "cooked_through"
          },
          "browning": {
            "at_least": "golden"
          },
          "thermal": {
            "set": "very_hot"
          },
          "texture": {
            "set": "tender"
          }
        },
        "equipment": {
          "cleanliness": {
            "set": "soiled"
          }
        }
      },
      "batching": {
        "limited_by": "vessel_surface_area"
      },
      "duration": {
        "typical_s": 420,
        "bound_s": 1800
      },
      "yield_factor": 0.75,
      "observables": [
        "browning",
        "texture"
      ],
      "hazards": [
        "hot_surface",
        "spatter"
      ],
      "precondition_note": "Deliberately no form precondition. Sauteing does not require slicing -- whole mushrooms, green beans and cherry tomatoes are all sauteed whole.",
      "attended": 0.25,
      "attended_note": "Derived from the `frequent` default of its own `agitation` parameter (30s every 120s). A task that sets `agitation` overrides it.",
      "attention_level": "frequent",
      "aliases": [
        "pan-frying",
        "sauteing",
        "sauté",
        "sweat",
        "sweating"
      ]
    },
    "fry": {
      "label": "Pan-fry",
      "core": "cook_and_cool",
      "definition": "Cook in a shallow layer of fat.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "heat": {
          "type": "heat_level",
          "default": "medium_high"
        }
      },
      "preconditions": {
        "equipment": {
          "class": "heated_vessel",
          "thermal": {
            "min": "very_hot"
          },
          "residue": {
            "has": [
              "fat"
            ]
          }
        }
      },
      "effects": {
        "material": {
          "doneness": {
            "at_least": "cooked_through"
          },
          "browning": {
            "at_least": "golden_brown"
          },
          "thermal": {
            "set": "very_hot"
          }
        },
        "equipment": {
          "cleanliness": {
            "set": "soiled"
          }
        }
      },
      "batching": {
        "limited_by": "vessel_surface_area"
      },
      "duration": {
        "typical_s": 480,
        "bound_s": 1800
      },
      "yield_factor": 0.8,
      "observables": [
        "browning",
        "internal_temperature"
      ],
      "hazards": [
        "hot_oil",
        "spatter"
      ],
      "attended": 1.0,
      "attended_note": "Genuinely exclusive: the failure window is seconds, so a cook who looks away ruins it."
    },
    "deep_fry": {
      "label": "Deep-fry",
      "core": "cook_and_cool",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "oil_temperature": {
          "type": "quantity",
          "dimension": "temperature",
          "required": true
        }
      },
      "preconditions": {
        "material": {
          "hydration": {
            "max": "drained"
          }
        },
        "equipment": {
          "class": "fryer",
          "thermal": {
            "min": "very_hot"
          }
        }
      },
      "precondition_note": "hydration<=drained is a safety precondition, not a quality one: water entering hot oil boils violently.",
      "effects": {
        "material": {
          "doneness": {
            "at_least": "cooked_through"
          },
          "browning": {
            "at_least": "golden_brown"
          },
          "thermal": {
            "set": "very_hot"
          },
          "hydration": {
            "set": "dry"
          }
        }
      },
      "batching": {
        "limited_by": "oil_volume",
        "reason": "Overloading drops oil temperature and the food absorbs fat instead of crisping."
      },
      "duration": {
        "typical_s": 240,
        "bound_s": 900
      },
      "yield_factor": 0.85,
      "observables": [
        "browning",
        "oil_temperature"
      ],
      "hazards": [
        "hot_oil",
        "spatter",
        "fire"
      ],
      "attended": 1.0,
      "attended_note": "Genuinely exclusive: the failure window is seconds, so a cook who looks away ruins it.",
      "aliases": [
        "deep fat frying",
        "deep frying",
        "deep-fry"
      ]
    },
    "boil": {
      "label": "Boil",
      "core": "cook_and_cool",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "equipment": {
          "class": "heated_vessel"
        }
      },
      "effects": {
        "material": {
          "doneness": {
            "at_least": "cooked_through"
          },
          "thermal": {
            "set": "very_hot"
          },
          "hydration": {
            "set": "submerged"
          },
          "texture": {
            "set": "tender"
          }
        }
      },
      "duration": {
        "typical_s": 600,
        "bound_s": 3600
      },
      "yield_factor": 1.0,
      "observables": [
        "texture",
        "rolling_boil"
      ],
      "hazards": [
        "hot_liquid",
        "steam"
      ],
      "attended": 0.0
    },
    "simmer": {
      "label": "Simmer",
      "core": "cook_and_cool",
      "definition": "Hold below a rolling boil.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "covered": {
          "type": "boolean",
          "default": false
        },
        "agitation": {
          "type": "enum",
          "values": [
            "none",
            "occasional",
            "frequent"
          ],
          "default": "occasional"
        }
      },
      "preconditions": {
        "equipment": {
          "class": "heated_vessel"
        }
      },
      "effects": {
        "material": {
          "doneness": {
            "at_least": "cooked_through"
          },
          "thermal": {
            "set": "hot"
          },
          "texture": {
            "set": "tender"
          }
        }
      },
      "duration": {
        "typical_s": 1800,
        "bound_s": 21600
      },
      "yield_factor": 0.9,
      "observables": [
        "texture",
        "surface_movement"
      ],
      "hazards": [
        "hot_liquid",
        "steam"
      ],
      "attended": 0.05,
      "attended_note": "Derived from the `occasional` default of its own `agitation` parameter (30s every 600s). A task that sets `agitation` overrides it.",
      "attention_level": "occasional"
    },
    "steam": {
      "label": "Steam",
      "core": "cook_and_cool",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "equipment": {
          "class": "steamer"
        }
      },
      "effects": {
        "material": {
          "doneness": {
            "at_least": "cooked_through"
          },
          "thermal": {
            "set": "hot"
          },
          "hydration": {
            "set": "moist"
          },
          "texture": {
            "set": "tender"
          }
        }
      },
      "duration": {
        "typical_s": 600,
        "bound_s": 3600
      },
      "yield_factor": 0.95,
      "observables": [
        "texture"
      ],
      "hazards": [
        "steam"
      ],
      "attended": 0.0
    },
    "poach": {
      "label": "Poach",
      "core": "cook_and_cool",
      "definition": "Cook submerged in liquid held well below boiling.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "equipment": {
          "class": "heated_vessel"
        }
      },
      "effects": {
        "material": {
          "doneness": {
            "at_least": "cooked_through"
          },
          "thermal": {
            "set": "hot"
          },
          "hydration": {
            "set": "submerged"
          },
          "texture": {
            "set": "tender"
          }
        }
      },
      "duration": {
        "typical_s": 600,
        "bound_s": 3600
      },
      "yield_factor": 1.0,
      "observables": [
        "texture",
        "internal_temperature"
      ],
      "hazards": [
        "hot_liquid"
      ],
      "attended": 0.05,
      "attended_note": "Roughly `occasional` attention: 30s every 600s.",
      "attention_level": "occasional",
      "aliases": [
        "poaching"
      ]
    },
    "bake": {
      "label": "Bake / roast",
      "core": "cook_and_cool",
      "definition": "Cook by dry heat in an enclosed cavity. `style: roast` denotes the higher-temperature, fat-basted variant; the physical operation is the same.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "style": {
          "type": "enum",
          "values": [
            "bake",
            "roast"
          ],
          "default": "bake"
        },
        "setpoint": {
          "type": "quantity",
          "dimension": "temperature",
          "required": true
        },
        "convection": {
          "type": "boolean",
          "required": false
        }
      },
      "preconditions": {
        "equipment": {
          "class": "oven"
        }
      },
      "precondition_note": "Deliberately does NOT require power=at_setpoint. Cold-start baking is a real technique ('place in a COLD oven'), so preheating is asserted per-task rather than baked into the operation.",
      "effects": {
        "material": {
          "doneness": {
            "at_least": "cooked_through"
          },
          "browning": {
            "at_least": "golden"
          },
          "thermal": {
            "set": "very_hot"
          },
          "hydration": {
            "set": "dry"
          }
        }
      },
      "duration": {
        "typical_s": 1800,
        "bound_s": 21600,
        "scale_exponent": 0.3,
        "scale_note": "Bake time scales approximately as (mass ratio)^0.3, not linearly. See docs/spec/SCALING.md."
      },
      "yield_factor": 0.85,
      "observables": [
        "browning",
        "internal_temperature",
        "volume"
      ],
      "hazards": [
        "hot_surface",
        "burns"
      ],
      "attended": 0.0,
      "aliases": [
        "baking",
        "roast",
        "roasting"
      ]
    },
    "grill": {
      "label": "Grill / broil",
      "core": "cook_and_cool",
      "definition": "Cook by radiant heat from one side. `heat_source: above` is broiling; `below` is grilling.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "heat_source": {
          "type": "enum",
          "values": [
            "above",
            "below"
          ],
          "default": "below"
        },
        "heat": {
          "type": "heat_level",
          "default": "high"
        }
      },
      "preconditions": {
        "equipment": {
          "class": "grill",
          "thermal": {
            "min": "very_hot"
          }
        }
      },
      "effects": {
        "material": {
          "doneness": {
            "at_least": "cooked_through"
          },
          "browning": {
            "at_least": "deep_brown"
          },
          "thermal": {
            "set": "very_hot"
          }
        }
      },
      "duration": {
        "typical_s": 480,
        "bound_s": 2400
      },
      "yield_factor": 0.8,
      "observables": [
        "browning",
        "internal_temperature"
      ],
      "hazards": [
        "hot_surface",
        "flare_up",
        "smoke"
      ],
      "attended": 1.0,
      "attended_note": "Genuinely exclusive: the failure window is seconds, so a cook who looks away ruins it.",
      "aliases": [
        "broil",
        "broiling"
      ]
    },
    "braise": {
      "label": "Braise",
      "core": "cook_and_cool",
      "definition": "Cook slowly in a small amount of liquid in a covered vessel. Conventionally follows a sear.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "setpoint": {
          "type": "quantity",
          "dimension": "temperature",
          "required": false
        },
        "covered": {
          "type": "boolean",
          "default": true
        }
      },
      "preconditions": {
        "material": {
          "hydration": {
            "min": "moist"
          }
        },
        "equipment": {
          "class": "heated_vessel",
          "attributes": {
            "lidded": true
          }
        },
        "applies_to": "any"
      },
      "effects": {
        "material": {
          "doneness": {
            "at_least": "well_done"
          },
          "thermal": {
            "set": "hot"
          },
          "texture": {
            "set": "tender"
          }
        }
      },
      "duration": {
        "typical_s": 7200,
        "bound_s": 28800
      },
      "yield_factor": 0.85,
      "observables": [
        "texture",
        "internal_temperature"
      ],
      "hazards": [
        "hot_liquid",
        "hot_surface"
      ],
      "precondition_note": "Describes the braising liquid, not the meat.",
      "attended": 0.0,
      "attended_note": "Unattended once covered and in the oven. Per-task `concurrent_actions` add the stirring back."
    },
    "reduce": {
      "label": "Reduce",
      "core": "cook_and_cool",
      "definition": "Concentrate a liquid by evaporation.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "target_fraction": {
          "type": "number",
          "minimum": 0.05,
          "maximum": 0.99,
          "note": "Fraction of the original volume remaining. 'Reduce by half' is target_fraction 0.5."
        }
      },
      "preconditions": {
        "material": {
          "form": {
            "is": "liquid"
          }
        },
        "equipment": {
          "class": "heated_vessel",
          "attributes": {
            "lidded": false
          }
        }
      },
      "effects": {
        "material": {
          "thermal": {
            "set": "hot"
          },
          "hydration": {
            "set": "wet"
          }
        }
      },
      "duration": {
        "typical_s": 900,
        "bound_s": 7200
      },
      "yield_factor_from_param": "target_fraction",
      "observables": [
        "volume",
        "viscosity"
      ],
      "hazards": [
        "hot_liquid",
        "steam"
      ],
      "attended": 0.05,
      "attended_note": "Roughly `occasional` attention: 30s every 600s.",
      "attention_level": "occasional"
    },
    "deglaze": {
      "label": "Deglaze",
      "core": "pouring",
      "definition": "Dissolve fond from a vessel with liquid, recovering it into the dish. The only operation that consumes an equipment residue, which is what makes the searing/braising vessel dependency explicit rather than implied.",
      "arity": {
        "consumes": [
          1,
          2
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "material": {
          "form": {
            "is": "liquid"
          }
        },
        "equipment": {
          "class": "heated_vessel",
          "residue": {
            "has": [
              "fond"
            ]
          },
          "thermal": {
            "min": "hot"
          }
        },
        "applies_to": "any"
      },
      "effects": {
        "material": {
          "thermal": {
            "set": "hot"
          }
        },
        "equipment": {
          "residue": {
            "remove": [
              "fond"
            ]
          }
        }
      },
      "duration": {
        "typical_s": 120,
        "bound_s": 600
      },
      "observables": [
        "vessel_surface"
      ],
      "hazards": [
        "steam",
        "hot_liquid",
        "alcohol_vapour"
      ],
      "precondition_note": "The precondition describes the deglazing liquid. The other input is the material being deglazed, which is emphatically not liquid.",
      "attended": 1.0,
      "attended_note": "Genuinely exclusive: the failure window is seconds, so a cook who looks away ruins it."
    },
    "melt": {
      "label": "Melt",
      "core": "cook_and_cool",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "material": {
          "texture": {
            "max": "firm"
          }
        },
        "equipment": {
          "class": "heat_source"
        }
      },
      "effects": {
        "material": {
          "form": {
            "set": "liquid"
          },
          "thermal": {
            "set": "hot"
          },
          "texture": {
            "set": "soft"
          }
        },
        "equipment": {
          "residue": {
            "add": [
              "fat"
            ]
          },
          "cleanliness": {
            "set": "soiled"
          }
        }
      },
      "duration": {
        "typical_s": 180,
        "bound_s": 900
      },
      "yield_factor": 1.0,
      "observables": [
        "form"
      ],
      "hazards": [
        "hot_surface",
        "scorching"
      ],
      "effect_note": "Melting fat in a vessel leaves fat in it, which is what `fry` then requires. Thermal state is deliberately NOT set here: bringing a vessel to temperature is `preheat`, and conflating the two would let melt silently satisfy a searing temperature it does not reach.",
      "attended": 0.25,
      "attended_note": "Roughly `frequent` attention: 30s every 120s.",
      "attention_level": "frequent",
      "aliases": [
        "melting"
      ]
    },
    "toast": {
      "label": "Toast",
      "core": "cook_and_cool",
      "definition": "Brown by dry heat without added fat. Bread, nuts, spices.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "material": {
          "hydration": {
            "max": "dry"
          }
        },
        "equipment": {
          "class": "heat_source"
        }
      },
      "effects": {
        "material": {
          "browning": {
            "at_least": "golden_brown"
          },
          "thermal": {
            "set": "hot"
          },
          "hydration": {
            "set": "dry"
          },
          "texture": {
            "set": "crisp"
          }
        }
      },
      "duration": {
        "typical_s": 180,
        "bound_s": 900
      },
      "yield_factor": 0.95,
      "observables": [
        "browning",
        "aroma"
      ],
      "hazards": [
        "burns",
        "scorching"
      ],
      "attended": 1.0,
      "attended_note": "Genuinely exclusive: the failure window is seconds, so a cook who looks away ruins it.",
      "aliases": [
        "dry-frying",
        "toasting"
      ]
    },
    "chill": {
      "label": "Chill / freeze",
      "core": "cook_and_cool",
      "definition": "Lower temperature to a target thermal band.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "target": {
          "type": "facet_value",
          "facet": "thermal",
          "default": "chilled",
          "allowed": [
            "frozen",
            "chilled",
            "cool",
            "ambient"
          ]
        }
      },
      "preconditions": {
        "equipment": {
          "class": "cold_source"
        }
      },
      "effects": {
        "material": {
          "thermal": {
            "from_param": "target"
          }
        }
      },
      "duration": {
        "typical_s": 3600,
        "bound_s": 86400
      },
      "yield_factor": 1.0,
      "observables": [
        "internal_temperature"
      ],
      "safety_note": "Cooling cooked food through the 60-5 C band is a critical control point; see docs/spec/SAFETY.md.",
      "attended": 0.0,
      "aliases": [
        "cool",
        "cooling",
        "freeze",
        "freezing",
        "refrigerate",
        "refrigerating"
      ]
    },
    "thaw": {
      "label": "Thaw",
      "core": "cook_and_cool",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "material": {
          "thermal": {
            "is": "frozen"
          }
        }
      },
      "effects": {
        "material": {
          "thermal": {
            "set": "chilled"
          }
        }
      },
      "duration": {
        "typical_s": 28800,
        "bound_s": 172800
      },
      "yield_factor": 0.95,
      "observables": [
        "internal_temperature"
      ],
      "safety_note": "Thawing at ambient temperature holds the surface in the danger zone; prefer a cold_source.",
      "attended": 0.0,
      "aliases": [
        "defrost"
      ]
    },
    "transfer": {
      "label": "Transfer",
      "core": "pick_and_place",
      "definition": "Move material to a destination, optionally combining it with what is already there. Covers add, pour, tip and place.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "mode": {
          "type": "enum",
          "values": [
            "place",
            "pour",
            "scoop",
            "tip",
            "drizzle",
            "sprinkle"
          ],
          "default": "place"
        },
        "destination": {
          "type": "equipment_ref",
          "required": true
        }
      },
      "preconditions": {},
      "effects": {
        "material": {
          "containment": {
            "set": "in",
            "from_param": "destination"
          }
        }
      },
      "state_preserving": true,
      "duration": {
        "typical_s": 30,
        "bound_s": 300
      },
      "observables": [
        "containment"
      ],
      "hazards": [
        "hot_liquid",
        "spill"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "add",
        "place",
        "pour",
        "put",
        "return"
      ]
    },
    "plate": {
      "label": "Plate",
      "core": "pick_and_place",
      "definition": "Transfer to a serving vessel with a spatial arrangement. Terminal operation for a served component.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "vessel": {
          "type": "equipment_ref",
          "required": true
        },
        "arrangement": {
          "type": "arrangement_list",
          "required": false,
          "note": "Ordered spatial relations: on, over, under, beside, around, centred."
        },
        "portions": {
          "type": "integer",
          "required": false
        }
      },
      "preconditions": {
        "equipment": {
          "class": "serving_vessel",
          "cleanliness": {
            "is": "clean"
          }
        }
      },
      "effects": {
        "material": {
          "containment": {
            "set": "in",
            "from_param": "vessel"
          }
        }
      },
      "order_significant": true,
      "duration": {
        "typical_s": 120,
        "bound_s": 600
      },
      "observables": [
        "appearance"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "arrange",
        "serve"
      ]
    },
    "garnish": {
      "label": "Garnish",
      "core": "pick_and_place",
      "definition": "Add a final decorative or aromatic element to a plated component.",
      "arity": {
        "consumes": [
          2,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "placement": {
          "type": "enum",
          "values": [
            "over",
            "beside",
            "around",
            "centred",
            "scattered"
          ],
          "default": "over"
        }
      },
      "preconditions": {},
      "effects": {
        "material": {}
      },
      "terminal": true,
      "duration": {
        "typical_s": 60,
        "bound_s": 300
      },
      "observables": [
        "appearance"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "top"
      ]
    },
    "marinate": {
      "label": "Marinate",
      "core": "preparing",
      "arity": {
        "consumes": [
          2,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "material": {
          "doneness": {
            "is": "raw"
          }
        }
      },
      "effects": {
        "material": {
          "surface": {
            "add": [
              "marinated",
              "seasoned"
            ]
          },
          "hydration": {
            "set": "wet"
          }
        }
      },
      "duration": {
        "typical_s": 1800,
        "bound_s": 172800
      },
      "yield_factor": 1.05,
      "observables": [
        "elapsed"
      ],
      "safety_note": "Marinating raw protein requires chilled holding; the marinade is contaminated afterwards and may not be reused without cooking.",
      "attended": 0.0,
      "aliases": [
        "marinading"
      ]
    },
    "brine": {
      "label": "Brine",
      "core": "preparing",
      "arity": {
        "consumes": [
          2,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "material": {
          "doneness": {
            "is": "raw"
          }
        }
      },
      "effects": {
        "material": {
          "surface": {
            "add": [
              "salted"
            ]
          },
          "hydration": {
            "set": "soaked"
          }
        }
      },
      "duration": {
        "typical_s": 7200,
        "bound_s": 172800
      },
      "yield_factor": 1.08,
      "observables": [
        "elapsed"
      ],
      "safety_note": "Requires chilled holding.",
      "attended": 0.0
    },
    "rest": {
      "label": "Rest",
      "core": "preparing",
      "definition": "Hold without action to allow redistribution, relaxation or setting.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {},
      "effects": {
        "material": {
          "structure": {
            "add": [
              "set"
            ]
          }
        }
      },
      "duration": {
        "typical_s": 600,
        "bound_s": 7200
      },
      "yield_factor": 0.98,
      "observables": [
        "elapsed",
        "internal_temperature"
      ],
      "attended": 0.0,
      "aliases": [
        "letting stand",
        "resting",
        "standing"
      ]
    },
    "proof": {
      "label": "Proof",
      "core": "preparing",
      "definition": "Allow a leavened dough to rise.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "target_expansion": {
          "type": "number",
          "default": 2.0,
          "note": "Volume multiple. 'Until doubled in size' is 2.0."
        }
      },
      "preconditions": {
        "material": {
          "structure": {
            "has": [
              "kneaded"
            ]
          },
          "thermal": {
            "min": "ambient"
          }
        }
      },
      "effects": {
        "material": {
          "structure": {
            "add": [
              "risen"
            ]
          }
        }
      },
      "duration": {
        "typical_s": 5400,
        "bound_s": 86400
      },
      "observables": [
        "volume",
        "elapsed"
      ],
      "over_condition": {
        "facet": "structure",
        "failure": "separated",
        "note": "Over-proofing collapses the gluten network. This operation carries a max_hold constraint in every real recipe; see semantic rule SR-011."
      },
      "attended": 0.0,
      "aliases": [
        "prove",
        "proving",
        "rising"
      ]
    },
    "ferment": {
      "label": "Ferment",
      "core": "preparing",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "material": {
          "thermal": {
            "min": "cool"
          }
        }
      },
      "effects": {
        "material": {
          "structure": {
            "add": [
              "set"
            ]
          }
        }
      },
      "duration": {
        "typical_s": 86400,
        "bound_s": 31536000
      },
      "observables": [
        "ph",
        "aroma",
        "elapsed"
      ],
      "safety_note": "Uncontrolled fermentation is a food-safety hazard; pH or salinity is a control point.",
      "attended": 0.0
    },
    "infuse": {
      "label": "Infuse",
      "core": "preparing",
      "definition": "Extract flavour from a solid into a liquid; the solid is normally removed afterwards. Covers a bouquet garni held in a braise.",
      "arity": {
        "consumes": [
          2,
          null
        ],
        "produces": [
          1,
          2
        ]
      },
      "preconditions": {
        "material": {
          "form": {
            "is": "liquid"
          }
        },
        "applies_to": "any"
      },
      "effects": {
        "material": {}
      },
      "produces_byproduct": {
        "optional": true,
        "label": "spent_solids",
        "note": "Naming this output is what lets a validator check that the aromatics were actually removed before serving."
      },
      "duration": {
        "typical_s": 1200,
        "bound_s": 86400
      },
      "observables": [
        "aroma",
        "colour",
        "elapsed"
      ],
      "precondition_note": "Describes the infusion medium, not the aromatics steeping in it.",
      "attended": 0.0,
      "aliases": [
        "steep",
        "steeping"
      ]
    },
    "assess": {
      "label": "Assess",
      "core": "control",
      "definition": "Observe a material and record the result, without changing it. The read half of the taste-and-adjust feedback loop that open-loop plans cannot otherwise express.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          0,
          0
        ]
      },
      "params": {
        "observable": {
          "type": "observable_ref",
          "required": true
        },
        "binds": {
          "type": "identifier",
          "required": true,
          "note": "Name bound to the observation, referenced by a subsequent `adjust`."
        }
      },
      "preconditions": {},
      "effects": {},
      "non_mutating": true,
      "duration": {
        "typical_s": 15,
        "bound_s": 120
      },
      "observables": [
        "taste",
        "texture",
        "browning",
        "aroma",
        "internal_temperature",
        "volume",
        "viscosity"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "check",
        "taste"
      ]
    },
    "adjust": {
      "label": "Adjust",
      "core": "control",
      "definition": "Add or alter in response to an assessment. The write half of the feedback loop; resolves to-taste quantities at execution time.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "responds_to": {
          "type": "identifier",
          "required": true,
          "note": "The `binds` name from a preceding `assess`."
        }
      },
      "preconditions": {},
      "effects": {
        "material": {}
      },
      "requires_preceding": "assess",
      "duration": {
        "typical_s": 30,
        "bound_s": 300
      },
      "observables": [
        "taste"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside."
    },
    "hold": {
      "label": "Hold",
      "core": "control",
      "definition": "Keep a material in its current state for a bounded period, optionally at temperature. Covers both 'set aside' and 'keep warm'.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "at": {
          "type": "facet_value",
          "facet": "thermal",
          "required": false
        }
      },
      "preconditions": {},
      "effects": {
        "material": {}
      },
      "state_preserving": true,
      "duration": {
        "typical_s": 600,
        "bound_s": 14400
      },
      "observables": [
        "elapsed",
        "internal_temperature"
      ],
      "safety_note": "Holding between 5 and 60 C is time-limited; see docs/spec/SAFETY.md.",
      "attended": 0.0,
      "aliases": [
        "keep"
      ]
    },
    "wash": {
      "label": "Wash",
      "core": "control",
      "definition": "Clean equipment. Required between contact with a contaminating material and contact with one that excludes that hazard.",
      "arity": {
        "consumes": [
          0,
          0
        ],
        "produces": [
          0,
          0
        ]
      },
      "preconditions": {
        "equipment": {
          "cleanliness": {
            "min": "soiled"
          }
        }
      },
      "effects": {
        "equipment": {
          "cleanliness": {
            "set": "clean"
          },
          "residue": {
            "remove": [
              "fond",
              "fat",
              "liquid",
              "crumb"
            ]
          },
          "thermal": {
            "set": "ambient"
          }
        }
      },
      "duration": {
        "typical_s": 120,
        "bound_s": 600
      },
      "observables": [
        "cleanliness"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside.",
      "aliases": [
        "rinse"
      ]
    },
    "grease": {
      "label": "Grease",
      "core": "control",
      "definition": "Apply fat to equipment to prevent sticking. Broken out because 'a greased loaf pan' otherwise hides an undeclared material.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          0,
          0
        ]
      },
      "preconditions": {
        "equipment": {
          "cleanliness": {
            "is": "clean"
          }
        }
      },
      "effects": {
        "equipment": {
          "surface": {
            "add": [
              "greased"
            ]
          }
        }
      },
      "duration": {
        "typical_s": 60,
        "bound_s": 180
      },
      "observables": [
        "surface"
      ],
      "attended": 0.9,
      "attended_note": "Hands-on, but a cook can glance away: the residual tenth is what lets something simmer alongside."
    },
    "external_procedure": {
      "label": "External procedure",
      "core": "control",
      "definition": "An opaque sub-procedure defined outside Sous, such as 'prepare according to package directions'. Deliberately NOT executable: it carries no preconditions or effects a planner can use, and any conformance profile above `core` rejects it. It exists so that such recipes fail loudly rather than being silently mis-modelled.",
      "arity": {
        "consumes": [
          0,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "instructions": {
          "type": "string",
          "required": true
        },
        "source": {
          "type": "string",
          "required": false
        }
      },
      "preconditions": {},
      "effects": {
        "material": {}
      },
      "executable": false,
      "duration": {
        "typical_s": null,
        "bound_s": null
      },
      "observables": [],
      "attended": 0.0,
      "attended_note": "Zero by construction -- the procedure is opaque, so its attention cost is unknown. It is non-executable regardless."
    },
    "blanch": {
      "label": "Blanch",
      "core": "cook_and_cool",
      "definition": "Cook briefly in boiling water and stop the cooking, usually in ice water. Sets colour and loosens skins; it is not cooking through.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "shock": {
          "type": "boolean",
          "default": true,
          "note": "Whether the material is chilled immediately afterwards."
        }
      },
      "preconditions": {
        "equipment": {
          "class": "heated_vessel"
        }
      },
      "effects": {
        "material": {
          "doneness": {
            "set": "blanched"
          },
          "texture": {
            "set": "tender"
          },
          "hydration": {
            "set": "moist"
          }
        }
      },
      "duration": {
        "typical_s": 90,
        "bound_s": 600
      },
      "yield_factor": 1.0,
      "observables": [
        "texture",
        "colour"
      ],
      "hazards": [
        "hot_liquid",
        "steam"
      ],
      "attended": 1.0,
      "attended_note": "Short and unforgiving: thirty seconds late is the difference between blanched and boiled.",
      "note": "Added in 0.4.0. The `doneness` facet has carried the value `blanched` since 0.1.0 with no operation that reached it -- a state the vocabulary could describe and not produce. A 1500-recipe corpus named it nine times."
    },
    "mash": {
      "label": "Mash",
      "core": "cutting",
      "definition": "Crush a cooked or soft material to a coarse, lumpy mass. Coarser than `puree`, which is smooth and homogeneous.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "material": {
          "texture": {
            "min": "tender"
          }
        }
      },
      "precondition_note": "Mashing something firm yields fragments, not a mash.",
      "effects": {
        "material": {
          "form": {
            "set": "mashed"
          },
          "structure": {
            "add": [
              "homogeneous"
            ]
          }
        }
      },
      "duration": {
        "typical_s": 120,
        "bound_s": 600
      },
      "yield_factor": 1.0,
      "observables": [
        "texture",
        "form"
      ],
      "hazards": [],
      "attended": 0.9,
      "note": "Added in 0.4.0. Like `blanch`, the `form` facet already carried `mashed` and nothing produced it."
    },
    "cream": {
      "label": "Cream",
      "core": "mixing",
      "definition": "Beat a solid fat with sugar until pale and aerated. The aeration is the point: it is what makes the crumb, and it fails if the fat is cold.",
      "arity": {
        "consumes": [
          2,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "material": {
          "texture": {
            "min": "soft"
          }
        }
      },
      "precondition_note": "Fat straight from the fridge will not hold air. This is why the precondition is on texture rather than on temperature: softened is the state that matters, however it was reached.",
      "effects": {
        "material": {
          "structure": {
            "add": [
              "aerated",
              "homogeneous"
            ]
          },
          "browning": {
            "set": "pale"
          }
        }
      },
      "duration": {
        "typical_s": 240,
        "bound_s": 900
      },
      "yield_factor": 1.0,
      "observables": [
        "structure",
        "colour"
      ],
      "hazards": [],
      "attended": 1.0,
      "note": "Added in 0.4.0, 36 occurrences. Distinct from `whisk`: same motion, but a declared precondition that whisking does not have.",
      "attended_note": "Continuous beating, so exclusive by hand. A stand mixer does the work and would make this periodic monitoring instead, but Sous has no way to say that a machine is holding the spoon -- see LIMITATIONS. The conservative figure is the honest one."
    },
    "stir_fry": {
      "label": "Stir-fry",
      "core": "cook_and_cool",
      "definition": "Cook quickly over very high heat with constant agitation, in a little fat.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "params": {
        "heat": {
          "type": "heat_level",
          "default": "maximum"
        }
      },
      "preconditions": {
        "equipment": {
          "class": "heated_vessel",
          "thermal": {
            "min": "very_hot"
          },
          "residue": {
            "has": [
              "fat"
            ]
          }
        },
        "material": {
          "hydration": {
            "max": "dry"
          }
        }
      },
      "precondition_note": "Wet material steams instead of searing, and the pan must already be at heat -- a stir-fry that starts cold is a braise.",
      "effects": {
        "material": {
          "doneness": {
            "at_least": "cooked_through"
          },
          "browning": {
            "at_least": "golden"
          },
          "thermal": {
            "set": "very_hot"
          },
          "texture": {
            "set": "crisp"
          }
        },
        "equipment": {
          "cleanliness": {
            "set": "soiled"
          }
        }
      },
      "duration": {
        "typical_s": 240,
        "bound_s": 900
      },
      "yield_factor": 0.85,
      "batching": {
        "limited_by": "vessel_surface_area"
      },
      "observables": [
        "browning",
        "texture"
      ],
      "hazards": [
        "hot_oil",
        "spatter",
        "hot_surface"
      ],
      "attended": 1.0,
      "attended_note": "Constant agitation is in the definition; a cook who steps away has stopped stir-frying.",
      "note": "Added in 0.4.0, 17 occurrences. NOT an alias of `saute`: higher heat, constant motion, and a crisp rather than tender result."
    },
    "baste": {
      "label": "Baste",
      "core": "pouring",
      "definition": "Spoon or brush cooking liquid or fat over a material while it cooks.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {},
      "effects": {
        "material": {
          "surface": {
            "add": [
              "oiled"
            ]
          },
          "hydration": {
            "set": "moist"
          }
        }
      },
      "duration": {
        "typical_s": 30,
        "bound_s": 120
      },
      "yield_factor": 1.0,
      "non_mutating": true,
      "observables": [
        "surface"
      ],
      "hazards": [
        "hot_oil",
        "hot_surface"
      ],
      "attended": 1.0,
      "note": "Added in 0.4.0, 11 occurrences. Usually a concurrent action inside a roast rather than a step of its own, which is what `concurrent_actions` is for."
    },
    "scald": {
      "label": "Scald",
      "core": "cook_and_cool",
      "definition": "Heat a liquid to just below boiling. Used for milk, where boiling would catch and skin.",
      "arity": {
        "consumes": [
          1,
          1
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "equipment": {
          "class": "heated_vessel"
        },
        "material": {
          "form": {
            "is": "liquid"
          }
        }
      },
      "effects": {
        "material": {
          "thermal": {
            "set": "very_hot"
          }
        }
      },
      "duration": {
        "typical_s": 300,
        "bound_s": 900
      },
      "yield_factor": 0.97,
      "yield_note": "A little evaporates.",
      "observables": [
        "internal_temperature",
        "surface"
      ],
      "hazards": [
        "hot_liquid",
        "steam",
        "burns"
      ],
      "attended": 0.25,
      "attention_level": "frequent",
      "note": "Added in 0.4.0, 7 occurrences. The failure mode is overshoot, so the termination condition matters more than the duration."
    },
    "temper": {
      "label": "Temper",
      "core": "mixing",
      "definition": "Raise the temperature of a heat-sensitive material gradually, by incorporating a hot liquid a little at a time, so that it does not curdle or seize.",
      "arity": {
        "consumes": [
          2,
          2
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "material": {
          "thermal": {
            "max": "warm"
          }
        }
      },
      "effects": {
        "material": {
          "thermal": {
            "set": "warm"
          },
          "structure": {
            "add": [
              "homogeneous"
            ]
          }
        }
      },
      "failure_states": [
        "curdled"
      ],
      "duration": {
        "typical_s": 120,
        "bound_s": 480
      },
      "yield_factor": 1.0,
      "observables": [
        "structure",
        "internal_temperature"
      ],
      "hazards": [
        "hot_liquid"
      ],
      "attended": 1.0,
      "note": "Added in 0.4.0. CAREFUL WITH THE NAME. English cooking uses 'tempering' for at least three unrelated techniques: this one (eggs into hot liquid), chocolate tempering (crystal control on cooling), and the Indian tadka (blooming whole spices in hot fat, a translation of tarka/chaunk). Only the first is meant here. The corpus that prompted this operation used it exclusively for eggs and custard; the Dal Tadka worked example is the third sense and is correctly modelled as `fry`."
    },
    "pressure_cook": {
      "label": "Pressure-cook",
      "core": "cook_and_cool",
      "definition": "Cook in a sealed vessel above atmospheric pressure, so the liquid exceeds 100 degrees Celsius.",
      "arity": {
        "consumes": [
          1,
          null
        ],
        "produces": [
          1,
          1
        ]
      },
      "preconditions": {
        "equipment": {
          "class": "pressure_cooker"
        }
      },
      "effects": {
        "material": {
          "doneness": {
            "at_least": "cooked_through"
          },
          "texture": {
            "set": "soft"
          },
          "thermal": {
            "set": "very_hot"
          },
          "hydration": {
            "set": "submerged"
          }
        }
      },
      "duration": {
        "typical_s": 1200,
        "bound_s": 5400
      },
      "yield_factor": 1.0,
      "observables": [
        "pressure",
        "texture"
      ],
      "hazards": [
        "hot_liquid",
        "steam",
        "pressure"
      ],
      "safety_note": "The vessel must depressurise before it is opened. Sous models that as an equipment precondition rather than as advice.",
      "attended": 0.0,
      "attended_note": "Sealed and unobservable while it runs, which is exactly why the termination bound matters.",
      "note": "Added in 0.4.0, 8 occurrences. Brings the `pressure_cooker` equipment class with it."
    }
  },
  "heat_levels": {
    "note": "Qualitative heat levels are what recipes actually say. Each maps to an approximate vessel-surface temperature band; a kitchen profile may override the mapping per appliance.",
    "values": {
      "low": {
        "approx_c": [
          60,
          120
        ]
      },
      "medium_low": {
        "approx_c": [
          120,
          150
        ]
      },
      "medium": {
        "approx_c": [
          150,
          180
        ]
      },
      "medium_high": {
        "approx_c": [
          180,
          220
        ]
      },
      "high": {
        "approx_c": [
          220,
          260
        ]
      },
      "maximum": {
        "approx_c": [
          260,
          350
        ]
      }
    }
  },
  "attention_levels": {
    "note": "How a qualitative frequency becomes schedulable. `visit_s` is how long one visit takes; `interval_s` is how often it recurs; `attended` is their ratio and is DERIVED, not independently chosen. A visit occupies a whole cook while it happens -- you cannot half-stir -- so a scheduler places visits rather than smearing the fraction across the task.",
    "values": {
      "constant": {
        "attended": 1.0,
        "interval_s": null,
        "note": "Continuous; not a visit pattern. Never leaves the pan.",
        "visit_s": null
      },
      "frequent": {
        "attended": 0.25,
        "interval_s": 120,
        "visit_s": 30
      },
      "occasional": {
        "attended": 0.05,
        "interval_s": 600,
        "visit_s": 30
      },
      "as_required": {
        "attended": 0.0222,
        "interval_s": 900,
        "visit_s": 20
      },
      "none": {
        "attended": 0.0,
        "interval_s": null,
        "visit_s": 0
      }
    }
  },
  "extension": {
    "namespace_pattern": "^x-[a-z0-9-]+:[a-z0-9_]+$",
    "requires": [
      "fallback"
    ],
    "note": "Vendor operations must name a `fallback` core operation so an executor that does not know the extension degrades rather than fails."
  }
}
