Skip to main content
Guild can run Goose recipes as agents. You supply a recipe.yaml file, and the platform validates it at build time, derives typed input and output schemas from it, and executes it at run time. This page describes every rule the platform applies: which fields are honored, how recipes are validated, how input and output schemas are constructed, and how the recipe and caller input combine at task start.

Recipe file

The agent version’s files must include a recipe.yaml at the root. The file must be valid YAML whose document root is a mapping. Other files may exist alongside recipe.yaml; the platform does not consume them. JSON recipes (recipe.json) are not supported.

Lifecycle

The same recipe.yaml is parsed at two points:
  1. Build time — The recipe is fully validated. On success, description, input_schema, and output_schema are extracted and persisted on the agent version. Any validation error fails the build, and all errors are reported together.
  2. Task start — The recipe is re-parsed from the version’s files. instructions, prompt, and parameters are not persisted — the recipe file is their single source of truth. Because versions are immutable, a recipe that validated at build time parses identically at task start.

Recipe fields

Every top-level field is honored, ignored, or rejected.
FieldTreatment
descriptionHonored — persisted as the version and agent description.
instructionsHonored — rendered and sent as the system prompt.
promptHonored — rendered and used as the default initial message.
parametersHonored — drives the input schema and template rendering.
responseHonoredjson_schema becomes the output schema.
titleIgnored — validated as a string if present; Guild uses the agent name.
versionIgnored
authorIgnored
activitiesIgnored — desktop-only in Goose; the Goose CLI ignores them too.
extensionsRejected — the platform does not honor recipe extensions.
settingsRejected — provider and model are platform-controlled.
sub_recipesRejected — not supported.
retryRejected — not supported.
Any other keyRejected — catches typos and future Goose fields not yet reviewed.
At least one of instructions or prompt must be present.
Rejected fields produce a build error naming the field — for example, Field 'extensions' is not supported on Guild. This is intentional: a recipe that declares an extension expects the agent to have it, and running without it produces confusing behavior. The build is the earliest moment authors learn what the platform will not support.

Validation

Recipe-level

  • description is required and must be a non-empty string.
  • instructions, prompt, and title must be strings when present.
  • At least one of instructions or prompt must be present.
  • Top-level fields not listed in the table above are rejected.

Parameter-level

parameters, when present, is a list of mappings. Each parameter must satisfy:
FieldRule
keyRequired. Matches ^[a-zA-Z_][a-zA-Z0-9_]*$. Must be unique across the list.
input_typeRequired. One of string, number, boolean, date, select. file is rejected.
requirementRequired. One of required, optional, user_prompt.
descriptionRequired. Non-empty string.
defaultRequired for optional parameters; forbidden for required and user_prompt parameters.
optionsRequired non-empty list of unique strings for select; forbidden for all other types.
Default coercion: any YAML scalar default is accepted and coerced to the declared type. Accepted values by type:
  • number — integer, float, or numeric string.
  • boolean — YAML boolean or "true" / "false" (case-insensitive).
  • date — ISO YYYY-MM-DD string.
  • select — must be one of the declared options.
  • string — any scalar, kept as a string.
A non-coercible default is a build error. The coerced value is what appears in the generated JSON schema.

Template-level

instructions and prompt are Jinja-style templates.
  • Templates must parse without syntax errors.
  • Every variable referenced in instructions or prompt must be a declared parameter.
  • Every declared parameter must be referenced in instructions or prompt. (activities are ignored on Guild and do not count as usage.)
  • The built-in {{ recipe_dir }} variable is rejected — there is no recipe directory in the Guild execution model.
  • Rendering is sandboxed: template expressions cannot access host internals.

Response-level

  • response, when present, must be a mapping containing json_schema.
  • response.json_schema must be a mapping and a structurally valid JSON Schema (Draft 2020-12).
  • response without json_schema is a build error.

Input schema

The input schema persisted on the version is a JSON Schema (Draft 2020-12) describing this shape:
{ "text": "...", "parameters": { "<key>": "<value>", ... } }
The schema has type: object and additionalProperties: false. It always contains a text property. It contains a parameters property only when the recipe declares at least one parameter.

text

text is always present with type: string.
  • Required when the recipe has no prompt — the caller must supply the initial message. Description: "The message for the agent."
  • Optional when the recipe has a prompt. Description: "Optional message appended after the recipe's default prompt."

Parameters

The parameters property has type: object and additionalProperties: false. Its required array lists every parameter whose requirement is required or user_prompt.
user_prompt-requirement parameters are treated as required. Goose’s semantics (“interactively prompt the user if not provided”) have no headless equivalent — leaving them unset would surface literal {{ key }} text in the rendered prompt. On Guild, the input form is the user prompting.
parameters itself is required at the top level when its required array is non-empty. Each parameter maps to a JSON Schema property according to its input_type:
input_typeJSON Schema property
string{"type": "string"}
number{"type": "number"}
boolean{"type": "boolean"}
date{"type": "string", "format": "date"}
select{"type": "string", "enum": [<options>]}
Each property carries the parameter’s description. Optional parameters also carry their coerced default.

Example

description: Review code with a configurable focus
instructions: You are a {{ language }} reviewer focused on {{ focus }}.
prompt: Review the code provided below.
parameters:
  - key: language
    input_type: select
    requirement: required
    description: Language under review
    options: [python, typescript]
  - key: focus
    input_type: string
    requirement: optional
    default: best practices
    description: Review focus area
This recipe produces the following input schema:
{
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "description": "Optional message appended after the recipe's default prompt."
    },
    "parameters": {
      "type": "object",
      "properties": {
        "language": {
          "type": "string",
          "enum": ["python", "typescript"],
          "description": "Language under review"
        },
        "focus": {
          "type": "string",
          "default": "best practices",
          "description": "Review focus area"
        }
      },
      "required": ["language"],
      "additionalProperties": false
    }
  },
  "required": ["parameters"],
  "additionalProperties": false
}
If the recipe omitted prompt, required at the top level would be ["text", "parameters"].

Output schema

When response.json_schema is present, it is persisted verbatim as the version’s output schema. When absent, the output schema defaults to the canonical text shape, so the agent’s final answer posts to chat:
{
  "type": "object",
  "properties": {
    "type": {"type": "string", "const": "text"},
    "text": {"type": "string"}
  },
  "required": ["type", "text"],
  "additionalProperties": false
}

Runtime semantics

At task start, the parsed recipe and the validated input combine as follows:
  1. Parameter values — declared defaults overlaid with input.parameters.
  2. Renderinginstructions and prompt are rendered with the parameter values.
  3. System prompt — the rendered instructions, when present.
  4. Initial message — determined by whether the recipe has a prompt and whether the caller supplied text:
    • Recipe has no prompttext (the schema makes it required).
    • Recipe has a prompt, no text given → the rendered prompt.
    • Both present → the rendered prompt, then text, joined by a blank line.
  5. Workspace context — when present, prepended to the rendered prompt at task start, wrapped in a {% raw %} block so it is not treated as a template. The resulting prompt orders workspace context first, then the rendered recipe prompt, then text. It is never injected into the rendered instructions. Prepending the context inside the prompt carries it on every --resume turn, matching the native Goose runtime behavior.
The append rule mirrors Goose’s own behavior: in interactive mode the recipe prompt is the first turn, with the user’s messages following it. Append preserves that ordering — default behavior first, user steering after.

Chat input mediation

Chat-originated input arrives as canonical text {"type": "text", "text": "..."}. When the target schema has a top-level text string property, that input maps to {"text": "<text>"}. Validation then applies as usual — a bare chat message can only start a Goose agent whose schema does not require parameters.