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. 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.
  • skills, when present, must be a list of unique, non-empty strings. Entries are trimmed; a duplicate, a blank entry, or a non-string entry is a build error.
  • 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: 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.

Skills

Each entry in skills names a Guild skill the agent depends on. Write it as a bare name, or as owner~name where owner is the agent’s own account — every reference resolves against that account, so an owner~name naming any other account does not resolve and fails the build.
  • A reference resolves to the skill’s latest version. Version pinning such as name@1.0.0 is rejected rather than silently ignored.
  • A reference that does not resolve fails the build.
  • A public agent cannot depend on a private skill, because someone running the agent from another account could not read it.
The build resolves and validates these references in a Validate Guild skills step, then records each resolved skill on the version in a Store Goose skills step, so a Goose agent’s skills are queryable like its tools. Distinct references that resolve to the same skill collapse into one record.

Input schema

The input schema persisted on the version is a JSON Schema (Draft 2020-12) describing this shape:
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: Each property carries the parameter’s description. Optional parameters also carry their coerced default.

Example

This recipe produces the following input schema:
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:

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. Rendering — instructions 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 prompt → text (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.

Execution environment

A Goose agent runs inside a Docker container. By default, the platform runs it in the guildai~goosebox image. If the agent version’s runtime environment specifies a custom container image, the Goose agent runs inside that image instead of the default. This lets you run Goose agents in a customized execution environment rather than the default box.

Skills at run time

When the recipe declares skills, the runtime materializes each one on disk before the agent starts, writing <workspace>/.agents/skills/<name>/SKILL.md. Each file is name and description YAML frontmatter followed by the skill body. A skill name that would escape the skills directory is rejected rather than written. Goose’s built-in skills extension is enabled with --with-builtin skills only when the recipe declares skills; the extension then discovers the materialized files and the agent loads a skill body when it needs one. A recipe that declares no skills runs without the extension, and its invocation is unchanged.

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. Structured input from a parent agent is validated against the Goose agent’s input schema before any wrapping. When the input already conforms to the schema, it passes through unchanged, so a parent agent can send structured parameters directly. Only input that does not conform to the schema is wrapped as canonical text {"type": "text", "text": "<serialized input>"}.