Skip to main content
Add a Recipe when a Benchmark task needs deterministic, Environment-aware changes to its per-task ExecutionPlan. A Recipe is planning policy. It may select an image, workspace, resources, network settings, or a specialized Benchmark or Harness plan, but it must not open a sandbox, install packages, call a model or provider, run a task, or score a result. Put those side effects in the lifecycle component that owns them.

Choose the Integration Form

External Recipe code executes in the AgentCompass process, outside the task sandbox. Only load packages you have reviewed and trust.

Implement the Base Contract

Every Recipe is a zero-argument constructible BaseRecipe with a unique id, matches(), and apply():
Use matches(req, task, plan) only to decide whether the Recipe applies. It receives the current plan, including changes made by earlier matching Recipes. It must not mutate any argument. Use apply(plan, req, task) to return a new ExecutionPlan. Deep-copy the incoming plan before changing nested Environment, Benchmark, or Harness plan fields. Preserve explicit compatible user values: fill missing fields with setdefault() or an equivalent fallback, and reject an incompatible explicit value with an actionable error instead of silently replacing it. Both methods belong to deterministic planning. Keep them free of network access, file writes, subprocesses, package installation, sandbox creation, model calls, and other externally visible side effects.

Register a Built-in Recipe

Place a public implementation under src/agentcompass/recipes/, then register it:
Import the module through the relevant package __init__.py files until importing agentcompass.recipes executes the decorator. The registry uses id as its key and rejects duplicate IDs. Keep provider-specific adaptation in the Recipe; do not move Environment mechanics into a Benchmark. Built-in Recipes must target only public providers and public infrastructure. Keep organization-specific deployment policy in a trusted external package instead.

Package a Trusted External Recipe

An external Recipe directory is a Python package, not a loose Python file:
Define the class without decorating it with the process-global RECIPES registry:
Export the same class from the package root:
The loader requires all of the following:
  • the directory exists and contains __init__.py;
  • RECIPE_CLASSES is a non-empty list or tuple;
  • every item is a concrete BaseRecipe subclass with a non-empty, unique id;
  • every class supports zero-argument construction.
Relative directories resolve from the current working directory. AgentCompass canonicalizes and de-duplicates paths, clones the built-in registry for the run, and appends external classes. A duplicate ID, including a collision with a built-in Recipe, fails during registration. After adding example_exact_match and example_answer from the Benchmark and Harness implementation tutorials, load and allow the Recipe for their deterministic task:
--recipe is an allowlist, not a force-run switch. The Recipe still needs to return True from matches(). See Recipes for the CLI, SDK, configuration-file, and orchestration equivalents.

Understand Application Order

The current Planner builds one default plan for an attempt and then walks the run-local Recipe registry in insertion order:
  1. If execution.enabled_recipes is non-empty, skip registry entries whose registered names are not in that allowlist.
  2. Construct the Recipe with no arguments.
  3. Call matches() with the plan produced so far.
  4. When it matches, replace the current plan with the value returned by apply() and record the Recipe id in applied_recipes.
  5. Continue to the next registry entry, which can inspect the updated plan.
Built-ins retain their registration insertion order. External classes are appended after built-ins, following runtime.recipe_dirs order and then each package’s RECIPE_CLASSES order. All matching Recipes are applied; the Planner does not automatically resolve overlapping writes. BaseRecipe currently declares priority and enabled_by_default, but the Planner does not consult either attribute. Do not claim that priority changes ordering or use either attribute as an enablement mechanism. Current participation is controlled by registration, execution.enabled_recipes, and matches(). Treat registration order as current execution semantics, not as a substitute for clear ownership. Avoid Recipes that write the same fields, and do not make correctness depend on an unrelated module import order.

Validate the Integration

First confirm that the package loads and the ID is present. There is currently no agentcompass list recipe command, so inspect the run-local registry directly for an external package:
Then run one known task with --task-concurrency 1, --max-retries 0, and DEBUG logging. Verify:
  • matches() is false for an unrelated Benchmark or Environment;
  • the expected ID appears in each relevant attempt under resolved_execution_plans in run_info.json and in the terminal result’s top-level applied_recipes list;
  • each saved attempt’s resolved execution plan contains the intended values;
  • an explicit compatible image, workspace, resource, or network value survives Recipe application;
  • the selected Environment can build its config from the adjusted plan and clean up after both success and failure.
If two Recipes could match, validate both combinations or redesign them so their ownership does not overlap. Follow Testing and Validation for the repository checks and single-task evidence expected in a pull request.

Completion Checklist

  • The behavior belongs in deterministic per-task planning rather than Benchmark, Harness, Environment, or runtime mechanics.
  • matches() and apply() are deterministic and side-effect free.
  • apply() returns a copied plan and preserves explicit user intent.
  • The Recipe ID is unique, and the correct built-in or RECIPE_CLASSES registration path loads it.
  • Matching and non-matching cases, explicit overrides, resolved plans, and cleanup have been verified.
  • Public user behavior and supported combinations are documented in both languages.