Scenario Recipe Schema
This page documents the canonical upload contract for scenario recipes. It is language-agnostic: the schema is described as JSON with per-field expectations. The source of truth lives in packages/types/src/schemas/scenarios.ts (ScenarioRecipesFileSchema).
The file is posted as the JSON body of:
POST /v1/setup/setups/:setupId/scenario-recipe-versionsThe planner analyzes your project in place but writes everything it generates - the discovery output, the scenarios document, and the recipe file itself - to its own output directory at ~/.autonoma/<project-slug>/. Nothing is written into the repository, so the source paths below point into that directory, not at committed files.
Top-level shape
{ "version": 1, "source": { "discoverPath": "string", "scenariosPath": "string" }, "validationMode": "sdk-check" | "endpoint-lifecycle", "recipes": [ /* at least one ScenarioRecipe */ ]}| Field | Type | Required | Notes |
|---|---|---|---|
version | integer, must equal 1 | yes | Contract version. Currently only 1 is accepted. Not a string. |
source | object | yes | Provenance pointers into the planner’s output directory. Additional keys are preserved. |
source.discoverPath | string | yes | Path to the discovery output the recipes were derived from, inside the planner’s output directory (~/.autonoma/<project-slug>/), e.g. autonoma/discover.json. Required - omitting it causes Zod to fail with Invalid input: expected string, received undefined. |
source.scenariosPath | string | yes | Path to the human-readable scenarios document, in the same output directory, e.g. autonoma/scenarios.md. |
validationMode | "sdk-check" | "endpoint-lifecycle" | yes | How Autonoma validated the recipes before upload. sdk-check = checkScenario/checkAllScenarios. endpoint-lifecycle = real HTTP up/down. |
recipes | array, minimum length 1 | yes | One entry per scenario. See below. |
ScenarioRecipe (one entry in recipes[])
{ "name": "string", "description": "string", "create": { /* arbitrary model graph, see below */ }, "variables": { /* optional, see below */ }, "validation": { "status": "validated", "method": "checkScenario" | "checkAllScenarios" | "endpoint-up-down", "phase": "ok", "up_ms": 0, "down_ms": 0 }}| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | Stable identifier. Must match the scenario name used in the LLM-facing docs. |
description | string | yes | Human-readable summary of the scenario state. |
create | object | yes | The model graph passed to the SDK’s createScenario / up flow. A flat map: keys are model names, values are arrays of seeded rows. Rows link with _alias / _ref, and a _ref is resolved wherever it appears - including nested inside an object or an array. Extra keys are preserved. |
variables | object (map of name → definition) | no | Deprecated. Still accepted for recipes that already declare it, but no longer generated or documented. Use the built-in tokens below instead. |
validation | object | yes | Proof that the recipe was validated. All fields must be present. |
validation.status | literal string "validated" | yes | |
validation.method | one of "checkScenario", "checkAllScenarios", "endpoint-up-down" | yes | Which validator produced this result. |
validation.phase | literal string "ok" | yes | |
validation.up_ms | non-negative integer | no | Milliseconds the up phase took. |
validation.down_ms | non-negative integer | no | Milliseconds the down phase took. |
Built-in tokens
Every value in create must be concrete, with exactly two exceptions. These tokens are written in double braces and need no declaration - Autonoma substitutes them when it provisions the scenario.
| Token | Value |
|---|---|
{{testRunId}} | The id of this provisioning run - the same value Autonoma sends the Environment Factory as the up request’s testRunId, so your recipe and your handler agree on one identity. |
{{testRunShortId}} | An 8-character hash of {{testRunId}}, for columns too short to hold a UUID (usernames, slugs, subdomains). |
They exist for one reason: concurrent runs of the same scenario would otherwise collide on unique columns. Use them anywhere a value must be unique per run, including inside a longer string.
"User": [ { "_alias": "admin", "email": "admin-{{testRunShortId}}@acme.test", "externalId": "{{testRunId}}" }]Any other {{token}} is rejected on upload - there is no general variable mechanism.
Full example
{ "version": 1, "source": { "discoverPath": "autonoma/discover.json", "scenariosPath": "autonoma/scenarios.md" }, "validationMode": "sdk-check", "recipes": [ { "name": "adminWithTwoProjects", "description": "Organization with an admin user and two projects.", "create": { "Organization": [{ "_alias": "org-1", "name": "Acme" }], "User": [ { "email": "admin-{{testRunShortId}}@acme.test", "role": "admin", "organizationId": { "_ref": "org-1" } } ], "Project": [ { "name": "Alpha", "organizationId": { "_ref": "org-1" } }, { "name": "Beta", "organizationId": { "_ref": "org-1" } } ] }, "validation": { "status": "validated", "method": "checkScenario", "phase": "ok", "up_ms": 142, "down_ms": 61 } } ]}Common rejection reasons
Invalid input: expected string, received undefinedundersource.discoverPath- thesourceobject is missingdiscoverPath. BothdiscoverPathandscenariosPathare required.These tokens resolve to nothing: {{<name>}}. Autonoma only substitutes {{testRunId}} and {{testRunShortId}}; replace the rest with concrete values.- thecreategraph uses a{{token}}that is not one of the two built-ins. Replace it with a concrete value.These _ref targets match no _alias in the graph- a row references an alias no row declares. Add the_alias, or drop the reference.The create graph must map each model name to an array of records- a model maps to a scalar or a bare array of non-objects. The Environment Factory rejects the whole seed in that shape.versionmust be literal1- don’t send"1"or"1.0". Integer1.recipesmust contain at least 1 element - empty arrays are rejected.validation.status/validation.phasemismatch - both are fixed literals ("validated"/"ok"). Any other value fails.
Related
- Test Planner - how scenarios are designed and recipes are validated before upload.
- Environment Factory - the
up/down/discoverSDK that consumes these recipes at runtime.