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 constructibleBaseRecipe with a unique id, matches(), and apply():
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 undersrc/agentcompass/recipes/, then register it:
__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:RECIPES registry:
- the directory exists and contains
__init__.py; RECIPE_CLASSESis a non-empty list or tuple;- every item is a concrete
BaseRecipesubclass with a non-empty, uniqueid; - every class supports zero-argument construction.
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:- If
execution.enabled_recipesis non-empty, skip registry entries whose registered names are not in that allowlist. - Construct the Recipe with no arguments.
- Call
matches()with the plan produced so far. - When it matches, replace the current plan with the value returned by
apply()and record the Recipeidinapplied_recipes. - Continue to the next registry entry, which can inspect the updated plan.
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 noagentcompass list recipe command, so inspect the run-local registry directly for an external package:
--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_plansinrun_info.jsonand in the terminal result’s top-levelapplied_recipeslist; - 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.
Completion Checklist
- The behavior belongs in deterministic per-task planning rather than Benchmark, Harness, Environment, or runtime mechanics.
matches()andapply()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_CLASSESregistration 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.
