{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://flowmarkup.dev/schema/FlowMarkup-0.9.0.schema.json",
  "title": "FlowMarkup 0.9.0 Process Definition",
  "description": "Composed FlowMarkup schema: directives + action schemas. Validates .flowmarkup.yaml files or the YAML frontmatter document of .flowmarkup.md literate files (not the full Markdown file). Generated by compose-schema.py; subsequently patched by hand — re-running the generator will overwrite manual fixes.",
  "type": "object",
  "required": [
    "flowmarkup"
  ],
  "properties": {
    "flowmarkup": {
      "$ref": "#/$defs/flow"
    }
  },
  "$defs": {
    "flow": {
      "description": "Root flow definition with metadata, contracts, variables, and step body",
      "type": "object",
      "required": [
        "title",
        "requires",
        "do"
      ],
      "additionalProperties": false,
      "properties": {
        "title": {
          "type": "string",
          "minLength": 1,
          "maxLength": 10000
        },
        "version": {
          "type": "integer",
          "minimum": 1,
          "default": 1
        },
        "description": {
          "type": "string",
          "description": "Short one-line description of this flow. Plain text only, no Markdown.",
          "maxLength": 500
        },
        "documentation": {
          "type": "string",
          "description": "Full Markdown documentation for this flow. For .flowmarkup.md files, the Markdown body is automatically loaded here if this field is omitted in frontmatter."
        },
        "timeout": {
          "$ref": "#/$defs/timeout"
        },
        "input": {
          "$ref": "#/$defs/paramContract"
        },
        "output": {
          "$ref": "#/$defs/outputContract"
        },
        "yields": {
          "$ref": "#/$defs/yieldsContract"
        },
        "throws": {
          "type": "array",
          "maxItems": 10000,
          "items": {
            "oneOf": [
              {
                "type": "string",
                "minLength": 1,
                "pattern": "^[A-Z].*Error$"
              },
              {
                "$ref": "#/$defs/errorDeclaration"
              },
              {
                "$ref": "#/$defs/errorHierarchy"
              }
            ]
          }
        },
        "triggers": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/triggerDef"
          },
          "minItems": 1,
          "maxItems": 10000,
          "description": "Trigger definitions that start new flow instances on events, cron schedules, or human-readable schedules."
        },
        "events": {
          "type": "object",
          "description": "Typed event contracts. Keys are event type names (snake_case). Values declare the expected payload shape. SA rules verify emit.data and waitFor.capture against these declarations.",
          "propertyNames": {
            "$ref": "#/$defs/eventName"
          },
          "additionalProperties": {
            "$ref": "#/$defs/eventDeclaration"
          },
          "maxProperties": 500
        },
        "requires": {
          "type": "object",
          "description": "REQUIRED. Declares the capabilities this flow requires from its caller. Every flow MUST include requires: (use requires: {} for no capabilities). Enables load-time validation and self-documentation. If the caller's effective caps don't satisfy requires, throws MissingCapabilityError before execution begins. Omitting a category = NONE for that category.",
          "additionalProperties": false,
          "properties": {
            "ENV": {
              "description": "Environment variable capability. Array of ENV.* key names this flow may read.",
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "minItems": 1
            },
            "CONTEXT": {
              "description": "Context variable scope capability. Per-key access control (aligned with GLOBAL model). Array form: listed keys get read-write access. Object form: separate read/write key lists. Omitting = NONE (no CONTEXT access).",
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "minLength": 1
                  },
                  "minItems": 1,
                  "description": "Per-key access: array of CONTEXT.* key names this flow may read and write."
                },
                {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "read": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "minLength": 1
                      },
                      "minItems": 1
                    },
                    "write": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "minLength": 1
                      },
                      "minItems": 1
                    }
                  }
                }
              ]
            },
            "GLOBAL": {
              "description": "Global variable scope capability. Per-key access control. Array form: listed keys get read-write access. Object form: separate read/write per-key lists. Omitting = NONE (no GLOBAL access).",
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "minLength": 1
                  },
                  "minItems": 1,
                  "description": "Per-key access: array of GLOBAL.* key names this flow may read and write."
                },
                {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "read": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "minLength": 1
                      },
                      "minItems": 1
                    },
                    "write": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "minLength": 1
                      },
                      "minItems": 1
                    }
                  }
                }
              ]
            },
            "SERVICES": {
              "description": "Services this flow requires. Array of service alias names, or typed object form.",
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "minLength": 1
                  },
                  "minItems": 1
                },
                {
                  "type": "object",
                  "minProperties": 1,
                  "additionalProperties": {
                    "$ref": "#/$defs/requiresServiceEntry"
                  }
                }
              ]
            },
            "SUBFLOWS": {
              "description": "Sub-flow invocation capability. true = can invoke sub-flows. Omitting = cannot invoke sub-flows.",
              "const": true
            },
            "REQUEST": {
              "description": "Outbound HTTP request capability (request action and HTTP-based service calls). Array of allowed origins/URL patterns. Does not gate exec or mail network access.",
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "minItems": 1
            },
            "EXEC": {
              "description": "System command execution capability. Array of executable names or paths.",
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "minItems": 1
            },
            "MAIL": {
              "description": "Email sending capability. true = unrestricted, or per-recipient domain/address array. Array items: '@domain.com' (domain allowlist) or 'user@domain.com' (exact address).",
              "oneOf": [
                {
                  "const": true
                },
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "minLength": 1
                  },
                  "minItems": 1
                }
              ]
            },
            "RUNTIME": {
              "description": "Runtime environment information capability. Grants access to RUNTIME.* scope (OS, engine, platform metadata). Deny-by-default.",
              "const": true
            },
            "SECRET": {
              "description": "Secrets this flow requires. Deny-by-default: the flow cannot access any secret not listed here. Array of secret names.",
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "minItems": 1
            },
            "STORAGE": {
              "description": "Storage capability. Deny-by-default. Keys are aliases (engine/parent-provisioned, matching ^[a-z][a-z0-9_]*$) or URL patterns (starting with a known scheme: s3://, sftp://, smb://, ftp://, file://). Array form grants full access; object form restricts per-key operations, with optional path prefixes for alias keys. SECURITY (CWE-22): file:// storage is restricted to configured storage roots. Engines MUST reject file:// URLs that resolve outside the storage root after symlink resolution. file:// SHOULD be disabled in multi-tenant deployments.",
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "minLength": 1
                  },
                  "minItems": 1,
                  "description": "Full access to all operations on listed aliases or URL patterns."
                },
                {
                  "type": "object",
                  "minProperties": 1,
                  "propertyNames": {
                    "pattern": "^([a-z][a-z0-9_]*|(s3|sftp|smb|ftp|file)://.*)$"
                  },
                  "additionalProperties": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "enum": [
                            "get",
                            "put",
                            "delete",
                            "list",
                            "info",
                            "exists",
                            "mkdir",
                            "copy",
                            "move",
                            "transfer"
                          ]
                        },
                        "minItems": 1,
                        "description": "Per-key operation list (for alias or URL-pattern keys)."
                      },
                      {
                        "type": "object",
                        "required": [
                          "operations"
                        ],
                        "additionalProperties": false,
                        "properties": {
                          "operations": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "get",
                                "put",
                                "delete",
                                "list",
                                "info",
                                "exists",
                                "mkdir",
                                "copy",
                                "move",
                                "transfer"
                              ]
                            },
                            "minItems": 1
                          },
                          "paths": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "minLength": 1
                            },
                            "minItems": 1,
                            "description": "Path prefix patterns. '*' matches any suffix. For alias keys only (URL-pattern keys embed path restrictions in the key)."
                          }
                        },
                        "description": "Per-alias operations + path prefix restrictions (alias keys only; URL-pattern keys use per-key operation list form)."
                      }
                    ]
                  },
                  "description": "Per-key restrictions: operation lists or operations + path prefixes."
                }
              ]
            },
            "SSH": {
              "description": "SSH remote command execution capability. Deny-by-default. Map-only form: keys are aliases (matching ^[a-z][a-z0-9_]*$) or hostnames (containing . or :), values are per-key command allowlists. A command allowlist is always required.",
              "type": "object",
              "minProperties": 1,
              "propertyNames": {
                "pattern": "^([a-z][a-z0-9_]*|.*[.:].*)$"
              },
              "additionalProperties": {
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1
                },
                "minItems": 1,
                "description": "Per-alias or per-host command allowlist."
              }
            },
            "RESOURCES": {
              "description": "Filesystem resources this flow requires. Deny-by-default. Array of resource names, or typed object form with individual resource contracts.",
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "minLength": 1
                  },
                  "minItems": 1
                },
                {
                  "type": "object",
                  "minProperties": 1,
                  "propertyNames": {
                    "pattern": "^[a-z][a-z0-9_]*$"
                  },
                  "additionalProperties": {
                    "$ref": "#/$defs/requiresResourceEntry"
                  }
                }
              ]
            }
          }
        },
        "services": {
          "type": "object",
          "description": "Flow-defined service instances. Keys become SERVICES.<alias> in CEL. Alias names must not conflict with engine-provisioned service names (ConfigurationError at load time). Services are initialized lazily on first call use.",
          "propertyNames": {
            "pattern": "^[a-z][a-z0-9_]*$"
          },
          "additionalProperties": {
            "$ref": "#/$defs/serviceDefinition"
          }
        },
        "rateLimit": {
          "$ref": "#/$defs/rateLimitSpec",
          "description": "Flow-level rate limit. Restricts how often this flow can be invoked. Scope defaults to GLOBAL (across all engine instances)."
        },
        "circuitBreaker": {
          "$ref": "#/$defs/circuitBreakerDefaultsSpec",
          "description": "Flow-level circuit breaker. Rejects invocations when the circuit is OPEN. Scope defaults to GLOBAL."
        },
        "idempotencyKey": {
          "$ref": "#/$defs/expression",
          "description": "Idempotency key for flow invocations (CEL expression producing a string). Engine uses this key to deduplicate concurrent or repeated invocations with the same key value. Key uniqueness scope is engine-wide. Dedup window is engine-configurable. Evaluated at invocation time before vars/const init — only ENV.*, SECRET.*, GLOBAL.*, CONTEXT.*, and flow input parameters are in scope."
        },
        "transaction": {
          "description": "Implicit sequential group over do:. Forks variable scopes on entry; commits atomically on success, discards on error (after rollback handlers). true = all scopes; named scope = fork only that scope. Syntactic sugar for wrapping do: in a top-level group: { transaction: ..., do: [...] }. catch: and finally: execute outside the implicit group.",
          "oneOf": [
            {
              "type": "boolean",
              "const": true
            },
            {
              "type": "string",
              "enum": [
                "GLOBAL",
                "CONTEXT",
                "LOCAL"
              ]
            }
          ]
        },
        "onRollbackError": {
          "description": "Policy when a rollback handler fails. CONTINUE (default): log and proceed. FAIL: stop and raise RollbackFailedError. Applies to rollback: handlers on child action steps; most useful when transaction: is set.",
          "type": "string",
          "enum": [
            "CONTINUE",
            "FAIL"
          ],
          "default": "CONTINUE"
        },
        "locking": {
          "description": "Lock strategy for the implicit transaction group. PESSIMISTIC (default): acquire per-variable locks at do: entry — no ConflictError possible. OPTIMISTIC: snapshot-based validation at commit — ConflictError on conflict. Only valid when transaction: is set.",
          "type": "string",
          "enum": [
            "PESSIMISTIC",
            "OPTIMISTIC"
          ],
          "default": "PESSIMISTIC"
        },
        "types": {
          "type": "object",
          "description": "Named type registry. Keys are PascalCase type names. Values are JSON Schema objects (inline or $ref to external file).",
          "additionalProperties": {
            "type": "object"
          },
          "propertyNames": {
            "pattern": "^[A-Z][a-zA-Z0-9]*$"
          },
          "maxProperties": 500
        },
        "vars": {
          "$ref": "#/$defs/vars"
        },
        "const": {
          "$ref": "#/$defs/vars",
          "description": "Readonly data elements. All entries are implicitly $readonly: true. Initialized before vars: and do:. CEL expression values MUST be prefixed with = (e.g., =ENV.API_BASE). Non-= strings are literals. Only ENV.*, SECRET.*, GLOBAL.*, CONTEXT.*, RUNTIME.* in scope (SA-INIT-1). Writing to a const name at runtime is SA-CONST-1 hard error."
        },
        "functions": {
          "type": "object",
          "description": "User-defined flow-local CEL functions. Keys are function names (^[a-z][a-zA-Z0-9_]*$). Each value is a functionDef with params and body. Functions are flow-scoped; not shared across flows (V1). Param type names are schema-validated; runtime type conformance is enforced by the engine. Arity at call sites is checked by SA-FN-6.",
          "propertyNames": {
            "pattern": "^[a-z][a-zA-Z0-9_]*$"
          },
          "additionalProperties": {
            "$ref": "#/$defs/functionDef"
          },
          "maxProperties": 500
        },
        "defaults": {
          "$ref": "#/$defs/stepDefaults",
          "description": "Flow-level step defaults. Cross-cutting properties (retry, timeout, rateLimit, circuitBreaker) inherited by all descendant action steps. Overridden at any inner scope (group defaults) or at the step level."
        },
        "examples": {
          "type": "object",
          "description": "Named input→output example pairs for documentation and testing. Keys are snake_case example names. Values are exampleSet objects with optional input:, output:, and notes: fields. SA rules SA-EX-1..4 validate key conformance against the declared flow input/output contract.",
          "propertyNames": {
            "pattern": "^[a-z][a-z0-9_]*$"
          },
          "additionalProperties": {
            "$ref": "#/$defs/exampleSet"
          }
        },
        "onVersionChange": {
          "$ref": "#/$defs/migrationStepListOrStep",
          "description": "Version migration handler. A step list executed during checkpoint resume when the flow content has changed since the checkpoint was taken. Receives a 'MIGRATION' binding with OLD_VERSION, NEW_VERSION, OLD_HASH, NEW_HASH, LOCATION, STEP_CURSOR, CHECKPOINTED_AT fields. May modify checkpoint variables before resume. A throw inside the handler wraps as MigrationError (non-catchable). If absent: FlowVersionError on version change (current behavior). A return inside the handler gracefully terminates the flow instance (finally: runs, checkpoint discarded, return value becomes flow output). Forbidden: exec, ssh, request, mail, storage, call, run (SA-VER-7), wait, waitFor, waitUntil (SA-VER-3), yield (SA-VER-5), async: true (SA-VER-4)."
        },
        "do": {
          "$ref": "#/$defs/stepListOrStep"
        },
        "catch": {
          "$ref": "#/$defs/catchHandler"
        },
        "finally": {
          "$ref": "#/$defs/stepListOrStep"
        }
      }
    },
    "catchHandler": {
      "description": "Error handler map. Keys are error type names; 'default' is the catch-all (must be last). Values are step lists or conditional handlers. Processed top-to-bottom (YAML key order).",
      "type": "object",
      "minProperties": 1,
      "propertyNames": {
        "pattern": "^([A-Z].*Error|default)$"
      },
      "additionalProperties": {
        "oneOf": [
          {
            "$ref": "#/$defs/stepListOrStep"
          },
          {
            "type": "object",
            "description": "Conditional catch clause. Condition evaluated with ERROR binding. If false, error falls through to next matching clause.",
            "properties": {
              "condition": {
                "$ref": "#/$defs/expression",
                "description": "CEL guard for this catch clause. ERROR binding available. If false, error falls through."
              },
              "do": {
                "$ref": "#/$defs/stepListOrStep",
                "description": "Handler steps when condition is true."
              }
            },
            "required": [
              "condition",
              "do"
            ],
            "additionalProperties": false
          }
        ]
      }
    },
    "flowmarkupKind": {
      "description": "Structural kind: UPPERCASE constant (STRING, TEXT, MARKDOWN, NUMBER, INTEGER, BOOLEAN, BINARY, ARRAY, MAP, DIRECTORY, ANY, EMAIL, URL, URI, UUID, IP, FILENAME). Compound shorthands (JSON, YAML, XML, CSV, TSV) expand to $kind + $type at the paramDef level, not here. EMAIL, URL, URI, UUID, IP, FILENAME are format-validated string subtypes (SPECIFICATION.md §2.4) — they expand to STRING with a built-in format constraint and are included here for direct validation.",
      "type": "string",
      "enum": [
        "STRING",
        "TEXT",
        "MARKDOWN",
        "NUMBER",
        "INTEGER",
        "BOOLEAN",
        "BINARY",
        "ARRAY",
        "MAP",
        "DIRECTORY",
        "ANY",
        "EMAIL",
        "URL",
        "URI",
        "UUID",
        "IP",
        "FILENAME"
      ]
    },
    "flowmarkupFormat": {
      "description": "Regex pattern for value validation. Only valid on $kind: STRING, $kind: TEXT, or $kind: MARKDOWN.",
      "type": "string",
      "minLength": 1
    },
    "charset": {
      "description": "IANA character encoding name (e.g. utf-8, shift_jis, windows-1252, iso-8859-1). Case-insensitive. Validated at runtime via Charset.forName().",
      "type": "string",
      "minLength": 1,
      "pattern": "^[a-zA-Z][a-zA-Z0-9._-]*$"
    },
    "mimeType": {
      "description": "MIME content/serialization type (e.g. application/json, text/plain, image/png).",
      "type": "string",
      "pattern": "^[a-zA-Z][a-zA-Z0-9+.-]*/[a-zA-Z0-9][a-zA-Z0-9!#$&+.-]*$"
    },
    "paramContract": {
      "description": "Named params contract. Structured form: required:/optional: split. Flat form: all params at top level; params with $default are optional, all others are required.",
      "oneOf": [
        {
          "description": "Structured form: required/optional split.",
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "required": {
              "type": "object",
              "propertyNames": {
                "pattern": "^[a-z][a-z0-9_]*$"
              },
              "additionalProperties": {
                "$ref": "#/$defs/paramDefOrType"
              }
            },
            "optional": {
              "type": "object",
              "propertyNames": {
                "pattern": "^[a-z][a-z0-9_]*$"
              },
              "additionalProperties": {
                "$ref": "#/$defs/paramDefOrType"
              }
            }
          }
        },
        {
          "description": "Flat form: all params at top level. Params with $default are optional; all others are required.",
          "type": "object",
          "not": {
            "anyOf": [
              {
                "required": [
                  "required"
                ]
              },
              {
                "required": [
                  "optional"
                ]
              }
            ]
          },
          "propertyNames": {
            "pattern": "^[a-z][a-z0-9_]*$"
          },
          "additionalProperties": {
            "$ref": "#/$defs/paramDefOrType"
          }
        }
      ]
    },
    "outputContract": {
      "description": "Flow-level output contract. Multi-param contract (required/optional split), flat form (all params at top level, all required), or single-value contract (just a type declaration).",
      "oneOf": [
        {
          "description": "Multi-param contract: named output params with required/optional split.",
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "required": {
              "type": "object",
              "propertyNames": {
                "pattern": "^[a-z][a-z0-9_]*$"
              },
              "additionalProperties": {
                "$ref": "#/$defs/paramDefOrType"
              }
            },
            "optional": {
              "type": "object",
              "propertyNames": {
                "pattern": "^[a-z][a-z0-9_]*$"
              },
              "additionalProperties": {
                "$ref": "#/$defs/paramDefOrType"
              }
            }
          }
        },
        {
          "description": "Flat form: all output params at top level, all required.",
          "type": "object",
          "not": {
            "anyOf": [
              {
                "required": [
                  "required"
                ]
              },
              {
                "required": [
                  "optional"
                ]
              },
              {
                "required": [
                  "$kind"
                ]
              },
              {
                "required": [
                  "$schema"
                ]
              }
            ]
          },
          "propertyNames": {
            "pattern": "^[a-z][a-z0-9_]*$"
          },
          "additionalProperties": {
            "$ref": "#/$defs/paramDefOrType"
          }
        },
        {
          "description": "Single-value contract: the flow returns one typed value (e.g. image/png, text). Use 'return: <expression>' inside the flow, and 'output: variable_name' on call steps.",
          "$ref": "#/$defs/paramDefOrType"
        }
      ]
    },
    "schemaRef": {
      "description": "Reference to a structural type schema. String form: PascalCase type name (resolved from flow types:). Object form: inline JSON Schema or $ref to external file.",
      "oneOf": [
        {
          "type": "string",
          "pattern": "^[A-Z][a-zA-Z0-9]*$",
          "description": "Type name — resolved from flow-level types:"
        },
        {
          "type": "object",
          "description": "Inline JSON Schema or { $ref: 'path' } to external schema file"
        }
      ]
    },
    "requiresServiceEntry": {
      "description": "Typed service requirement. Short form: provider protocol ID string. Long form: object with protocol, optional operations list, and notes.",
      "oneOf": [
        {
          "type": "string",
          "minLength": 1
        },
        {
          "type": "object",
          "required": [
            "provider"
          ],
          "additionalProperties": false,
          "properties": {
            "provider": {
              "type": "string",
              "minLength": 1
            },
            "operations": {
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "minItems": 1,
              "description": "Operation names that must appear in services.<alias>.meta.operations at load time."
            },
            "_notes_": {
              "type": "string",
              "maxLength": 10000
            }
          }
        }
      ]
    },
    "requiresResourceEntry": {
      "description": "Resource requirement contract. Files are the default; use $kind: DIRECTORY for directory resources. Content type is inferred from extension at runtime unless $type (MIME) is explicitly set. No $default = required; $default: null = optional (RESOURCES.<key> is null if not provided).",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "$kind": {
          "type": "string",
          "enum": [
            "DIRECTORY"
          ],
          "description": "Resource kind. Only DIRECTORY needs to be declared — files are the default. Engine throws MissingCapabilityError if the provided resource kind does not match."
        },
        "$type": {
          "$ref": "#/$defs/mimeType",
          "description": "Explicit MIME content type override for file resources (e.g. text/plain, image/png). When omitted, content type is inferred from the file extension at runtime."
        },
        "$name": {
          "type": "string",
          "minLength": 1,
          "description": "Filename constraint. Literal string: resource's actual filename must match and must be a valid filename (SA-RES-3). CEL expression (=expr prefix): dynamic constraint evaluated at load time."
        },
        "$default": {
          "type": "null",
          "description": "Marks this resource as optional. Only null is accepted. If the engine does not provide this resource, RESOURCES.<key> evaluates to null instead of throwing MissingCapabilityError."
        },
        "_notes_": {
          "type": "string",
          "description": "Human-readable description of this resource's purpose.",
          "maxLength": 10000
        },
        "$charset": {
          "$ref": "#/$defs/charset",
          "description": "Character encoding for this resource file. Overrides auto-detection when the engine loads the resource content."
        }
      }
    },
    "serviceDefinition": {
      "type": "object",
      "required": [
        "provider"
      ],
      "additionalProperties": false,
      "description": "Flow-level service instance. provider: is a static language-agnostic ID (not CEL) — same carve-out as exec.command and throw.error. properties: string values are CEL expressions evaluated on first invocation of any operation on that service (lazy init); non-strings pass through. Once evaluated, property values are cached for the flow instance's lifetime. The schema permits any value; runtime enforces restriction to ENV.*, SECRET.*, and literals (SA-SVC-4). Flow variables are not available at init time.",
      "properties": {
        "provider": {
          "type": "string",
          "minLength": 1
        },
        "_notes_": {
          "type": "string",
          "maxLength": 10000
        },
        "properties": {
          "type": "object",
          "additionalProperties": {}
        }
      }
    },
    "paramDef": {
      "description": "Parameter definition with optional kind, type, format, and default value metadata. $kind is required unless inferable from $schema (schema root type → kind), $enum (always STRING), or a literal $default (YAML scalar type → kind).",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "$kind": {
          "$ref": "#/$defs/varKindExpr",
          "description": "FlowMarkup kind. Bare UPPERCASE constant or compound shorthand (JSON, YAML, XML, CSV, TSV). Compound shorthands expand to $kind + $type at load time."
        },
        "$type": {
          "$ref": "#/$defs/mimeType",
          "description": "MIME content/serialization type (e.g. application/json, text/plain). Optional — some $kind values have implicit MIME types (TEXT → text/plain, MARKDOWN → text/markdown, BINARY → application/octet-stream)."
        },
        "$format": {
          "$ref": "#/$defs/flowmarkupFormat",
          "description": "Regex pattern for value validation. Only valid on $kind: STRING, $kind: TEXT, or $kind: MARKDOWN."
        },
        "_notes_": {
          "type": "string",
          "description": "Human-readable documentation for this element. Plain text or Markdown.",
          "maxLength": 10000
        },
        "$default": {},
        "$nullable": {
          "type": "boolean",
          "description": "Whether this parameter may be null. Auto-derived if omitted: required without default → false; optional without default → true; default: null → true; default: <value> → false. Explicit true/false overrides auto-derivation."
        },
        "$schema": {
          "$ref": "#/$defs/schemaRef"
        },
        "$enum": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "minItems": 1,
          "uniqueItems": true,
          "description": "Allowed string values. Shorthand for JSON Schema enum constraint. Mutually exclusive with $schema."
        },
      },
      "not": {
        "required": [
          "$schema",
          "$enum"
        ]
      }
    },
    "paramDefOrType": {
      "description": "Parameter definition: bare UPPERCASE kind string (shorthand for {$kind: X}), compound shorthand (JSON → {$kind: MAP, $type: application/json}, YAML → {$kind: MAP, $type: application/yaml}, XML → {$kind: MAP, $type: application/xml}, CSV → {$kind: ARRAY, $type: text/csv}, TSV → {$kind: ARRAY, $type: text/tab-separated-values}), or full paramDef object.",
      "oneOf": [
        {
          "$ref": "#/$defs/flowmarkupKind"
        },
        {
          "type": "string",
          "enum": [
            "JSON",
            "YAML",
            "XML",
            "CSV",
            "TSV"
          ]
        },
        {
          "$ref": "#/$defs/paramDef"
        }
      ]
    },
    "errorDeclaration": {
      "description": "Error type declaration with optional parent hierarchy and data fields",
      "type": "object",
      "required": [
        "$kind"
      ],
      "additionalProperties": false,
      "properties": {
        "$kind": {
          "type": "string",
          "minLength": 1,
          "pattern": "^[A-Z].*Error$",
          "description": "Error type name (e.g. PaymentDeclinedError). NOT a flowmarkupKind enum — bare string identifier."
        },
        "$parent": {
          "type": "string",
          "minLength": 1,
          "pattern": "^[A-Z].*Error$",
          "description": "Parent error type (single inheritance). IS-A relationship. Must be declared in the same throws: list (SA-ERR-2). Cannot be self-referential (SA-ERR-4). Cycles forbidden (SA-ERR-1). Catch clauses matching the parent type also catch this type."
        },
        "_notes_": {
          "type": "string",
          "description": "Human-readable documentation for this element. Plain text or Markdown.",
          "maxLength": 10000
        },
        "data": {
          "type": "object",
          "additionalProperties": {
            "$ref": "#/$defs/paramDefOrType"
          }
        }
      }
    },
    "errorHierarchy": {
      "description": "Nested error hierarchy. Key is parent error type name, value is array of child declarations (string, errorDeclaration, or recursive errorHierarchy).",
      "type": "object",
      "minProperties": 1,
      "maxProperties": 1,
      "patternProperties": {
        "^[A-Z].*Error$": {
          "type": "array",
          "minItems": 1,
          "items": {
            "oneOf": [
              {
                "type": "string",
                "minLength": 1,
                "pattern": "^[A-Z].*Error$"
              },
              {
                "$ref": "#/$defs/errorDeclaration"
              },
              {
                "$ref": "#/$defs/errorHierarchy"
              }
            ]
          }
        }
      },
      "additionalProperties": false
    },
    "eventDeclaration": {
      "type": "object",
      "additionalProperties": false,
      "description": "Typed event contract. Declares the payload shape for an event type. data: uses paramContract (required/optional split) for SA verification of emit.data and waitFor.capture.",
      "properties": {
        "_notes_": {
          "type": "string",
          "description": "Human-readable documentation. Plain text or Markdown.",
          "maxLength": 10000
        },
        "data": {
          "$ref": "#/$defs/paramContract"
        }
      }
    },
    "paramSpec": {
      "type": "object",
      "required": [
        "$kind"
      ],
      "additionalProperties": false,
      "description": "Type annotation for a function parameter. $kind is validated against FlowMarkup kinds at schema time; conformance is enforced at runtime by the engine.",
      "properties": {
        "$kind": {
          "$ref": "#/$defs/flowmarkupKind",
          "description": "FlowMarkup kind for this parameter (STRING, NUMBER, BOOLEAN, INTEGER, ARRAY, MAP, ANY, etc.). Schema-validated; runtime-enforced by the engine."
        },
        "_notes_": {
          "type": "string",
          "maxLength": 10000
        }
      }
    },
    "paramSpecOrType": {
      "description": "Function parameter definition: bare UPPERCASE kind string (shorthand for {$kind: X}), compound shorthand (JSON → {$kind: MAP}, YAML → {$kind: MAP}, XML → {$kind: MAP}, CSV → {$kind: ARRAY}, TSV → {$kind: ARRAY}), or full paramSpec object.",
      "oneOf": [
        {
          "$ref": "#/$defs/flowmarkupKind"
        },
        {
          "type": "string",
          "enum": [
            "JSON",
            "YAML",
            "XML",
            "CSV",
            "TSV"
          ]
        },
        {
          "$ref": "#/$defs/paramSpec"
        }
      ]
    },
    "functionDef": {
      "type": "object",
      "required": [
        "params",
        "body"
      ],
      "additionalProperties": false,
      "description": "User-defined flow-local CEL function. params: array of names (untyped) or object of name → paramSpec (typed — type names schema-validated, conformance runtime-enforced). body: always-CEL (= prefix required). Params and all flow CEL bindings are in scope. No recursion.",
      "properties": {
        "params": {
          "oneOf": [
            {
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^[a-z][a-zA-Z0-9_]*$"
              },
              "uniqueItems": true,
              "description": "Untyped: list of parameter names."
            },
            {
              "type": "object",
              "propertyNames": {
                "pattern": "^[a-z][a-zA-Z0-9_]*$"
              },
              "additionalProperties": {
                "$ref": "#/$defs/paramSpecOrType"
              },
              "description": "Typed: map of parameter name to type annotation. Type names are schema-validated."
            }
          ]
        },
        "body": {
          "$ref": "#/$defs/expression",
          "description": "CEL expression (= prefix required). Params and all CEL bindings in scope. No recursion."
        },
        "_notes_": {
          "type": "string",
          "maxLength": 10000
        }
      }
    },
    "exampleSet": {
      "type": "object",
      "additionalProperties": false,
      "description": "A named example pairing input values with expected output values. Used for documentation, tooling hints, and as seed data for the testing framework. input: and output: are both optional — an example may show input-only, output-only, or both. Values are pure data (no CEL expressions). SA-EX-1..4 validate key conformance against the flow's declared input/output contract.",
      "properties": {
        "_notes_": {
          "type": "string",
          "description": "Human-readable description of this example.",
          "maxLength": 10000
        },
        "input": {
          "type": "object",
          "description": "Input values. Keys must match declared flow input params (SA-EX-1, SA-EX-2)."
        },
        "output": {
          "description": "Expected output values. Map form for multi-param output; scalar for single-value output flows (SA-EX-3, SA-EX-4)."
        }
      }
    },
    "vars": {
      "type": "object",
      "description": "Flow-local state variables. String CEL expressions MUST be prefixed with = (e.g., =counter + 1, =ENV.API_KEY). Strings without = are treated as literal values. Non-strings (number, boolean, null, array, plain object) pass through as-is. Use $kind to declare the FlowMarkup kind explicitly. Variable names MUST NOT use GLOBAL., CONTEXT., LOCAL., RUNTIME., or ENV. prefixes (those scopes are engine-managed). Variable names MUST NOT be the reserved binding names LOCAL, CONTEXT, GLOBAL, RUNTIME, SERVICES, ERROR, EVENT, MIGRATION, YIELD, RESULT, RESULTS, or RESOURCES.",
      "propertyNames": {
        "allOf": [
          {
            "not": {
              "anyOf": [
                {
                  "enum": [
                    "LOCAL",
                    "CONTEXT",
                    "GLOBAL",
                    "RUNTIME",
                    "SERVICES",
                    "ERROR",
                    "EVENT",
                    "MIGRATION",
                    "YIELD",
                    "RESULT",
                    "RESULTS",
                    "RESOURCES"
                  ]
                },
                {
                  "pattern": "^(GLOBAL|CONTEXT|LOCAL|ENV|RUNTIME|SECRET)\\."
                }
              ]
            }
          },
          { "not": { "pattern": "\\$" } },
          { "not": { "pattern": "-" } }
        ]
      },
      "additionalProperties": {
        "oneOf": [
          {
            "type": "string"
          },
          {
            "type": "number"
          },
          {
            "type": "boolean"
          },
          {
            "type": "null"
          },
          {
            "type": "array"
          },
          {
            "$ref": "#/$defs/typedVar"
          },
          {
            "type": "object",
            "not": {
              "required": [
                "$kind"
              ]
            }
          }
        ]
      },
      "maxProperties": 1000
    },
    "typedVar": {
      "description": "Data element declaration with type metadata and optional initial value",
      "type": "object",
      "required": [
        "$kind"
      ],
      "if": {
        "not": {
          "properties": { "$nullable": { "const": true } },
          "required": ["$nullable"]
        }
      },
      "then": {
        "required": ["$value"]
      },
      "additionalProperties": false,
      "properties": {
        "$kind": {
          "$ref": "#/$defs/varKindExpr",
          "description": "Structural kind. Bare UPPERCASE constant (STRING, MAP, BINARY, …) or CEL = expression for runtime values (=my_kind). See varKindExpr for the full constant list."
        },
        "$type": {
          "$ref": "#/$defs/mimeType",
          "description": "MIME content/serialization type (e.g. application/json, text/plain). Optional — some $kind values have implicit MIME types."
        },
        "$format": {
          "$ref": "#/$defs/flowmarkupFormat",
          "description": "Regex pattern for value validation. Only valid on $kind: STRING, $kind: TEXT, or $kind: MARKDOWN."
        },
        "$name": {
          "$ref": "#/$defs/expression",
          "description": "CEL expression. Filename hint. Used when the value originated from a file or multipart upload."
        },
        "$schema": {
          "$ref": "#/$defs/schemaRef",
          "description": "Static type reference — NOT a CEL expression. PascalCase type name (resolved from types:) or inline JSON Schema object."
        },
        "$encoding": {
          "$ref": "#/$defs/encodingExpr",
          "description": "Encoding for binary data stored in YAML. Bare constant (BASE64, BASE64URL, HEX) or CEL = expression. UTF variants are $charset, not $encoding."
        },
        "$value": {
          "description": "Initial value. Follows the universal = prefix rule: a string with = prefix is a CEL expression (e.g. =GLOBAL.count + 1); a plain string without = is a string literal (e.g. a base64 payload or a URL). Non-string YAML values (number, boolean, null, array, object) pass through as-is."
        },
        "$nullable": {
          "type": "boolean",
          "description": "Whether this variable may hold null. Auto-derived if omitted: true when $value is null, false otherwise. Explicit true/false overrides auto-derivation."
        },
        "$secret": {
          "type": "boolean",
          "description": "Treat as local SecretValue — opaque, non-exportable. All SA-SECRET rules apply. Cannot be logged, returned, emitted, yielded, interpolated, compared, or assigned to another variable. Only valid use: pass to action params. Implies $exportable: false."
        },
        "$exportable": {
          "type": "boolean",
          "description": "Whether value can be logged, returned, emitted, yielded. Defaults to true. Set false to block output at boundaries while still allowing CEL computation. Weaker than $secret (which also blocks comparison and interpolation)."
        },
        "$readonly": {
          "type": "boolean",
          "description": "Whether this data element is immutable after initial assignment. Equivalent to declaring in const:. Writing to a $readonly data element is SA-CONST-1 ERROR. When a readonly value is copied via set:, the target does NOT inherit $readonly — only the value is copied."
        },
        "$charset": {
          "$ref": "#/$defs/charset",
          "description": "Character encoding for text content loaded from external sources (RESOURCES, HTTP, exec). Overrides auto-detection. Not applicable to BINARY kind (SA-CHARSET-1) or when $encoding is set (SA-CHARSET-2)."
        },
        "$sanitized": {
          "type": "boolean",
          "description": "Marks value as having passed through application-level sanitization. Suppresses SA-MAIL-15 and SA-XML-3. Does not affect taint propagation. Engines MUST NOT auto-set. SA-TAINT-6 (ERROR) flags usage without provenance."
        },
        "$sanitizedBy": {
          "type": "string",
          "minLength": 1,
          "description": "Declares sanitization provenance (e.g. htmlSanitize, service:sanitizer.clean). When present, engine validates provenance at load time. Required when $sanitized: true to avoid SA-TAINT-6."
        },
        "$trusted": {
          "type": "boolean",
          "description": "Marks value as originating from a trusted source. Exempts from SA-EXEC-11, SA-SSH-10, SA-STORAGE-18, SA-XML-3 taint checks. Does not bypass $secret/$exportable restrictions. SA-TAINT-7 (ERROR) flags usage on untrusted-source values."
        },
        "$declassify": {
          "oneOf": [
            {
              "type": "boolean",
              "const": true,
              "description": "Boolean shorthand: marks this variable as intentionally declassified. Requires declassify.policy != DENY."
            },
            {
              "type": "object",
              "description": "Object form with transform provenance and optional multi-secret lineage.",
              "required": ["via"],
              "additionalProperties": false,
              "properties": {
                "via": {
                  "type": "string",
                  "minLength": 1,
                  "description": "Name of the one-way transform function used (e.g., sha256, hmac, bcrypt). Must be from the normative safe-transform list in SECRETS.md §7.4."
                },
                "sources": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "minLength": 1
                  },
                  "minItems": 1,
                  "uniqueItems": true,
                  "description": "Source secret references (e.g., SECRET.a, SECRET.b) for multi-secret lineage. Required when the declassified value derives from multiple secret sources. SA-TAINT-9a enforces completeness."
                }
              }
            }
          ],
          "description": "Removes $secret taint flag from a variable that is the direct result of a one-way transformation. Disabled by default (engine declassify.policy: DENY). See FLOWMARKUP-SECRETS.md §7.4 and SA-SECRET-24/SA-TAINT-8/SA-TAINT-9a."
        }
      }
    },
    "expression": {
      "type": "string",
      "minLength": 1,
      "maxLength": 10000,
      "description": "A CEL expression (prefixed with =) or template string (using {{ }}). Maximum 10,000 characters to prevent resource exhaustion (ReDoS, memory exhaustion) during parsing and evaluation. Engines MUST reject expressions exceeding this limit at load time."
    },
    "durationOrExpr": {
      "description": "Duration literal (500ms, 30s, 5m, 1h, 1d), integer milliseconds, or CEL expression evaluating to a duration string or integer ms. Duration literals matching ^\\d+(ms|[smhd])$ are matched first; non-matching strings are evaluated as CEL.",
      "oneOf": [
        {
          "$ref": "#/$defs/expression"
        },
        {
          "type": "integer",
          "minimum": 0
        }
      ]
    },
    "durationPositiveOrExpr": {
      "description": "Positive integer milliseconds (>=1) or CEL expression (= prefix) evaluating to a duration string or positive integer ms. Zero is not a valid value. Duration string literals (e.g. '5m') are only accepted in the cacheHint shorthand form at the cacheHintSpec root, not in this field.",
      "oneOf": [
        {
          "$ref": "#/$defs/expression"
        },
        {
          "type": "integer",
          "minimum": 1
        }
      ]
    },
    "timeout": {
      "description": "Timeout. String: CEL expression evaluating to a duration string or integer ms. Duration literals matching ^\\d+(ms|[smhd])$ are matched first (e.g. '5s', '2m' without inner quotes). Non-matching strings are evaluated as CEL (e.g., \"'5s'\", timeout_var, \"base * 2\"). Integer: milliseconds.",
      "oneOf": [
        {
          "$ref": "#/$defs/expression"
        },
        {
          "type": "integer",
          "minimum": 0
        }
      ]
    },
    "onTimeout": {
      "description": "Timeout handler. Executes the do: steps after the specified duration elapses. Used on group and action steps to define timeout behavior inline.",
      "type": "object",
      "required": [
        "after",
        "do"
      ],
      "additionalProperties": false,
      "properties": {
        "after": {
          "description": "Duration after which the on-timeout handler fires. Duration literal (e.g. '5s', '2m'), integer (ms), or CEL expression.",
          "$ref": "#/$defs/timeout"
        },
        "do": {
          "$ref": "#/$defs/stepListOrStep"
        }
      }
    },
    "stepDefaults": {
      "description": "Default cross-cutting properties inherited by descendant action steps. A step-level key fully replaces the in-scope default (no sub-key merging). Use retry: null or timeout: null on a step to explicitly disable the default. Does not apply to directive steps (group, if, forEach, etc.).",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "retry": {
          "$ref": "#/$defs/retryPolicy"
        },
        "timeout": {
          "$ref": "#/$defs/timeout"
        },
        "rateLimit": {
          "$ref": "#/$defs/rateLimitSpec"
        },
        "circuitBreaker": {
          "$ref": "#/$defs/circuitBreakerDefaultsSpec"
        },
        "cacheHint": {
          "$ref": "#/$defs/cacheHintSpec",
          "description": "Advisory cache hint for storage actions. Silently ignored by non-storage action steps. Replace-not-merge semantics (same as retry/timeout). Set to null to disable an inherited default."
        }
      }
    },
    "stepList": {
      "description": "Ordered list of executable steps (directives and actions). Limited to 10,000 steps per list to prevent resource exhaustion during parsing.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/step"
      },
      "minItems": 1,
      "maxItems": 10000
    },
    "stepListOrStep": {
      "description": "One or more steps. Array for multiple steps; single step object (unwrapped) for exactly one step.",
      "oneOf": [
        {
          "$ref": "#/$defs/stepList"
        },
        {
          "$ref": "#/$defs/step"
        }
      ]
    },
    "migrationStep": {
      "description": "A step permitted inside onVersionChange: handlers. Excludes action steps (exec, ssh, request, mail, storage, call, run — SA-VER-7) and blocking/streaming directives (wait, waitFor, waitUntil, yield — SA-VER-3, SA-VER-5). Note: this restriction applies to direct children of onVersionChange: only; steps nested inside control-flow containers (forEach, while, etc.) are not enforced at the schema level and require SA-VER-7 static analysis.",
      "allOf": [
        { "$ref": "#/$defs/step" },
        {
          "not": {
            "anyOf": [
              { "required": ["call"] },
              { "required": ["exec"] },
              { "required": ["mail"] },
              { "required": ["request"] },
              { "required": ["run"] },
              { "required": ["storage"] },
              { "required": ["ssh"] },
              { "required": ["wait"] },
              { "required": ["waitFor"] },
              { "required": ["waitUntil"] },
              { "required": ["yield"] }
            ]
          }
        }
      ]
    },
    "migrationStepListOrStep": {
      "description": "One or more steps permitted inside onVersionChange: handlers. Excludes action steps and blocking/streaming directives (SA-VER-3, SA-VER-5, SA-VER-7).",
      "oneOf": [
        {
          "type": "array",
          "items": { "$ref": "#/$defs/migrationStep" },
          "minItems": 1,
          "maxItems": 10000
        },
        { "$ref": "#/$defs/migrationStep" }
      ]
    },
    "step": {
      "description": "A single step. One key = the step type. Directives (built-in control flow) have fixed schemas. Actions (SPI-loaded) have input/output/errors contract.",
      "oneOf": [
        {
          "$ref": "#/$defs/groupStep"
        },
        {
          "$ref": "#/$defs/ifStep"
        },
        {
          "$ref": "#/$defs/forEachStep"
        },
        {
          "$ref": "#/$defs/whileStep"
        },
        {
          "$ref": "#/$defs/repeatStep"
        },
        {
          "$ref": "#/$defs/tryStep"
        },
        {
          "$ref": "#/$defs/setStep"
        },
        {
          "$ref": "#/$defs/logStep"
        },
        {
          "$ref": "#/$defs/logWarnStep"
        },
        {
          "$ref": "#/$defs/logErrorStep"
        },
        {
          "$ref": "#/$defs/switchStep"
        },
        {
          "$ref": "#/$defs/assertStep"
        },
        {
          "$ref": "#/$defs/throwStep"
        },
        {
          "$ref": "#/$defs/returnStep"
        },
        {
          "$ref": "#/$defs/yieldStep"
        },
        {
          "$ref": "#/$defs/waitStep"
        },
        {
          "$ref": "#/$defs/breakStep"
        },
        {
          "$ref": "#/$defs/continueStep"
        },
        {
          "$ref": "#/$defs/emitStep"
        },
        {
          "$ref": "#/$defs/waitForStep"
        },
        {
          "$ref": "#/$defs/lockStep"
        },
        {
          "$ref": "#/$defs/waitUntilStep"
        },
        {
          "$ref": "#/$defs/cancelStep"
        },
        {
          "$ref": "#/$defs/parallelStep"
        },
        {
          "$ref": "#/$defs/raceStep"
        },
        {
          "description": "SPI step schema: service invocation. Use 'call' for services; use 'run' for sub-flows.",
          "type": "object",
          "required": [
            "call"
          ],
          "additionalProperties": false,
          "properties": {
            "call": {
              "type": "object",
              "required": [
                "service",
                "operation"
              ],
              "additionalProperties": false,
              "properties": {
                "service": {
                  "$ref": "#/$defs/serviceRef",
                  "description": "Service to invoke. Bare alias name (e.g., db, payment — resolved to SERVICES.<name>) or CEL expression (e.g., =SERVICES.db, =SERVICES[ENV.AI_SERVICE])."
                },
                "operation": {
                  "$ref": "#/$defs/operationRef",
                  "description": "Operation (method) to invoke on the service. Think of the service as a class instance and the operation as the method call. Bare method name (e.g., query, generate, charge) or CEL expression for dynamic dispatch (e.g., =selected_op). Must be a valid programming-language identifier."
                },
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "condition": {
                  "$ref": "#/$defs/expression"
                },
                "timeout": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/timeout"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "retry": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/retryPolicy"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "onTimeout": {
                  "$ref": "#/$defs/onTimeout"
                },
                "rateLimit": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/rateLimitSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "circuitBreaker": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/circuitBreakerSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "async": {
                  "type": "boolean",
                  "description": "Fire-and-forget: invoke the service without waiting for completion. result:, retry:, and step-level timeout are ignored when async: true. Errors are logged but do not propagate to the caller.",
                  "default": false
                },
                "params": {
                  "$ref": "#/$defs/stepParams"
                },
                "result": {
                  "$ref": "#/$defs/stepResult"
                },
                "errors": {
                  "$ref": "#/$defs/errorList"
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "rollback": {
                  "$ref": "#/$defs/stepListOrStep",
                  "description": "Compensation handler. Executed in reverse completion order when an error propagates past the enclosing transaction group or flow boundary. Runs before transaction variable rollback so handlers can access output variables."
                },
                "onYield": {
                  "$ref": "#/$defs/onYield"
                },
                "catch": {
                  "$ref": "#/$defs/catchHandler"
                }
              },
              "not": {
                "required": ["rollback", "catch"]
              }
            }
          }
        },
        {
          "description": "System command execution action. Runs a process directly (no shell). command: is a carved-out plain string (not CEL) for static capability validation. args: items are CEL expressions.",
          "type": "object",
          "required": [
            "exec"
          ],
          "additionalProperties": false,
          "properties": {
            "exec": {
              "type": "object",
              "required": [
                "command"
              ],
              "additionalProperties": false,
              "properties": {
                "command": {
                  "type": "string",
                  "minLength": 1,
                  "pattern": "^[^=]",
                  "description": "Executable name or path. Plain string — NOT a CEL expression. Enables static capability validation at load time. No shell interpretation: no glob expansion, pipes, or redirects."
                },
                "args": {
                  "description": "Command arguments. Array form: each item MUST be a string (CEL expression or literal). String form: single CEL expression evaluating to a list of strings at runtime. Each argument is limited to 32,768 characters and the array is limited to 1,000 items to prevent resource exhaustion.",
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "maxLength": 32768,
                        "description": "Argument value. Must be a string: CEL expressions are prefixed with =, literals are plain strings. Objects, arrays, numbers, and booleans are not permitted as command-line arguments."
                      },
                      "maxItems": 1000,
                      "description": "Array of string arguments. Each item is a CEL expression (= prefix) or a plain string literal."
                    },
                    {
                      "$ref": "#/$defs/expression",
                      "description": "CEL expression evaluating to a list of string arguments at runtime."
                    }
                  ]
                },
                "workingDir": {
                  "$ref": "#/$defs/expression",
                  "description": "Working directory for the child process (CEL expression)."
                },
                "env": {
                  "type": "object",
                  "additionalProperties": {},
                  "description": "Additional environment variables for the child process. Keys are variable names (plain strings), values are CEL expressions or pass-through literals. SecretValues are resolved by the engine before process creation. Recommended over exec.args for passing credentials. Merged with inherited process environment; exec-level values override.",
                  "maxProperties": 100
                },
                "stdin": {
                  "$ref": "#/$defs/expression",
                  "description": "Data written to the process stdin (written once, pipe closed — not interactive). CEL expression."
                },
                "parseAs": {
                  "$ref": "#/$defs/parseAsExpr"
                },
                "charset": {
                  "$ref": "#/$defs/charset",
                  "description": "Character encoding for stdout. Overrides auto-detection. Only meaningful for text-based parseAs (not BINARY)."
                },
                "stderrCharset": {
                  "$ref": "#/$defs/charset",
                  "description": "Character encoding for stderr. Overrides auto-detection."
                },
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "condition": {
                  "$ref": "#/$defs/expression"
                },
                "timeout": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/timeout"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "retry": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/retryPolicy"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "onTimeout": {
                  "$ref": "#/$defs/onTimeout"
                },
                "rateLimit": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/rateLimitSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "circuitBreaker": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/circuitBreakerSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "async": {
                  "type": "boolean",
                  "description": "Fire-and-forget: launch the process without waiting for completion. result:, retry:, and step-level timeout are ignored when async: true. Errors are logged but do not propagate to the caller.",
                  "default": false
                },
                "result": {
                  "$ref": "#/$defs/stepResult"
                },
                "errors": {
                  "$ref": "#/$defs/errorList"
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "rollback": {
                  "$ref": "#/$defs/stepListOrStep",
                  "description": "Compensation handler. Executed in reverse completion order when an error propagates past the enclosing transaction group or flow boundary. Runs before transaction variable rollback so handlers can access output variables."
                },
                "onYield": {
                  "$ref": "#/$defs/onYield"
                },
                "catch": {
                  "$ref": "#/$defs/catchHandler"
                }
              },
              "not": {
                "required": ["rollback", "catch"]
              }
            }
          }
        },
        {
          "description": "Email sending action. Sends an email via SMTP. Capability-gated (deny-by-default): the engine must provision mail capability before any flow can use it. Engine-level SMTP configuration (host, port, credentials, default sender) is opaque to flows.",
          "type": "object",
          "required": [
            "mail"
          ],
          "additionalProperties": false,
          "properties": {
            "mail": {
              "type": "object",
              "required": [
                "subject"
              ],
              "additionalProperties": false,
              "anyOf": [
                {
                  "required": [
                    "to"
                  ]
                },
                {
                  "required": [
                    "cc"
                  ]
                },
                {
                  "required": [
                    "bcc"
                  ]
                }
              ],
              "properties": {
                "to": {
                  "description": "Primary recipients. CEL expression (single address) or array of CEL expressions (multiple addresses). Limited to 500 recipients to prevent abuse.",
                  "oneOf": [
                    {
                      "$ref": "#/$defs/expression"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/$defs/expression"
                      },
                      "minItems": 1,
                      "maxItems": 500
                    }
                  ]
                },
                "cc": {
                  "description": "CC recipients. CEL expression (single address) or array of CEL expressions (multiple addresses). Limited to 500 recipients to prevent abuse.",
                  "oneOf": [
                    {
                      "$ref": "#/$defs/expression"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/$defs/expression"
                      },
                      "minItems": 1,
                      "maxItems": 500
                    }
                  ]
                },
                "bcc": {
                  "description": "BCC recipients. CEL expression (single address) or array of CEL expressions (multiple addresses). Limited to 500 recipients to prevent abuse.",
                  "oneOf": [
                    {
                      "$ref": "#/$defs/expression"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/$defs/expression"
                      },
                      "minItems": 1,
                      "maxItems": 500
                    }
                  ]
                },
                "from": {
                  "$ref": "#/$defs/expression",
                  "description": "Sender address (CEL expression). Falls back to engine-level default sender when omitted."
                },
                "replyTo": {
                  "$ref": "#/$defs/expression",
                  "description": "Reply-To address (CEL expression)."
                },
                "subject": {
                  "$ref": "#/$defs/expression",
                  "description": "Email subject line (CEL expression)."
                },
                "body": {
                  "description": "Email body. String form: CEL expression for plain text. Object form: text and/or html parts (multipart/alternative when both provided).",
                  "oneOf": [
                    {
                      "$ref": "#/$defs/expression",
                      "description": "Plain text body (CEL expression)."
                    },
                    {
                      "type": "object",
                      "additionalProperties": false,
                      "anyOf": [
                        {
                          "required": [
                            "text"
                          ]
                        },
                        {
                          "required": [
                            "html"
                          ]
                        }
                      ],
                      "properties": {
                        "text": {
                          "$ref": "#/$defs/expression",
                          "description": "Plain text part (CEL expression). Sent as text/plain."
                        },
                        "html": {
                          "$ref": "#/$defs/expression",
                          "description": "HTML part (CEL expression). Sent as text/html."
                        }
                      }
                    }
                  ]
                },
                "attachments": {
                  "description": "File attachments. Each item is either a CEL expression (variable reference with $kind/$name metadata) or a full object with explicit properties.",
                  "type": "array",
                  "maxItems": 50,
                  "items": {
                    "oneOf": [
                      {
                        "$ref": "#/$defs/expression",
                        "description": "Variable reference (CEL). Engine uses the value's MIME $type (or falls back to $kind mapping) for content-type and $name for filename."
                      },
                      {
                        "type": "object",
                        "required": [
                          "data"
                        ],
                        "additionalProperties": false,
                        "properties": {
                          "data": {
                            "$ref": "#/$defs/expression",
                            "description": "Attachment data (CEL expression)."
                          },
                          "name": {
                            "$ref": "#/$defs/expression",
                            "description": "Filename (CEL expression). Falls back to value's $name."
                          },
                          "contentType": {
                            "$ref": "#/$defs/expression",
                            "description": "MIME content type (CEL expression). Falls back to value's MIME $type or $kind mapping."
                          },
                          "inline": {
                            "description": "Whether this attachment is inline (for cid: references in HTML body). Boolean or CEL expression.",
                            "oneOf": [
                              {
                                "type": "boolean"
                              },
                              {
                                "$ref": "#/$defs/expression"
                              }
                            ],
                            "default": false
                          },
                          "contentId": {
                            "$ref": "#/$defs/expression",
                            "description": "Content-ID for inline attachments (CEL expression). Used for cid: references in HTML body."
                          }
                        }
                      }
                    ]
                  }
                },
                "headers": {
                  "type": "object",
                  "additionalProperties": {},
                  "description": "Custom email headers. Keys are header names (plain strings), values are CEL expressions. Engine-managed headers (From, To, Cc, Bcc, Subject, Date, Message-ID, MIME-Version, Content-Type) cannot be overridden.",
                  "maxProperties": 100
                },
                "priority": {
                  "description": "Email priority. Static: HIGH, NORMAL, LOW. CEL: expression (=) evaluating to one of those strings at runtime. Sets X-Priority and Importance headers.",
                  "oneOf": [
                    {
                      "type": "string",
                      "enum": [
                        "HIGH",
                        "NORMAL",
                        "LOW"
                      ]
                    },
                    {
                      "type": "string",
                      "pattern": "^=",
                      "minLength": 2,
                      "maxLength": 10000
                    }
                  ]
                },
                "smtp": {
                  "type": "object",
                  "additionalProperties": false,
                  "description": "SMTP server override. All properties are optional — partial overrides are merged with engine-level defaults. Requires mail capability.",
                  "properties": {
                    "host": {
                      "$ref": "#/$defs/expression",
                      "description": "SMTP server hostname (CEL expression)."
                    },
                    "port": {
                      "description": "SMTP server port. Integer (1-65535) or CEL expression.",
                      "oneOf": [
                        {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 65535
                        },
                        {
                          "$ref": "#/$defs/expression"
                        }
                      ]
                    },
                    "tls": {
                      "description": "TLS mode for SMTP connection. STARTTLS (default, recommended) or SMTPS.",
                      "oneOf": [
                        {
                          "type": "string",
                          "enum": [
                            "STARTTLS",
                            "SMTPS"
                          ]
                        },
                        {
                          "type": "string",
                          "pattern": "^=",
                          "minLength": 2,
                          "maxLength": 10000
                        }
                      ],
                      "default": "STARTTLS"
                    },
                    "auth": {
                      "type": "object",
                      "additionalProperties": false,
                      "description": "SMTP authentication credentials.",
                      "properties": {
                        "username": {
                          "$ref": "#/$defs/expression",
                          "description": "SMTP username (CEL expression)."
                        },
                        "password": {
                          "$ref": "#/$defs/expression",
                          "description": "SMTP password (CEL expression). Use SECRET.* or ENV.* for credentials."
                        }
                      }
                    },
                    "connectTimeout": {
                      "$ref": "#/$defs/timeout",
                      "description": "Connection timeout for the SMTP server."
                    }
                  }
                },
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "condition": {
                  "$ref": "#/$defs/expression"
                },
                "timeout": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/timeout"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "retry": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/retryPolicy"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "onTimeout": {
                  "$ref": "#/$defs/onTimeout"
                },
                "rateLimit": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/rateLimitSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "circuitBreaker": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/circuitBreakerSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "async": {
                  "type": "boolean",
                  "description": "Fire-and-forget: send the email without waiting for SMTP confirmation. result:, retry:, and step-level timeout are ignored when async: true. SMTP errors are logged but do not propagate to the caller.",
                  "default": false
                },
                "result": {
                  "$ref": "#/$defs/stepResult"
                },
                "errors": {
                  "$ref": "#/$defs/errorList"
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "rollback": {
                  "$ref": "#/$defs/stepListOrStep",
                  "description": "Compensation handler. Executed in reverse completion order when an error propagates past the enclosing transaction group or flow boundary. Runs before transaction variable rollback so handlers can access output variables."
                },
                "catch": {
                  "$ref": "#/$defs/catchHandler"
                }
              },
              "not": {
                "required": ["rollback", "catch"]
              }
            }
          }
        },
        {
          "description": "HTTP request action. Sends an HTTP request and maps the response (status, headers, body) to flow variables.",
          "type": "object",
          "required": [
            "request"
          ],
          "additionalProperties": false,
          "properties": {
            "request": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "method"
              ],
              "oneOf": [
                {
                  "required": [
                    "url"
                  ]
                },
                {
                  "required": [
                    "host"
                  ]
                }
              ],
              "properties": {
                "method": {
                  "description": "HTTP method. Static: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS. CEL: expression (=) evaluating to one of those strings at runtime (e.g., \"=is_mutation ? POST : GET\").",
                  "oneOf": [
                    {
                      "type": "string",
                      "enum": [
                        "GET",
                        "POST",
                        "PUT",
                        "DELETE",
                        "PATCH",
                        "HEAD",
                        "OPTIONS"
                      ]
                    },
                    {
                      "type": "string",
                      "pattern": "^=",
                      "minLength": 2,
                      "maxLength": 10000
                    }
                  ]
                },
                "url": {
                  "allOf": [
                    { "$ref": "#/$defs/expression" },
                    { "maxLength": 8192 }
                  ],
                  "description": "HTTP request URL. When using CEL expressions, the resolved URL MUST have scheme http:// or https:// only. The engine validates against the REQUEST capability's allowed-origin list and rejects private IP addresses (RFC 1918, loopback, link-local) to prevent SSRF."
                },
                "scheme": {
                  "type": "string",
                  "enum": [
                    "http",
                    "https"
                  ],
                  "default": "https",
                  "description": "URL scheme. Defaults to https (recommended). SECURITY (CWE-319): http:// (unencrypted) SHOULD NOT be used in production. http:// transmits data in cleartext including headers, authentication credentials, and request/response bodies. Engines SHOULD warn when http:// URLs are used outside localhost. Use https:// for all production traffic. SA-REQ-11 flags http:// with authentication as an error."
                },
                "host": {
                  "$ref": "#/$defs/expression",
                  "description": "Hostname with optional port (CEL expression). Used with decomposed URL form."
                },
                "path": {
                  "$ref": "#/$defs/expression",
                  "description": "URL path (CEL expression). Used with decomposed URL form. Defaults to '/' when omitted."
                },
                "query": {
                  "type": "object",
                  "description": "Query parameters. Keys are parameter names, values are CEL expressions or pass-through literals. The engine URL-encodes values. Allowed with both url and decomposed forms. SecretValues in query values are resolved by the engine and redacted from logged URLs.",
                  "additionalProperties": {},
                  "maxProperties": 100
                },
                "headers": {
                  "type": "object",
                  "description": "HTTP request headers. Keys are header names, values are CEL expressions or pass-through literals. SecretValues in header values are resolved by the engine and redacted from logs. Limited to 200 headers to prevent resource exhaustion.",
                  "additionalProperties": {},
                  "maxProperties": 200
                },
                "auth": {
                  "type": "object",
                  "additionalProperties": false,
                  "description": "Authentication convenience. Avoids manual Authorization header construction.",
                  "oneOf": [
                    {
                      "required": [
                        "bearer"
                      ]
                    },
                    {
                      "required": [
                        "basic"
                      ]
                    }
                  ],
                  "properties": {
                    "bearer": {
                      "$ref": "#/$defs/expression",
                      "description": "Bearer token (CEL expression). Engine prepends 'Bearer ' prefix. Sets Authorization: Bearer <token>. Do NOT include 'Bearer ' in the value. SecretValues are accepted and resolved by the engine."
                    },
                    "basic": {
                      "type": "object",
                      "required": [
                        "username",
                        "password"
                      ],
                      "additionalProperties": false,
                      "description": "Basic auth credentials. Engine base64-encodes. Sets Authorization: Basic <encoded>.",
                      "properties": {
                        "username": {
                          "$ref": "#/$defs/expression",
                          "description": "Username (CEL expression). SecretValues are accepted and resolved by the engine."
                        },
                        "password": {
                          "$ref": "#/$defs/expression",
                          "description": "Password (CEL expression). SecretValues are accepted and resolved by the engine."
                        }
                      }
                    }
                  }
                },
                "body": {
                  "description": "Request body. String shorthand (CEL expression, Content-Type must be set via headers) or structured form (json/urlencoded/multipart/raw).",
                  "oneOf": [
                    {
                      "type": "string",
                      "description": "Shorthand: CEL expression producing the body content. Use \"''\" for an empty body. Content-Type must be set via headers."
                    },
                    {
                      "type": "object",
                      "additionalProperties": false,
                      "oneOf": [
                        {
                          "required": [
                            "json"
                          ]
                        },
                        {
                          "required": [
                            "urlencoded"
                          ]
                        },
                        {
                          "required": [
                            "multipart"
                          ]
                        },
                        {
                          "required": [
                            "raw"
                          ]
                        }
                      ],
                      "properties": {
                        "json": {
                          "description": "JSON body. Auto-sets Content-Type: application/json. Value is a CEL expression (string) or structured literal (object/array/scalar). String values in nested objects/arrays are recursively evaluated as CEL."
                        },
                        "urlencoded": {
                          "type": "object",
                          "description": "URL-encoded form body. Auto-sets Content-Type: application/x-www-form-urlencoded. Keys are field names, values are CEL expressions or pass-through literals.",
                          "additionalProperties": {},
                          "maxProperties": 100
                        },
                        "multipart": {
                          "type": "array",
                          "description": "Multipart form body. Auto-sets Content-Type: multipart/form-data.",
                          "minItems": 1,
                          "items": {
                            "type": "object",
                            "required": [
                              "name",
                              "data"
                            ],
                            "additionalProperties": false,
                            "properties": {
                              "name": {
                                "type": "string",
                                "minLength": 1,
                                "description": "Part field name (CEL expression)."
                              },
                              "filename": {
                                "type": "string",
                                "minLength": 1,
                                "description": "Filename for file uploads (CEL expression)."
                              },
                              "contentType": {
                                "type": "string",
                                "minLength": 1,
                                "description": "MIME type of this part (CEL expression). Defaults to application/octet-stream for binary, text/plain for strings."
                              },
                              "data": {
                                "description": "Part data (CEL expression if string, or pass-through literal)."
                              }
                            }
                          }
                        },
                        "raw": {
                          "type": "string",
                          "minLength": 1,
                          "description": "Raw body content (CEL expression). Content-Type must be set via headers."
                        }
                      }
                    }
                  ]
                },
                "cookies": {
                  "type": "object",
                  "description": "Request cookies. Keys are cookie names, values are CEL expressions or pass-through literals. Sent as Cookie header.",
                  "additionalProperties": {},
                  "maxProperties": 100
                },
                "expect": {
                  "type": "object",
                  "additionalProperties": false,
                  "description": "Response validation. Throws HttpError if the response does not match. Checked before output mapping.",
                  "properties": {
                    "status": {
                      "description": "Expected HTTP status code(s). Single integer (100–599), array of integers, or CEL expression string producing an integer or array of integers. Throws HttpError if response status is not in the list. Range constraints enforced at runtime when CEL.",
                      "oneOf": [
                        {
                          "type": "integer",
                          "minimum": 100,
                          "maximum": 599
                        },
                        {
                          "type": "array",
                          "items": {
                            "type": "integer",
                            "minimum": 100,
                            "maximum": 599
                          },
                          "minItems": 1
                        },
                        {
                          "$ref": "#/$defs/expression"
                        }
                      ]
                    },
                    "contentType": {
                      "type": "string",
                      "description": "Expected response Content-Type (static string, prefix match). E.g., 'application/json' matches 'application/json; charset=utf-8'. Throws HttpError if response Content-Type does not match."
                    }
                  }
                },
                "followRedirects": {
                  "description": "Follow HTTP 3xx redirects. Boolean literal or CEL expression string producing a boolean. Defaults to true.",
                  "oneOf": [
                    {
                      "$ref": "#/$defs/expression"
                    },
                    {
                      "type": "boolean"
                    }
                  ],
                  "default": true
                },
                "connectTimeout": {
                  "$ref": "#/$defs/timeout",
                  "description": "TCP connection timeout (CEL expression or integer ms). Throws ConnectionError if the connection cannot be established within this duration. Defaults to engine configuration."
                },
                "readTimeout": {
                  "$ref": "#/$defs/timeout",
                  "description": "Response read timeout (CEL expression or integer ms). Throws TimeoutError if the server does not begin sending a response within this duration after the request is sent. Defaults to engine configuration."
                },
                "maxResponseSize": {
                  "description": "Maximum response body size in bytes. Integer literal or CEL expression producing an integer. Throws HttpError if the response body exceeds this limit. Defaults to engine configuration.",
                  "oneOf": [
                    {
                      "type": "integer",
                      "minimum": 1
                    },
                    {
                      "$ref": "#/$defs/expression"
                    }
                  ]
                },
                "parseAs": {
                  "$ref": "#/$defs/parseAsExpr"
                },
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "condition": {
                  "$ref": "#/$defs/expression"
                },
                "async": {
                  "type": "boolean",
                  "description": "Fire-and-forget: send the request without waiting for a response. result:, retry:, and step-level timeout are ignored when async: true. Errors are logged but do not propagate to the caller.",
                  "default": false
                },
                "timeout": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/timeout"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "retry": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/retryPolicy"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "onTimeout": {
                  "$ref": "#/$defs/onTimeout"
                },
                "rateLimit": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/rateLimitSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "circuitBreaker": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/circuitBreakerSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "result": {
                  "$ref": "#/$defs/stepResult"
                },
                "errors": {
                  "$ref": "#/$defs/errorList"
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "rollback": {
                  "$ref": "#/$defs/stepListOrStep",
                  "description": "Compensation handler. Executed in reverse completion order when an error propagates past the enclosing transaction group or flow boundary. Runs before transaction variable rollback so handlers can access output variables."
                },
                "onYield": {
                  "$ref": "#/$defs/onYield"
                },
                "catch": {
                  "$ref": "#/$defs/catchHandler"
                }
              },
              "not": {
                "required": ["rollback", "catch"]
              }
            }
          }
        },
        {
          "description": "Sub-flow invocation with declarative capability security. Use 'run' for sub-flows; use 'call' for service invocation.",
          "type": "object",
          "required": [
            "run"
          ],
          "additionalProperties": false,
          "properties": {
            "run": {
              "type": "object",
              "required": [
                "flow"
              ],
              "additionalProperties": false,
              "properties": {
                "flow": {
                  "type": "string",
                  "maxLength": 8192,
                  "description": "Sub-flow reference: relative path, absolute path, HTTPS URL, registry ref, or git-backed URL. Supported git schemes: github://owner/repo/path (GitHub.com, public + private; // separator optional), gitlab://group/.../project//path (GitLab.com; // separator required), bitbucket://workspace/repo/path (Bitbucket Cloud; // separator optional), https://host/repo.git//path (generic git host via .git// separator). Append ?ref=VALUE to any git-backed URL to pin to a branch, tag, or commit SHA (e.g., github://org/repo/flow.yaml?ref=v1.0.0). Without .git//, HTTPS URLs are direct HTTP fetch. Credentials for git-backed flows are engine-managed (never in YAML). The special value CURRENT (the literal string \"CURRENT\") refers to the current flow, enabling self-invocation and recursive patterns without hardcoding the filename. CURRENT inherits all caller capabilities. integrity: has no effect when flow is CURRENT. Capability resolution: effective = requires(sub-flow) ∩ capabilities(caller) ∩ cap(run-step, if present). Every sub-flow MUST declare requires:. The sub-flow receives the intersection of what it declares, what the caller has, and any cap: restrictions."
                },
                "integrity": {
                  "type": "string",
                  "pattern": "^sha(256|384|512)-[A-Za-z0-9+/=]+$",
                  "description": "SRI-style integrity hash for remote flow verification. Engine verifies fetched content matches hash before execution. Format: sha256-<base64>, sha384-<base64>, or sha512-<base64>."
                },
                "handle": {
                  "type": "string",
                  "pattern": "^[a-z][a-z0-9_]*$",
                  "description": "Creates a FlowHandle binding for source filtering, status checks, and cancellation. Read-only — cannot be overwritten by set:. Cannot be logged, returned, emitted, or yielded (non-exportable by design). Most useful with async: true. FlowHandle properties (CEL, read-only): handle.status (RUNNING, COMPLETED, FAILED, CANCELLED)."
                },
                "cap": {
                  "description": "Capability restriction for the sub-flow. Object form: per-category allowlist — unstated categories default to NONE. INHERIT string: pass all caller capabilities through (sub-flow still bounded by its own requires:). Equivalent to omitting cap: but documents intent.",
                  "oneOf": [
                    {
                      "type": "string",
                      "enum": [
                        "INHERIT"
                      ],
                      "description": "Pass all caller capabilities through. Sub-flow still bounded by its own requires:."
                    },
                    {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "ENV": {
                          "description": "Environment variable capability. Array of var names, INHERIT (forward caller's entire grant), or CEL expression.",
                          "oneOf": [
                            {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1
                              },
                              "minItems": 1
                            },
                            {
                              "type": "string",
                              "enum": [
                                "INHERIT"
                              ],
                              "description": "Forward caller's entire ENV grant to the sub-flow."
                            },
                            {
                              "type": "string",
                              "pattern": "^=",
                              "minLength": 2,
                              "maxLength": 10000
                            }
                          ]
                        },
                        "CONTEXT": {
                          "description": "Context variable scope capability. Per-key access control, INHERIT (forward caller's entire grant), or CEL expression.",
                          "oneOf": [
                            {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1
                              },
                              "minItems": 1,
                              "description": "Per-key read-write access."
                            },
                            {
                              "type": "object",
                              "additionalProperties": false,
                              "properties": {
                                "read": {
                                  "type": "array",
                                  "items": {
                                    "type": "string",
                                    "minLength": 1
                                  },
                                  "minItems": 1
                                },
                                "write": {
                                  "type": "array",
                                  "items": {
                                    "type": "string",
                                    "minLength": 1
                                  },
                                  "minItems": 1
                                }
                              }
                            },
                            {
                              "type": "string",
                              "enum": [
                                "INHERIT"
                              ],
                              "description": "Forward caller's entire CONTEXT grant to the sub-flow."
                            },
                            {
                              "type": "string",
                              "pattern": "^=",
                              "minLength": 2,
                              "maxLength": 10000
                            }
                          ]
                        },
                        "GLOBAL": {
                          "description": "Global variable scope capability. Per-key access control, INHERIT (forward caller's entire grant), or CEL expression.",
                          "oneOf": [
                            {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1
                              },
                              "minItems": 1,
                              "description": "Per-key read-write access."
                            },
                            {
                              "type": "object",
                              "additionalProperties": false,
                              "properties": {
                                "read": {
                                  "type": "array",
                                  "items": {
                                    "type": "string",
                                    "minLength": 1
                                  },
                                  "minItems": 1
                                },
                                "write": {
                                  "type": "array",
                                  "items": {
                                    "type": "string",
                                    "minLength": 1
                                  },
                                  "minItems": 1
                                }
                              }
                            },
                            {
                              "type": "string",
                              "enum": [
                                "INHERIT"
                              ],
                              "description": "Forward caller's entire GLOBAL grant to the sub-flow."
                            },
                            {
                              "type": "string",
                              "pattern": "^=",
                              "minLength": 2,
                              "maxLength": 10000
                            }
                          ]
                        },
                        "SERVICES": {
                          "description": "Service invocation capability.",
                          "oneOf": [
                            {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1
                              },
                              "minItems": 1
                            },
                            {
                              "type": "object",
                              "minProperties": 1,
                              "description": "Remapping: keys are child alias names (snake_case); values are CEL expressions in parent scope resolving to service handles (e.g., SERVICES.analytics_db). Mapped service must be in parent's effective caps (SA-SVC-6).",
                              "propertyNames": {
                                "pattern": "^[a-z][a-z0-9_]*$"
                              },
                              "additionalProperties": {
                                "$ref": "#/$defs/expression"
                              }
                            },
                            {
                              "type": "string",
                              "pattern": "^=",
                              "minLength": 2,
                              "maxLength": 10000
                            }
                          ]
                        },
                        "SUBFLOWS": {
                          "description": "Sub-flow invocation capability. true = grant; false = explicit deny (same as omitting). Boolean; does not support INHERIT.",
                          "type": "boolean"
                        },
                        "REQUEST": {
                          "description": "Outbound HTTP request capability. Array of allowed origins/URL patterns, INHERIT (forward caller's entire grant), or CEL expression.",
                          "oneOf": [
                            {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1
                              },
                              "minItems": 1
                            },
                            {
                              "type": "string",
                              "enum": [
                                "INHERIT"
                              ],
                              "description": "Forward caller's entire REQUEST grant to the sub-flow."
                            },
                            {
                              "type": "string",
                              "pattern": "^=",
                              "minLength": 2,
                              "maxLength": 10000
                            }
                          ]
                        },
                        "EXEC": {
                          "description": "System command execution capability. Array of executable names, INHERIT (forward caller's entire grant), or CEL expression.",
                          "oneOf": [
                            {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1
                              },
                              "minItems": 1
                            },
                            {
                              "type": "string",
                              "enum": [
                                "INHERIT"
                              ],
                              "description": "Forward caller's entire EXEC grant to the sub-flow."
                            },
                            {
                              "type": "string",
                              "pattern": "^=",
                              "minLength": 2,
                              "maxLength": 10000
                            }
                          ]
                        },
                        "MAIL": {
                          "description": "Email sending capability. Boolean, per-recipient domain/address array, INHERIT (forward caller's entire grant), or CEL expression.",
                          "oneOf": [
                            {
                              "type": "boolean"
                            },
                            {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1
                              },
                              "minItems": 1
                            },
                            {
                              "type": "string",
                              "enum": [
                                "INHERIT"
                              ],
                              "description": "Forward caller's entire MAIL grant to the sub-flow."
                            },
                            {
                              "type": "string",
                              "pattern": "^=",
                              "minLength": 2,
                              "maxLength": 10000
                            }
                          ]
                        },
                        "RUNTIME": {
                          "description": "Runtime environment information capability. true = grant; false = explicit deny (same as omitting). Boolean; does not support INHERIT.",
                          "type": "boolean"
                        },
                        "SECRET": {
                          "description": "Secrets capability grant. Array of secret names to forward from caller's secret set, INHERIT (forward caller's entire secret grant — the set given by parent, not limited to own requires:), or CEL expression (=). Inline secret definitions are not supported (SA-SECRET-19, ERROR) — secrets MUST come from the engine's secrets provider.",
                          "oneOf": [
                            {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1
                              },
                              "minItems": 1
                            },
                            {
                              "type": "string",
                              "enum": [
                                "INHERIT"
                              ],
                              "description": "Forward caller's entire SECRET grant to the sub-flow."
                            },
                            {
                              "type": "string",
                              "pattern": "^=",
                              "minLength": 2,
                              "maxLength": 10000
                            }
                          ]
                        },
                        "RESOURCES": {
                          "description": "Filesystem resource capability. Array of resource names (pass-through by name), object form (remap/create: keys are child resource names, values are CEL expressions), INHERIT (forward caller's entire grant), or CEL expression.",
                          "oneOf": [
                            {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1
                              },
                              "minItems": 1
                            },
                            {
                              "type": "object",
                              "minProperties": 1,
                              "description": "Build RESOURCES map for sub-flow. Keys are child resource names (snake_case); values are CEL expressions — an existing Resource handle (=RESOURCES.name) or a path string variable (=ENV.PATH, =my_var). Engine wraps string results as new Resource objects named by the key.",
                              "propertyNames": {
                                "pattern": "^[a-z][a-z0-9_]*$"
                              },
                              "additionalProperties": {
                                "$ref": "#/$defs/expression"
                              }
                            },
                            {
                              "type": "string",
                              "enum": [
                                "INHERIT"
                              ],
                              "description": "Forward caller's entire RESOURCES grant to the sub-flow."
                            },
                            {
                              "type": "string",
                              "pattern": "^=",
                              "minLength": 2,
                              "maxLength": 10000
                            }
                          ]
                        },
                        "STORAGE": {
                          "description": "Storage capability grant. Array of aliases/URL patterns (full access), object form with per-key operation/path restrictions or alias provisioning, INHERIT (forward caller's entire grant), or CEL expression.",
                          "oneOf": [
                            {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1
                              },
                              "minItems": 1,
                              "description": "Full access to all operations on listed aliases or URL patterns."
                            },
                            {
                              "type": "object",
                              "minProperties": 1,
                              "additionalProperties": {
                                "oneOf": [
                                  {
                                    "type": "array",
                                    "items": {
                                      "type": "string",
                                      "enum": [
                                        "get",
                                        "put",
                                        "delete",
                                        "list",
                                        "info",
                                        "exists",
                                        "mkdir",
                                        "copy",
                                        "move",
                                        "transfer"
                                      ]
                                    },
                                    "minItems": 1,
                                    "description": "Per-key operation list (forward alias/URL pattern, restrict operations)."
                                  },
                                  {
                                    "type": "object",
                                    "required": [
                                      "operations"
                                    ],
                                    "additionalProperties": false,
                                    "properties": {
                                      "operations": {
                                        "type": "array",
                                        "items": {
                                          "type": "string",
                                          "enum": [
                                            "get",
                                            "put",
                                            "delete",
                                            "list",
                                            "info",
                                            "exists",
                                            "mkdir",
                                            "copy",
                                            "move",
                                            "transfer"
                                          ]
                                        },
                                        "minItems": 1
                                      },
                                      "paths": {
                                        "type": "array",
                                        "items": {
                                          "type": "string",
                                          "minLength": 1
                                        },
                                        "minItems": 1,
                                        "description": "Path prefix patterns. '*' matches any suffix. For alias keys only."
                                      }
                                    },
                                    "description": "Per-alias operations + path prefix restrictions (alias keys only)."
                                  },
                                  {
                                    "type": "string",
                                    "pattern": "^(s3|sftp|smb|ftp|file)://",
                                    "description": "Provision alias → literal URL (alias provisioning for sub-flows)."
                                  },
                                  {
                                    "type": "string",
                                    "pattern": "^=",
                                    "minLength": 2,
                                    "maxLength": 10000,
                                    "description": "Provision alias → CEL expression resolving to URL (alias provisioning for sub-flows)."
                                  },
                                  {
                                    "type": "string",
                                    "pattern": "^[a-z][a-z0-9_]*$",
                                    "description": "Forward engine alias as-is to sub-flow."
                                  }
                                ]
                              },
                              "description": "Per-key restrictions (operation lists, operations + paths) or alias provisioning (URL string, CEL expression, alias forwarding)."
                            },
                            {
                              "type": "string",
                              "enum": [
                                "INHERIT"
                              ],
                              "description": "Forward caller's entire STORAGE grant to the sub-flow."
                            },
                            {
                              "type": "string",
                              "pattern": "^=",
                              "minLength": 2,
                              "maxLength": 10000
                            }
                          ]
                        },
                        "SSH": {
                          "description": "SSH capability grant. Object form with per-alias or per-host command allowlists, alias provisioning, INHERIT (forward caller's entire grant), or CEL expression.",
                          "oneOf": [
                            {
                              "type": "object",
                              "minProperties": 1,
                              "additionalProperties": {
                                "oneOf": [
                                  {
                                    "type": "array",
                                    "items": {
                                      "type": "string",
                                      "minLength": 1
                                    },
                                    "minItems": 1,
                                    "description": "Per-alias or per-host command allowlist."
                                  },
                                  {
                                    "type": "string",
                                    "description": "Provision alias → hostname string or forward engine alias."
                                  },
                                  {
                                    "type": "string",
                                    "pattern": "^=",
                                    "minLength": 2,
                                    "maxLength": 10000,
                                    "description": "Provision alias → CEL expression resolving to hostname."
                                  }
                                ]
                              }
                            },
                            {
                              "type": "string",
                              "enum": [
                                "INHERIT"
                              ],
                              "description": "Forward caller's entire SSH grant to the sub-flow."
                            },
                            {
                              "type": "string",
                              "pattern": "^=",
                              "minLength": 2,
                              "maxLength": 10000
                            }
                          ]
                        }
                      }
                    }
                  ]
                },
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "condition": {
                  "$ref": "#/$defs/expression"
                },
                "timeout": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/timeout"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "retry": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/retryPolicy"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "onTimeout": {
                  "$ref": "#/$defs/onTimeout"
                },
                "rateLimit": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/rateLimitSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "circuitBreaker": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/circuitBreakerSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "async": {
                  "type": "boolean",
                  "description": "Fire-and-forget: invoke the sub-flow without waiting for completion. result:, retry:, and step-level timeout are ignored when async: true. Async sub-flows receive a deep clone of declared CONTEXT keys (no back-propagation).",
                  "default": false
                },
                "params": {
                  "$ref": "#/$defs/stepParams"
                },
                "result": {
                  "$ref": "#/$defs/stepResult"
                },
                "errors": {
                  "$ref": "#/$defs/errorList"
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "rollback": {
                  "$ref": "#/$defs/stepListOrStep",
                  "description": "Compensation handler. Executed in reverse completion order when an error propagates past the enclosing transaction group or flow boundary. Runs before transaction variable rollback so handlers can access output variables."
                },
                "onYield": {
                  "$ref": "#/$defs/onYield"
                },
                "catch": {
                  "$ref": "#/$defs/catchHandler"
                }
              },
              "not": {
                "required": ["rollback", "catch"]
              }
            }
          }
        },
        {
          "description": "Storage service operation. Capability-gated (deny-by-default): the engine must provision STORAGE capability before any flow can use it. Supports get, put, delete, list, info, exists, mkdir, copy, move, and cross-storage transfer.",
          "type": "object",
          "required": [
            "storage"
          ],
          "additionalProperties": false,
          "properties": {
            "storage": {
              "type": "object",
              "required": [
                "operation"
              ],
              "additionalProperties": false,
              "properties": {
                "url": {
                  "type": "string",
                  "minLength": 1,
                  "description": "Storage resource URL or alias. Supported schemes: s3:// (recommended for cloud), sftp:// (encrypted), smb:// (Windows shares — use Kerberos authentication), ftp:// (WARNING: unencrypted, credentials sent in cleartext — use sftp:// instead), file:// (local filesystem — requires engine-level sandboxing, subject to symlink TOCTOU risks). SECURITY (CWE-22): file:// storage is restricted to configured storage roots. Engines MUST reject file:// URLs that resolve outside the storage root after symlink resolution. file:// SHOULD be disabled in multi-tenant deployments. CEL expressions are supported (prefix with =)."
                },
                "operation": {
                  "description": "Storage operation. Static: get, put, delete, list, info, exists, mkdir, copy, move, transfer. CEL: expression (=) evaluating to one of those strings at runtime.",
                  "oneOf": [
                    {
                      "type": "string",
                      "enum": [
                        "get",
                        "put",
                        "delete",
                        "list",
                        "info",
                        "exists",
                        "mkdir",
                        "copy",
                        "move",
                        "transfer"
                      ]
                    },
                    {
                      "type": "string",
                      "pattern": "^=",
                      "minLength": 2,
                      "maxLength": 10000
                    }
                  ]
                },
                "path": {
                  "$ref": "#/$defs/expression",
                  "description": "Relative file/directory path within the storage endpoint (CEL expression). Optional when full path is in url:; REQUIRED when url: is an alias or base URL. When both url: and path: are present, path is appended to the resolved URL. The engine validates paths against traversal attacks (rejects ../ sequences). INVALID for transfer (SA-STORAGE-7)."
                },
                "data": {
                  "$ref": "#/$defs/expression",
                  "description": "File content for put (CEL expression). Accepts: BINARY (raw bytes), STRING/TEXT (auto-encoded), MAP/ARRAY (auto-serialized), or a RESOURCES handle. INVALID for transfer (SA-STORAGE-7)."
                },
                "parseAs": {
                  "$ref": "#/$defs/parseAsExpr"
                },
                "overwrite": {
                  "description": "Defaults to false to prevent accidental data loss. When false, put operations on existing files raise a StorageError. Explicit overwrite: true is REQUIRED to replace existing files. Flows SHOULD default to safe (non-destructive) behavior; set overwrite: true only when replacement is intentional.",
                  "oneOf": [
                    {
                      "type": "boolean"
                    },
                    {
                      "$ref": "#/$defs/expression"
                    }
                  ],
                  "default": false
                },
                "targetPath": {
                  "$ref": "#/$defs/expression",
                  "description": "Destination path for copy/move within the same resolved URL authority (CEL expression)."
                },
                "limit": {
                  "description": "Maximum number of entries to return for list operations. Integer literal or CEL expression.",
                  "oneOf": [
                    {
                      "type": "integer",
                      "minimum": 1
                    },
                    {
                      "$ref": "#/$defs/expression"
                    }
                  ]
                },
                "cursor": {
                  "$ref": "#/$defs/expression",
                  "description": "Pagination cursor for list operations (CEL expression). Pass RESULT.cursor from a previous list call."
                },
                "source": {
                  "type": "object",
                  "required": [
                    "url"
                  ],
                  "additionalProperties": false,
                  "description": "Source for cross-storage transfer. REQUIRED for transfer operation. path: is required when url: is an alias or base URL without a path component; optional when url: is a fully-qualified URL including the path.",
                  "properties": {
                    "url": {
                      "type": "string",
                      "minLength": 1,
                      "description": "Source storage target. Three forms: bare alias, literal URL, or CEL expression (= prefix)."
                    },
                    "path": {
                      "$ref": "#/$defs/expression",
                      "description": "Source file path (CEL expression)."
                    }
                  }
                },
                "target": {
                  "type": "object",
                  "required": [
                    "url"
                  ],
                  "additionalProperties": false,
                  "description": "Target for cross-storage transfer. REQUIRED for transfer operation. path: is required when url: is an alias or base URL without a path component; optional when url: is a fully-qualified URL including the path.",
                  "properties": {
                    "url": {
                      "type": "string",
                      "minLength": 1,
                      "description": "Target storage target. Three forms: bare alias, literal URL, or CEL expression (= prefix)."
                    },
                    "path": {
                      "$ref": "#/$defs/expression",
                      "description": "Target file path (CEL expression)."
                    }
                  }
                },
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "condition": {
                  "$ref": "#/$defs/expression"
                },
                "timeout": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/timeout"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "retry": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/retryPolicy"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "onTimeout": {
                  "$ref": "#/$defs/onTimeout"
                },
                "rateLimit": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/rateLimitSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "circuitBreaker": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/circuitBreakerSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "cacheHint": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/cacheHintSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "async": {
                  "type": "boolean",
                  "description": "Fire-and-forget: perform the storage operation without waiting for completion. result:, retry:, and step-level timeout are ignored when async: true. Errors are logged but do not propagate to the caller.",
                  "default": false
                },
                "result": {
                  "$ref": "#/$defs/stepResult"
                },
                "errors": {
                  "$ref": "#/$defs/errorList"
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "rollback": {
                  "$ref": "#/$defs/stepListOrStep",
                  "description": "Compensation handler. Executed in reverse completion order when an error propagates past the enclosing transaction group or flow boundary. Runs before transaction variable rollback so handlers can access output variables."
                },
                "onYield": {
                  "$ref": "#/$defs/onYield"
                },
                "catch": {
                  "$ref": "#/$defs/catchHandler"
                }
              },
              "not": {
                "required": ["rollback", "catch"]
              },
              "allOf": [
                {
                  "if": {
                    "properties": {
                      "operation": { "const": "transfer" }
                    }
                  },
                  "then": {
                    "required": ["operation", "source", "target"]
                  }
                },
                {
                  "if": {
                    "properties": {
                      "operation": {
                        "enum": ["get", "put", "delete", "list", "info", "exists", "mkdir", "copy", "move"]
                      }
                    }
                  },
                  "then": {
                    "required": ["operation", "url"]
                  }
                }
              ]
            }
          }
        },
        {
          "description": "SSH remote command execution action. Runs a command on a remote host via SSH exec channel (no shell session, no PTY). command: is a carved-out plain string (not CEL) for static capability validation against the SSH allowlist. The engine POSIX-escapes all args and env values.",
          "type": "object",
          "required": [
            "ssh"
          ],
          "additionalProperties": false,
          "properties": {
            "ssh": {
              "type": "object",
              "required": [
                "host",
                "command"
              ],
              "additionalProperties": false,
              "properties": {
                "host": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 8192,
                  "description": "SSH hostname or alias. The engine validates against the SSH capability's allowed-host list."
                },
                "command": {
                  "type": "string",
                  "minLength": 1,
                  "pattern": "^[^=]",
                  "description": "Remote executable name. Plain string — NOT a CEL expression. Enables static capability validation against the SSH allowlist. Shell metacharacters (|, >, <, &&, ||, ;, $(), backticks, newlines) are rejected at static analysis (SA-SSH-1)."
                },
                "args": {
                  "description": "Command arguments. Array form: each item MUST be a string (CEL expression or literal). String form: single CEL expression evaluating to a list of strings at runtime. The engine POSIX-shell-escapes each argument individually. Each argument is limited to 32,768 characters and the array is limited to 1,000 items to prevent resource exhaustion.",
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "maxLength": 32768,
                        "description": "Argument value. Must be a string: CEL expressions are prefixed with =, literals are plain strings. Objects, arrays, numbers, and booleans are not permitted as command-line arguments."
                      },
                      "maxItems": 1000,
                      "description": "Array of string arguments. Each item is a CEL expression (= prefix) or a plain string literal."
                    },
                    {
                      "$ref": "#/$defs/expression",
                      "description": "CEL expression evaluating to a list of string arguments at runtime."
                    }
                  ]
                },
                "workingDir": {
                  "$ref": "#/$defs/expression",
                  "description": "Remote working directory (CEL expression). Prepended as 'cd <dir> &&' to the remote command."
                },
                "env": {
                  "type": "object",
                  "additionalProperties": {},
                  "description": "Environment variables injected on the remote host. Keys are variable names (plain strings), values are CEL expressions or pass-through literals. SecretValues are resolved by the engine before command construction. Prepended as 'env KEY=val' to the remote command.",
                  "maxProperties": 100
                },
                "stdin": {
                  "$ref": "#/$defs/expression",
                  "description": "Data piped to the remote command's stdin via the SSH channel (written once, pipe closed — not interactive). CEL expression."
                },
                "parseAs": {
                  "$ref": "#/$defs/parseAsExpr"
                },
                "charset": {
                  "$ref": "#/$defs/charset",
                  "description": "Character encoding for stdout. Overrides auto-detection. Only meaningful for text-based parseAs (not BINARY)."
                },
                "stderrCharset": {
                  "$ref": "#/$defs/charset",
                  "description": "Character encoding for stderr. Overrides auto-detection."
                },
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "condition": {
                  "$ref": "#/$defs/expression"
                },
                "timeout": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/timeout"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "retry": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/retryPolicy"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "onTimeout": {
                  "$ref": "#/$defs/onTimeout"
                },
                "rateLimit": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/rateLimitSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "circuitBreaker": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/circuitBreakerSpec"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "async": {
                  "type": "boolean",
                  "description": "Fire-and-forget: launch the remote command without waiting for completion. result:, retry:, and step-level timeout are ignored when async: true. Errors are logged but do not propagate to the caller.",
                  "default": false
                },
                "result": {
                  "$ref": "#/$defs/stepResult"
                },
                "errors": {
                  "$ref": "#/$defs/errorList"
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "rollback": {
                  "$ref": "#/$defs/stepListOrStep",
                  "description": "Compensation handler. Executed in reverse completion order when an error propagates past the enclosing transaction group or flow boundary. Runs before transaction variable rollback so handlers can access output variables."
                },
                "onYield": {
                  "$ref": "#/$defs/onYield"
                },
                "catch": {
                  "$ref": "#/$defs/catchHandler"
                }
              },
              "not": {
                "required": ["rollback", "catch"]
              }
            }
          }
        },
        {
          "$ref": "#/$defs/actionStep"
        }
      ]
    },
    "retryPolicy": {
      "description": "Retry policy. Object form: inline specification. String shorthand: '<maxAttempts>[/<delay>[/<backoff>]]' (e.g. '3/2s/EXPONENTIAL'). Integer shorthand: bare number for maxAttempts-only (e.g. 3). CEL expression form (= prefix): references a retryPolicy object by name.",
      "oneOf": [
        {
          "type": "string",
          "pattern": "^=",
          "minLength": 2,
          "maxLength": 10000,
          "description": "CEL expression referencing a retryPolicy object by name (e.g. =STD_RETRY from const:)."
        },
        {
          "type": "string",
          "pattern": "^\\d+(/\\d+(ms|[smhd])(/(FIXED|LINEAR|EXPONENTIAL))?)?$",
          "description": "Shorthand: <maxAttempts>, <maxAttempts>/<delay>, or <maxAttempts>/<delay>/<backoff>. Examples: '3', '3/2s', '5/500ms/EXPONENTIAL', '3/1s/LINEAR'."
        },
        {
          "type": "integer",
          "minimum": 1,
          "description": "Integer shorthand: bare number treated as maxAttempts (e.g. retry: 3)."
        },
        {
          "type": "object",
          "required": [
            "maxAttempts"
          ],
          "additionalProperties": false,
          "properties": {
            "maxAttempts": {
              "description": "Maximum retry attempts. Integer literal (≥1) or CEL expression string producing an integer. Minimum enforced at runtime when CEL.",
              "oneOf": [
                {
                  "$ref": "#/$defs/expression"
                },
                {
                  "type": "integer",
                  "minimum": 1
                }
              ]
            },
            "delay": {
              "$ref": "#/$defs/durationOrExpr",
              "description": "Initial delay before the first retry. Duration literal (e.g. '2s'), integer milliseconds, or CEL expression."
            },
            "backoff": {
              "description": "Retry backoff strategy. Static: FIXED, LINEAR, EXPONENTIAL. CEL: expression evaluating to one of those strings at runtime (e.g., \"=feature_flags.use_exponential ? EXPONENTIAL : FIXED\").",
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "FIXED",
                    "LINEAR",
                    "EXPONENTIAL"
                  ]
                },
                {
                  "type": "string",
                  "pattern": "^=",
                  "minLength": 2,
                  "maxLength": 10000
                }
              ]
            },
            "maxDelay": {
              "$ref": "#/$defs/durationOrExpr",
              "description": "Cap on the delay between retries (applies after backoff). Duration literal, integer milliseconds, or CEL expression."
            },
            "jitter": {
              "description": "Jitter applied to the delay. true = 25% (predefined default). Number: 0.0–1.0 factor (e.g. 0.25 = 25%). String: percentage (e.g. '25%'). Defaults to true when backoff is exponential, false otherwise.",
              "oneOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 1
                },
                {
                  "type": "string",
                  "pattern": "^\\d+(\\.\\d+)?%$"
                }
              ]
            },
            "onErrors": {
              "description": "Whitelist of error types to retry. Only errors whose type matches an entry trigger a retry. Omitting retries on all errors. Mutually exclusive with nonRetryable.",
              "$ref": "#/$defs/errorList"
            },
            "nonRetryable": {
              "description": "Blacklist of error types that MUST NOT be retried. All other errors trigger a retry. Mutually exclusive with onErrors.",
              "$ref": "#/$defs/errorList"
            }
          },
          "not": {
            "required": [
              "onErrors",
              "nonRetryable"
            ],
            "description": "onErrors and nonRetryable are mutually exclusive"
          }
        }
      ]
    },
    "cacheHintSpec": {
      "description": "Advisory cache configuration for storage operations. String shorthand: '<ttl>[/<revalidation>[/<scope>]]' (e.g. '5m', '5m/ALWAYS', '5m/CONDITIONAL/GLOBAL'). Boolean: true (opt-in, engine defaults) or false (opt-out, must not cache). Object form: inline specification. CEL expression form (= prefix): references a cacheHintSpec object by name.",
      "oneOf": [
        {
          "type": "string",
          "pattern": "^=",
          "minLength": 2,
          "maxLength": 10000,
          "description": "CEL expression referencing a cacheHintSpec object by name (e.g. =STD_CACHE from const:)."
        },
        {
          "type": "string",
          "pattern": "^[1-9]\\d*(ms|[smhd])(/(?:CONDITIONAL|ALWAYS|NEVER)(/(?:LOCAL|CONTEXT|GLOBAL))?)?$",
          "description": "Shorthand: <ttl>, <ttl>/<revalidation>, or <ttl>/<revalidation>/<scope>. TTL must be positive (non-zero). Examples: '5m', '1h/ALWAYS', '30s/CONDITIONAL/GLOBAL'."
        },
        {
          "type": "boolean",
          "description": "true: explicit opt-in with engine-determined defaults. false: explicit opt-out — data MUST NOT be cached."
        },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "ttl": {
              "$ref": "#/$defs/durationPositiveOrExpr",
              "description": "Suggested time-to-live for cached entries. Duration literal (e.g. '5m', '1h'), positive integer milliseconds, or CEL expression. When omitted, engine determines TTL. A value of 0 is not valid; use cacheHint: false to disable caching."
            },
            "revalidation": {
              "description": "Cache revalidation strategy. CONDITIONAL: use conditional GET (ETag/mtime). ALWAYS: always revalidate before serving. NEVER: serve from cache until TTL expires.",
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "CONDITIONAL",
                    "ALWAYS",
                    "NEVER"
                  ]
                },
                {
                  "type": "string",
                  "pattern": "^=",
                  "minLength": 2,
                  "maxLength": 10000
                }
              ],
              "default": "CONDITIONAL"
            },
            "staleWhileRevalidate": {
              "$ref": "#/$defs/durationOrExpr",
              "description": "Serve stale content while refreshing in the background. Duration literal, integer milliseconds, or CEL expression. Default: 0 (disabled)."
            },
            "staleIfError": {
              "$ref": "#/$defs/durationOrExpr",
              "description": "Serve stale content when backend returns an error. Duration literal, integer milliseconds, or CEL expression. Default: 0 (disabled). Checked as fast-path before first retry and as final fallback after retries exhausted."
            },
            "negative": {
              "description": "Cache 'not found' results for get, info, and exists operations. Boolean or CEL expression.",
              "oneOf": [
                {
                  "type": "boolean"
                },
                {
                  "$ref": "#/$defs/expression"
                }
              ],
              "default": false
            },
            "negativeTtl": {
              "$ref": "#/$defs/durationOrExpr",
              "description": "TTL for negative cache entries. Only meaningful when negative: true. Duration literal, integer milliseconds, or CEL expression. Default: 30s."
            },
            "scope": {
              "description": "Cache sharing boundary. LOCAL: per-flow-instance. CONTEXT: shared within same context/correlation. GLOBAL: shared across all instances (tenant-scoped).",
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "LOCAL",
                    "CONTEXT",
                    "GLOBAL"
                  ]
                },
                {
                  "type": "string",
                  "pattern": "^=",
                  "minLength": 2,
                  "maxLength": 10000
                }
              ],
              "default": "LOCAL"
            },
            "varyBy": {
              "$ref": "#/$defs/expression",
              "description": "CEL expression producing a string appended to the cache key for partitioning (e.g. =ENV.REGION)."
            },
            "maxSize": {
              "description": "Maximum content size in bytes that should be cached. Content larger than this is always fetched from origin. Not applicable to info or exists.",
              "oneOf": [
                {
                  "type": "integer",
                  "minimum": 1
                },
                {
                  "$ref": "#/$defs/expression"
                }
              ]
            },
            "priority": {
              "description": "Eviction priority hint. Engines MAY use this to influence eviction order.",
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "LOW",
                    "NORMAL",
                    "HIGH"
                  ]
                },
                {
                  "type": "string",
                  "pattern": "^=",
                  "minLength": 2,
                  "maxLength": 10000
                }
              ],
              "default": "NORMAL"
            },
            "warm": {
              "description": "Pre-populate cache at flow load time. Applicable to get and info only. Boolean or CEL expression.",
              "oneOf": [
                {
                  "type": "boolean"
                },
                {
                  "$ref": "#/$defs/expression"
                }
              ],
              "default": false
            },
            "invalidation": {
              "description": "Write-side invalidation strategy. EXACT: invalidate entries matching exact path. PREFIX: invalidate all entries whose path starts with the written path. NONE: do not invalidate on write.",
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "EXACT",
                    "PREFIX",
                    "NONE"
                  ]
                },
                {
                  "type": "string",
                  "pattern": "^=",
                  "minLength": 2,
                  "maxLength": 10000
                }
              ],
              "default": "EXACT"
            },
            "invalidatePaths": {
              "description": "Additional paths to invalidate on write. Array of string literals or CEL expressions.",
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "minItems": 1
            },
            "readYourWrites": {
              "description": "Ensures reads following writes to the same path within a single flow instance return written data, not stale cache. Boolean or CEL expression.",
              "oneOf": [
                {
                  "type": "boolean"
                },
                {
                  "$ref": "#/$defs/expression"
                }
              ],
              "default": false
            }
          }
        }
      ]
    },
    "rateLimitSpec": {
      "description": "Rate limit specification. Shorthand string form: '<invocations>/<window>' or '<invocations>/<window>/<scope>' (e.g. '100/1m', '50/30s/GLOBAL'). Object form: inline specification. CEL expression form (= prefix): references a rateLimitSpec object by name.",
      "oneOf": [
        {
          "type": "string",
          "pattern": "^=",
          "minLength": 2,
          "maxLength": 10000,
          "description": "CEL expression referencing a rateLimitSpec object by name."
        },
        {
          "type": "string",
          "pattern": "^\\d+\\s*/\\s*\\d+(ms|[smhd])(\\s*/\\s*(LOCAL|CONTEXT|GLOBAL))?$",
          "description": "Shorthand: <invocations>/<window>[/<scope>]. Optional spaces around /. Examples: '100/1m', '50/30s/GLOBAL', '1000 / 1h'."
        },
        {
          "type": "object",
          "required": [
            "invocations",
            "per"
          ],
          "additionalProperties": false,
          "properties": {
            "invocations": {
              "description": "Maximum invocations allowed within the per: window. Integer literal (≥1) or CEL expression string producing an integer. Minimum enforced at runtime when CEL.",
              "oneOf": [
                {
                  "$ref": "#/$defs/expression"
                },
                {
                  "type": "integer",
                  "minimum": 1
                }
              ]
            },
            "per": {
              "$ref": "#/$defs/durationOrExpr",
              "description": "Rate window. Duration literal (e.g. '1m', '30s', '500ms'), integer milliseconds, or CEL expression (e.g. =GLOBAL.rate_window) for operator-tunable configuration."
            },
            "strategy": {
              "description": "Behavior when the rate limit is exceeded. WAIT (default): queue and delay the invocation until the window allows. REJECT: throw RateLimitError immediately without waiting. Static enum or CEL expression (=) evaluating to one of those strings at runtime.",
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "WAIT",
                    "REJECT"
                  ]
                },
                {
                  "type": "string",
                  "pattern": "^=",
                  "minLength": 2,
                  "maxLength": 10000
                }
              ]
            },
            "scope": {
              "$ref": "#/$defs/scope"
            },
            "key": {
              "$ref": "#/$defs/expression",
              "description": "Optional CEL expression. When present, creates isolated invocation counters per unique key value. Example: 'CONTEXT.tenant_id' for per-tenant rate limiting. Omit to use a single shared counter."
            },
            "timeout": {
              "$ref": "#/$defs/timeout",
              "description": "Maximum time to wait for a rate-limit slot when strategy: WAIT. When exceeded, throws RateLimitError. Meaningful only for strategy: WAIT — no effect with strategy: REJECT."
            }
          }
        }
      ]
    },
    "circuitBreakerDefaultsSpec": {
      "description": "Circuit breaker specification for defaults block. Only object form and CEL expression are valid — integer and string shorthands are not allowed in defaults (SA-DEF-5).",
      "oneOf": [
        {
          "$ref": "#/$defs/expression"
        },
        {
          "type": "object",
          "required": [
            "name",
            "threshold"
          ],
          "additionalProperties": false,
          "properties": {
            "name": {
              "$ref": "#/$defs/expression",
              "description": "Breaker identifier (CEL expression). Prefix with = for expressions (e.g., name: ='payment_service' or dynamic: name: ='svc_' + service_id). Static literal: name: payment_service (no = prefix needed)."
            },
            "threshold": {
              "description": "Number of failures within the window to trip the breaker OPEN. Integer literal (≥1) or CEL expression string producing an integer.",
              "oneOf": [
                {
                  "$ref": "#/$defs/expression"
                },
                {
                  "type": "integer",
                  "minimum": 1
                }
              ]
            },
            "window": {
              "$ref": "#/$defs/durationOrExpr",
              "description": "Sliding window for counting failures. Duration literal (e.g. '1m', '30s'), integer milliseconds, or CEL expression (e.g. =GLOBAL.cb_window). Default: 1m."
            },
            "resetTimeout": {
              "$ref": "#/$defs/durationOrExpr",
              "description": "Time the breaker stays OPEN before transitioning to HALF-OPEN. Duration literal, integer milliseconds, or CEL expression (e.g. =GLOBAL.cb_reset). Default: 30s."
            },
            "halfOpenAttempts": {
              "description": "Number of trial requests allowed in HALF-OPEN state before deciding to close or re-open. Integer literal (≥1) or CEL expression. Default: 3.",
              "oneOf": [
                {
                  "$ref": "#/$defs/expression"
                },
                {
                  "type": "integer",
                  "minimum": 1
                }
              ]
            },
            "scope": {
              "$ref": "#/$defs/scope"
            },
            "errors": {
              "description": "Whitelist of error types that count as circuit breaker failures. Only these errors increment the failure counter. Mutually exclusive with non-countable.",
              "$ref": "#/$defs/errorList"
            },
            "nonCountable": {
              "description": "Blacklist of error types excluded from failure counting. All other errors count. Mutually exclusive with errors.",
              "$ref": "#/$defs/errorList"
            }
          },
          "not": {
            "required": [
              "errors",
              "nonCountable"
            ],
            "description": "errors and non-countable are mutually exclusive"
          }
        }
      ]
    },
    "circuitBreakerSpec": {
      "description": "Circuit breaker specification. String shorthand: '<threshold>/<name>' (e.g. '5/payment_api'). Integer shorthand: bare number for threshold (requires _id_ on step, e.g. 5). Object form: inline specification. CEL expression form (= prefix): references a circuitBreakerSpec object by name (e.g. =AI_BREAKER from const:).",
      "oneOf": [
        {
          "$ref": "#/$defs/expression"
        },
        {
          "type": "string",
          "pattern": "^[1-9][0-9]*/[a-zA-Z][a-zA-Z0-9_.:-]*$",
          "description": "Shorthand: <threshold>/<name>. Name is a static literal (no = prefix, no / in name). Examples: '5/payment_api', '10/inventory', '3/com.example.orders.api'."
        },
        {
          "type": "integer",
          "minimum": 1,
          "description": "Integer shorthand: bare number treated as threshold (e.g. circuitBreaker: 5). Name is auto-derived from the step's _id_. SA-CB-13 rejects this form on steps without _id_."
        },
        {
          "type": "object",
          "required": [
            "name",
            "threshold"
          ],
          "additionalProperties": false,
          "properties": {
            "name": {
              "$ref": "#/$defs/expression",
              "description": "Breaker identifier (CEL expression). Prefix with = for expressions (e.g., name: ='payment_service' or dynamic: name: ='svc_' + service_id). Static literal: name: payment_service (no = prefix needed)."
            },
            "threshold": {
              "description": "Number of failures within the window to trip the breaker OPEN. Integer literal (≥1) or CEL expression string producing an integer.",
              "oneOf": [
                {
                  "$ref": "#/$defs/expression"
                },
                {
                  "type": "integer",
                  "minimum": 1
                }
              ]
            },
            "window": {
              "$ref": "#/$defs/durationOrExpr",
              "description": "Sliding window for counting failures. Duration literal (e.g. '1m', '30s'), integer milliseconds, or CEL expression (e.g. =GLOBAL.cb_window). Default: 1m."
            },
            "resetTimeout": {
              "$ref": "#/$defs/durationOrExpr",
              "description": "Time the breaker stays OPEN before transitioning to HALF-OPEN. Duration literal, integer milliseconds, or CEL expression (e.g. =GLOBAL.cb_reset). Default: 30s."
            },
            "halfOpenAttempts": {
              "description": "Number of trial requests allowed in HALF-OPEN state before deciding to close or re-open. Integer literal (≥1) or CEL expression. Default: 3.",
              "oneOf": [
                {
                  "$ref": "#/$defs/expression"
                },
                {
                  "type": "integer",
                  "minimum": 1
                }
              ]
            },
            "scope": {
              "$ref": "#/$defs/scope"
            },
            "errors": {
              "description": "Whitelist of error types that count as circuit breaker failures. Only these errors increment the failure counter. Mutually exclusive with non-countable.",
              "$ref": "#/$defs/errorList"
            },
            "nonCountable": {
              "description": "Blacklist of error types excluded from failure counting. All other errors count. Mutually exclusive with errors.",
              "$ref": "#/$defs/errorList"
            }
          },
          "not": {
            "required": [
              "errors",
              "nonCountable"
            ],
            "description": "errors and non-countable are mutually exclusive"
          }
        }
      ]
    },
    "stepParams": {
      "description": "Step input parameters (params:). Three forms: (1) Object: keys = parameter names, values = CEL expressions (= prefix), literals, or null (same-name: key passes local variable =<key>). (2) List: variable names — each passes the local variable with the same name as parameter with the same name. Equivalent to object form with all-null values.",
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": {}
        },
        {
          "description": "List form: each name passes the local variable =<name> as parameter <name>.",
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9_]*$"
          },
          "minItems": 1
        }
      ]
    },
    "stepResult": {
      "description": "Step result mapping. Three forms: (1) String: single variable name receives the whole RESULT. (2) Object: keys = target variable names, values = CEL expressions (= prefix) referencing RESULT, or null for same-name mapping (key receives RESULT.<key>). (3) List: field names — each name receives RESULT.<name>. Target variable names may use GLOBAL., CONTEXT., or LOCAL. prefix.",
      "oneOf": [
        {
          "description": "Single variable name: receives the action's or sub-flow's whole return value. May use GLOBAL., CONTEXT., or LOCAL. prefix.",
          "type": "string",
          "pattern": "^(GLOBAL\\.|CONTEXT\\.|LOCAL\\.)?[a-z][a-z0-9_]*$"
        },
        {
          "description": "Multi-value mapping: keys = target variable names, values = CEL expressions (must start with =) or null (same-name: key receives RESULT.<key>). $yields key receives the collected list of all yielded values.",
          "type": "object",
          "properties": {
            "$yields": {
              "type": "string",
              "pattern": "^(GLOBAL\\.|CONTEXT\\.|LOCAL\\.)?[a-z][a-z0-9_]*$",
              "description": "Variable to receive the collected list of all yielded values (list materialization). Only meaningful when the target declares yields: or is a streaming service. Mutually exclusive with onYield on the same step."
            }
          },
          "additionalProperties": {
            "oneOf": [
              {
                "type": "string",
                "pattern": "^=",
                "maxLength": 10000
              },
              {
                "type": "null",
                "description": "Same-name mapping: this key receives RESULT.<key>. Shorthand for key: =RESULT.<key>."
              }
            ]
          }
        },
        {
          "description": "List form: each field name receives RESULT.<name> as a same-named local variable. Equivalent to object form with all-null values.",
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9_]*$"
          },
          "minItems": 1
        }
      ]
    },
    "captureMapping": {
      "description": "capture: mapping for waitFor. Three forms: (1) Object: keys = target variable names, values = CEL expressions (= prefix) or null (same-name: key receives EVENT.DATA.<key>). (2) List: field names — each receives EVENT.DATA.<name>. Target variable names may use GLOBAL., CONTEXT., or LOCAL. prefix.",
      "oneOf": [
        {
          "type": "object",
          "propertyNames": {
            "not": {
              "anyOf": [
                {
                  "enum": [
                    "LOCAL",
                    "CONTEXT",
                    "GLOBAL",
                    "RUNTIME",
                    "SERVICES",
                    "ERROR",
                    "EVENT",
                    "MIGRATION",
                    "YIELD",
                    "RESULT",
                    "RESULTS",
                    "RESOURCES"
                  ]
                },
                {
                  "pattern": "^(ENV|RUNTIME|SECRET)\\."
                }
              ]
            }
          },
          "additionalProperties": {
            "oneOf": [
              {
                "type": "string",
                "pattern": "^=",
                "maxLength": 10000
              },
              {
                "type": "null",
                "description": "Same-name mapping: this key receives EVENT.DATA.<key>. Shorthand for key: =EVENT.DATA.<key>."
              }
            ]
          }
        },
        {
          "description": "List form: each field name receives EVENT.DATA.<name> as a same-named local variable.",
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9_]*$"
          },
          "minItems": 1
        }
      ]
    },
    "errorList": {
      "description": "List of error type declarations",
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1,
        "pattern": "^[A-Z].*Error$"
      },
      "maxItems": 10000
    },
    "actionBody": {
      "description": "Universal shape for all actions (SPI-loaded steps). params/result/errors are optional; omitting them is equivalent to empty. catch and rollback are mutually exclusive (SA-ERR-7).",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "_id_": {
          "$ref": "#/$defs/stepId"
        },
        "_label_": {
          "type": "string",
          "description": "Human-readable step name for logs and visualization",
          "maxLength": 500
        },
        "_notes_": {
          "type": "string",
          "description": "Human-readable documentation. Plain text or Markdown.",
          "maxLength": 10000
        },
        "condition": {
          "$ref": "#/$defs/expression"
        },
        "timeout": {
          "description": "Step timeout. Duration literal, integer ms, or CEL expression. null explicitly disables any in-scope defaults: timeout.",
          "oneOf": [
            {
              "$ref": "#/$defs/timeout"
            },
            {
              "type": "null"
            }
          ]
        },
        "onTimeout": {
          "$ref": "#/$defs/onTimeout"
        },
        "retry": {
          "description": "Retry policy. Object, CEL expression, or null to explicitly disable any in-scope defaults: retry.",
          "oneOf": [
            {
              "$ref": "#/$defs/retryPolicy"
            },
            {
              "type": "null"
            }
          ]
        },
        "rateLimit": {
          "description": "Action-level rate limit. Restricts how often this step may execute. Scope defaults to LOCAL. null explicitly disables any in-scope defaults: rateLimit.",
          "oneOf": [
            {
              "$ref": "#/$defs/rateLimitSpec"
            },
            {
              "type": "null"
            }
          ]
        },
        "circuitBreaker": {
          "description": "Action-level circuit breaker. Rejects execution when the circuit is OPEN. Scope defaults to LOCAL. null explicitly disables any in-scope defaults: circuitBreaker.",
          "oneOf": [
            {
              "$ref": "#/$defs/circuitBreakerSpec"
            },
            {
              "type": "null"
            }
          ]
        },
        "async": {
          "type": "boolean",
          "description": "Fire-and-forget: invoke the action without waiting for completion. result:, retry:, and step-level timeout are ignored when async: true. Errors are logged but do not propagate to the caller.",
          "default": false
        },
        "params": {
          "$ref": "#/$defs/stepParams"
        },
        "result": {
          "$ref": "#/$defs/stepResult"
        },
        "errors": {
          "$ref": "#/$defs/errorList"
        },
        "_meta_": {
          "$ref": "#/$defs/meta"
        },
        "rollback": {
          "$ref": "#/$defs/stepListOrStep",
          "description": "Compensation handler. Executed in reverse completion order when an error propagates past the enclosing transaction group or flow boundary. Runs before transaction variable rollback so handlers can access output variables."
        },
        "onYield": {
          "$ref": "#/$defs/onYield"
        },
        "catch": {
          "$ref": "#/$defs/catchHandler"
        }
      },
      "not": {
        "required": ["rollback", "catch"]
      }
    },
    "actionStep": {
      "description": "Matches any action (non-directive step). The action name is the single key; its value conforms to actionBody. SECURITY: Action names MUST be static strings matching the pattern ^[a-zA-Z][a-zA-Z0-9_.]*$ — CEL expressions and dynamic values are NOT permitted as action names. This ensures static analysis can enumerate all actions in a flow at load time.",
      "type": "object",
      "minProperties": 1,
      "maxProperties": 1,
      "propertyNames": {
        "pattern": "^[a-zA-Z][a-zA-Z0-9_.]*$",
        "description": "Action names MUST be static identifiers: start with a letter, then alphanumeric, dots, or underscores. Dynamic (CEL) action names are not permitted."
      },
      "not": {
        "anyOf": [
          {
            "required": [
              "group"
            ]
          },
          {
            "required": [
              "if"
            ]
          },
          {
            "required": [
              "forEach"
            ]
          },
          {
            "required": [
              "while"
            ]
          },
          {
            "required": [
              "repeat"
            ]
          },
          {
            "required": [
              "try"
            ]
          },
          {
            "required": [
              "set"
            ]
          },
          {
            "required": [
              "log"
            ]
          },
          {
            "required": [
              "logWarn"
            ]
          },
          {
            "required": [
              "logError"
            ]
          },
          {
            "required": [
              "switch"
            ]
          },
          {
            "required": [
              "assert"
            ]
          },
          {
            "required": [
              "throw"
            ]
          },
          {
            "required": [
              "return"
            ]
          },
          {
            "required": [
              "wait"
            ]
          },
          {
            "required": [
              "break"
            ]
          },
          {
            "required": [
              "continue"
            ]
          },
          {
            "required": [
              "emit"
            ]
          },
          {
            "required": [
              "waitFor"
            ]
          },
          {
            "required": [
              "lock"
            ]
          },
          {
            "required": [
              "waitUntil"
            ]
          },
          {
            "required": [
              "yield"
            ]
          },
          {
            "required": [
              "cancel"
            ]
          },
          {
            "required": [
              "parallel"
            ]
          },
          {
            "required": [
              "race"
            ]
          },
          {
            "required": [
              "call"
            ]
          },
          {
            "required": [
              "exec"
            ]
          },
          {
            "required": [
              "mail"
            ]
          },
          {
            "required": [
              "request"
            ]
          },
          {
            "required": [
              "run"
            ]
          },
          {
            "required": [
              "storage"
            ]
          },
          {
            "required": [
              "ssh"
            ]
          }
        ]
      },
      "additionalProperties": {
        "$ref": "#/$defs/actionBody"
      }
    },
    "groupStep": {
      "description": "Step group. mode: SEQUENCE (default) runs do: or branches: in order; mode: PARALLEL runs all branches concurrently; mode: RACE runs branches concurrently and takes the first to complete.",
      "type": "object",
      "required": [
        "group"
      ],
      "additionalProperties": false,
      "properties": {
        "group": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "_id_": {
              "$ref": "#/$defs/stepId"
            },
            "_label_": {
              "type": "string",
              "maxLength": 500
            },
            "_notes_": {
              "type": "string",
              "description": "Human-readable documentation. Plain text or Markdown.",
              "maxLength": 10000
            },
            "_meta_": {
              "$ref": "#/$defs/meta"
            },
            "condition": {
              "$ref": "#/$defs/expression",
              "description": "Guard condition. Step is skipped if this CEL expression evaluates to false."
            },
            "timeout": {
              "$ref": "#/$defs/timeout"
            },
            "onTimeout": {
              "$ref": "#/$defs/onTimeout"
            },
            "mode": {
              "description": "Execution mode. Static: SEQUENCE (default), PARALLEL, RACE. CEL: expression (=) evaluating to one of those strings at runtime (e.g., \"=ENV.ENABLE_PARALLEL ? PARALLEL : SEQUENCE\").",
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "SEQUENCE",
                    "PARALLEL",
                    "RACE"
                  ]
                },
                {
                  "type": "string",
                  "pattern": "^=",
                  "minLength": 2,
                  "maxLength": 10000
                }
              ],
              "default": "SEQUENCE"
            },
            "failPolicy": {
              "description": "Failure policy for parallel/race branches. FAST (default): cancel remaining branches on first failure. COMPLETE: wait for all branches to finish before propagating errors. Static enum or CEL expression (=) evaluating to one of those strings at runtime.",
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "FAST",
                    "COMPLETE"
                  ]
                },
                {
                  "type": "string",
                  "pattern": "^=",
                  "minLength": 2,
                  "maxLength": 10000
                }
              ],
              "default": "FAST"
            },
            "transaction": {
              "description": "Variable scope forking. On entry, the runtime snapshots variable scopes. All writes go to the fork. On success: committed atomically. On error: discarded (after rollback handlers complete). true = all scopes; named scope = fork only that scope.",
              "oneOf": [
                {
                  "type": "boolean",
                  "const": true
                },
                {
                  "type": "string",
                  "enum": [
                    "GLOBAL",
                    "CONTEXT",
                    "LOCAL"
                  ]
                }
              ]
            },
            "onRollbackError": {
              "description": "Policy when a rollback handler fails. CONTINUE (default): log and proceed. FAIL: stop and raise RollbackFailedError.",
              "type": "string",
              "enum": [
                "CONTINUE",
                "FAIL"
              ],
              "default": "CONTINUE"
            },
            "locking": {
              "description": "Lock strategy for transaction group isolation. PESSIMISTIC (default): acquire per-variable locks at group entry — no ConflictError possible. OPTIMISTIC: snapshot-based validation at commit — ConflictError on conflict. Only valid when transaction is set.",
              "type": "string",
              "enum": [
                "PESSIMISTIC",
                "OPTIMISTIC"
              ],
              "default": "PESSIMISTIC"
            },
            "defaults": {
              "$ref": "#/$defs/stepDefaults",
              "description": "Group-scoped step defaults. Shadows any outer defaults: for all action steps directly or transitively contained in this group."
            },
            "do": {
              "$ref": "#/$defs/stepListOrStep"
            },
            "branches": {
              "type": "object",
              "minProperties": 1,
              "additionalProperties": {
                "$ref": "#/$defs/branchValue"
              }
            }
          },
          "allOf": [
            {
              "not": {
                "required": [
                  "do",
                  "branches"
                ]
              }
            },
            {
              "if": {
                "properties": {
                  "mode": {
                    "enum": [
                      "PARALLEL",
                      "RACE"
                    ]
                  }
                },
                "required": [
                  "mode"
                ]
              },
              "then": {
                "required": [
                  "branches"
                ]
              }
            },
            {
              "if": {
                "anyOf": [
                  {
                    "properties": {
                      "mode": {
                        "const": "SEQUENCE"
                      }
                    },
                    "required": [
                      "mode"
                    ]
                  },
                  {
                    "not": {
                      "required": [
                        "mode"
                      ]
                    }
                  }
                ]
              },
              "then": {
                "anyOf": [
                  {
                    "required": [
                      "do"
                    ]
                  },
                  {
                    "required": [
                      "branches"
                    ]
                  }
                ]
              }
            }
          ]
        }
      }
    },
    "parallelStep": {
      "description": "Shorthand for group with mode: PARALLEL. Config keys (failPolicy, timeout, etc.) are group options; all other keys are branch names. Desugars to: group: { mode: PARALLEL, branches: { <non-config keys> }, <config keys> }.",
      "type": "object",
      "required": [
        "parallel"
      ],
      "additionalProperties": false,
      "properties": {
        "parallel": {
          "type": "object",
          "minProperties": 1,
          "properties": {
            "_id_": {
              "$ref": "#/$defs/stepId"
            },
            "_label_": {
              "type": "string",
              "maxLength": 500
            },
            "_notes_": {
              "type": "string",
              "description": "Human-readable documentation. Plain text or Markdown.",
              "maxLength": 10000
            },
            "_meta_": {
              "$ref": "#/$defs/meta"
            },
            "condition": {
              "$ref": "#/$defs/expression",
              "description": "Guard condition. Step is skipped if this CEL expression evaluates to false."
            },
            "timeout": {
              "$ref": "#/$defs/timeout"
            },
            "onTimeout": {
              "$ref": "#/$defs/onTimeout"
            },
            "failPolicy": {
              "description": "Failure policy for parallel branches. FAST (default): cancel remaining on first failure. COMPLETE: wait for all branches.",
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "FAST",
                    "COMPLETE"
                  ]
                },
                {
                  "type": "string",
                  "pattern": "^=",
                  "minLength": 2,
                  "maxLength": 10000
                }
              ],
              "default": "FAST"
            },
            "transaction": {
              "description": "Variable scope forking. true = all scopes; named scope = fork only that scope.",
              "oneOf": [
                {
                  "type": "boolean",
                  "const": true
                },
                {
                  "type": "string",
                  "enum": [
                    "GLOBAL",
                    "CONTEXT",
                    "LOCAL"
                  ]
                }
              ]
            },
            "onRollbackError": {
              "description": "Policy when a rollback handler fails. CONTINUE (default): log and proceed. FAIL: stop and raise RollbackFailedError.",
              "type": "string",
              "enum": [
                "CONTINUE",
                "FAIL"
              ],
              "default": "CONTINUE"
            },
            "locking": {
              "type": "string",
              "enum": [
                "PESSIMISTIC",
                "OPTIMISTIC"
              ],
              "default": "PESSIMISTIC"
            },
            "defaults": {
              "$ref": "#/$defs/stepDefaults",
              "description": "Group-scoped step defaults."
            }
          },
          "additionalProperties": {
            "$ref": "#/$defs/branchValue"
          }
        }
      }
    },
    "raceStep": {
      "description": "Shorthand for group with mode: RACE. Config keys (failPolicy, timeout, etc.) are group options; all other keys are branch names. Desugars to: group: { mode: RACE, branches: { <non-config keys> }, <config keys> }.",
      "type": "object",
      "required": [
        "race"
      ],
      "additionalProperties": false,
      "properties": {
        "race": {
          "type": "object",
          "minProperties": 1,
          "properties": {
            "_id_": {
              "$ref": "#/$defs/stepId"
            },
            "_label_": {
              "type": "string",
              "maxLength": 500
            },
            "_notes_": {
              "type": "string",
              "description": "Human-readable documentation. Plain text or Markdown.",
              "maxLength": 10000
            },
            "_meta_": {
              "$ref": "#/$defs/meta"
            },
            "condition": {
              "$ref": "#/$defs/expression",
              "description": "Guard condition. Step is skipped if this CEL expression evaluates to false."
            },
            "timeout": {
              "$ref": "#/$defs/timeout"
            },
            "onTimeout": {
              "$ref": "#/$defs/onTimeout"
            },
            "failPolicy": {
              "description": "Failure policy for race branches. FAST (default): cancel remaining on first failure. COMPLETE: wait for all branches.",
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "FAST",
                    "COMPLETE"
                  ]
                },
                {
                  "type": "string",
                  "pattern": "^=",
                  "minLength": 2,
                  "maxLength": 10000
                }
              ],
              "default": "FAST"
            },
            "transaction": {
              "description": "Variable scope forking. true = all scopes; named scope = fork only that scope.",
              "oneOf": [
                {
                  "type": "boolean",
                  "const": true
                },
                {
                  "type": "string",
                  "enum": [
                    "GLOBAL",
                    "CONTEXT",
                    "LOCAL"
                  ]
                }
              ]
            },
            "onRollbackError": {
              "description": "Policy when a rollback handler fails. CONTINUE (default): log and proceed. FAIL: stop and raise RollbackFailedError.",
              "type": "string",
              "enum": [
                "CONTINUE",
                "FAIL"
              ],
              "default": "CONTINUE"
            },
            "locking": {
              "type": "string",
              "enum": [
                "PESSIMISTIC",
                "OPTIMISTIC"
              ],
              "default": "PESSIMISTIC"
            },
            "defaults": {
              "$ref": "#/$defs/stepDefaults",
              "description": "Group-scoped step defaults."
            }
          },
          "additionalProperties": {
            "$ref": "#/$defs/branchValue"
          }
        }
      }
    },
    "branchValue": {
      "description": "Branch definition. Either a simple array of steps, or an object with optional condition, optional depends-on (array of branch names), and required do (array of steps).",
      "oneOf": [
        {
          "$ref": "#/$defs/stepList"
        },
        {
          "type": "object",
          "required": [
            "do"
          ],
          "additionalProperties": false,
          "properties": {
            "condition": {
              "$ref": "#/$defs/expression",
              "description": "Guard condition. Branch is skipped if this CEL expression evaluates to false."
            },
            "dependsOn": {
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "minItems": 1,
              "description": "Array of branch names that must complete before this branch starts. Defines DAG ordering constraints within the group."
            },
            "do": {
              "$ref": "#/$defs/stepListOrStep"
            }
          }
        }
      ]
    },
    "ifStep": {
      "description": "Conditional branching with then/elseIf/else branches",
      "type": "object",
      "required": [
        "if"
      ],
      "additionalProperties": false,
      "properties": {
        "if": {
          "type": "object",
          "required": [
            "condition",
            "then"
          ],
          "additionalProperties": false,
          "properties": {
            "_id_": {
              "$ref": "#/$defs/stepId"
            },
            "_label_": {
              "type": "string",
              "maxLength": 500
            },
            "_notes_": {
              "type": "string",
              "description": "Human-readable documentation. Plain text or Markdown.",
              "maxLength": 10000
            },
            "_meta_": {
              "$ref": "#/$defs/meta"
            },
            "condition": {
              "$ref": "#/$defs/expression"
            },
            "then": {
              "$ref": "#/$defs/stepListOrStep"
            },
            "elseIf": {
              "type": "array",
              "items": {
                "$ref": "#/$defs/elseIfClause"
              },
              "minItems": 1,
              "description": "Optional chain of else-if clauses evaluated in order after the primary condition is false."
            },
            "else": {
              "$ref": "#/$defs/stepListOrStep"
            }
          }
        }
      }
    },
    "elseIfClause": {
      "description": "Additional condition branch within an if directive",
      "type": "object",
      "required": [
        "condition",
        "then"
      ],
      "additionalProperties": false,
      "properties": {
        "condition": {
          "$ref": "#/$defs/expression",
          "description": "CEL boolean expression. Evaluated if all preceding conditions (primary and earlier else-if) were false."
        },
        "then": {
          "$ref": "#/$defs/stepListOrStep"
        }
      }
    },
    "forEachStep": {
      "description": "Iterate over a collection, executing steps for each item",
      "type": "object",
      "required": [
        "forEach"
      ],
      "additionalProperties": false,
      "properties": {
        "forEach": {
          "type": "object",
          "required": [
            "items",
            "do"
          ],
          "additionalProperties": false,
          "properties": {
            "_id_": {
              "$ref": "#/$defs/stepId"
            },
            "_label_": {
              "type": "string",
              "maxLength": 500
            },
            "_notes_": {
              "type": "string",
              "description": "Human-readable documentation. Plain text or Markdown.",
              "maxLength": 10000
            },
            "_meta_": {
              "$ref": "#/$defs/meta"
            },
            "condition": {
              "$ref": "#/$defs/expression",
              "description": "Guard condition. Step is skipped if this CEL expression evaluates to false."
            },
            "timeout": {
              "$ref": "#/$defs/timeout"
            },
            "items": {
              "$ref": "#/$defs/expression",
              "description": "CEL expression evaluating to the collection to iterate. Runtime default limit is 10,000 items; engines MAY reject or truncate larger collections."
            },
            "as": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9_]*$",
              "default": "item",
              "description": "Loop variable name. Defaults to item when omitted."
            },
            "index": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9_]*$"
            },
            "concurrent": {
              "description": "If true (or a CEL expression evaluating to true), iterations run concurrently. Each iteration operates in an isolated variable scope.",
              "oneOf": [
                {
                  "$ref": "#/$defs/expression"
                },
                {
                  "type": "boolean"
                }
              ],
              "default": false
            },
            "maxConcurrency": {
              "description": "Maximum concurrent iterations when concurrent: true. Integer literal (≥1) or CEL expression string producing an integer. Minimum enforced at runtime when CEL. Omit for unlimited concurrency.",
              "oneOf": [
                {
                  "$ref": "#/$defs/expression"
                },
                {
                  "type": "integer",
                  "minimum": 1
                }
              ]
            },
            "completionCondition": {
              "description": "CEL expression evaluated after each concurrent iteration completes. When it evaluates to true, remaining pending iterations are cancelled and the for-each exits. Only meaningful when concurrent: true. Static analysis SHOULD warn when used without concurrent: true.",
              "$ref": "#/$defs/expression"
            },
            "maxItems": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000000,
              "description": "Maximum number of items to iterate. Raises ResourceLimitError if the collection exceeds this limit. Use to bound iteration over user-controlled input. Default: 10,000."
            },
            "onTimeout": {
              "$ref": "#/$defs/onTimeout"
            },
            "do": {
              "$ref": "#/$defs/stepListOrStep"
            }
          }
        }
      }
    },
    "repeatStep": {
      "description": "Post-condition loop: executes do: body at least once, then checks until: after each iteration. Loops until until: is true. Supports break/continue.",
      "type": "object",
      "required": [
        "repeat"
      ],
      "additionalProperties": false,
      "properties": {
        "repeat": {
          "type": "object",
          "required": [
            "do",
            "until"
          ],
          "additionalProperties": false,
          "properties": {
            "_id_": {
              "$ref": "#/$defs/stepId"
            },
            "_label_": {
              "type": "string",
              "maxLength": 500
            },
            "_notes_": {
              "type": "string",
              "description": "Human-readable documentation. Plain text or Markdown.",
              "maxLength": 10000
            },
            "_meta_": {
              "$ref": "#/$defs/meta"
            },
            "condition": {
              "$ref": "#/$defs/expression",
              "description": "Guard condition. Step is skipped if this CEL expression evaluates to false."
            },
            "timeout": {
              "$ref": "#/$defs/timeout"
            },
            "do": {
              "$ref": "#/$defs/stepListOrStep"
            },
            "until": {
              "$ref": "#/$defs/expression",
              "description": "CEL expression evaluated after each iteration. Loop exits when true. Always YAML double-quoted."
            },
            "maxIterations": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000000,
              "default": 100000,
              "description": "Maximum number of iterations. Raises ResourceLimitError if exceeded."
            }
          }
        }
      }
    },
    "whileStep": {
      "description": "Loop while a condition remains true",
      "type": "object",
      "required": [
        "while"
      ],
      "additionalProperties": false,
      "properties": {
        "while": {
          "type": "object",
          "required": [
            "condition",
            "do"
          ],
          "additionalProperties": false,
          "properties": {
            "_id_": {
              "$ref": "#/$defs/stepId"
            },
            "_label_": {
              "type": "string",
              "maxLength": 500
            },
            "_notes_": {
              "type": "string",
              "description": "Human-readable documentation. Plain text or Markdown.",
              "maxLength": 10000
            },
            "_meta_": {
              "$ref": "#/$defs/meta"
            },
            "timeout": {
              "$ref": "#/$defs/timeout"
            },
            "condition": {
              "$ref": "#/$defs/expression"
            },
            "do": {
              "$ref": "#/$defs/stepListOrStep"
            },
            "maxIterations": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000000,
              "default": 100000,
              "description": "Maximum number of iterations. Raises ResourceLimitError if exceeded."
            }
          }
        }
      }
    },
    "tryStep": {
      "description": "Error handling with catch clauses and optional finally block",
      "type": "object",
      "required": [
        "try"
      ],
      "additionalProperties": false,
      "properties": {
        "try": {
          "type": "object",
          "required": [
            "do"
          ],
          "additionalProperties": false,
          "anyOf": [
            {
              "required": [
                "catch"
              ]
            },
            {
              "required": [
                "finally"
              ]
            }
          ],
          "properties": {
            "_id_": {
              "$ref": "#/$defs/stepId"
            },
            "_label_": {
              "type": "string",
              "maxLength": 500
            },
            "_notes_": {
              "type": "string",
              "description": "Human-readable documentation. Plain text or Markdown.",
              "maxLength": 10000
            },
            "_meta_": {
              "$ref": "#/$defs/meta"
            },
            "condition": {
              "$ref": "#/$defs/expression",
              "description": "Guard condition. Step is skipped entirely (including finally:) if this CEL expression evaluates to false."
            },
            "do": {
              "$ref": "#/$defs/stepListOrStep"
            },
            "catch": {
              "$ref": "#/$defs/catchHandler"
            },
            "finally": {
              "$ref": "#/$defs/stepListOrStep"
            }
          }
        }
      }
    },
    "setStep": {
      "description": "Assign values to one or more data elements",
      "type": "object",
      "required": [
        "set"
      ],
      "additionalProperties": false,
      "properties": {
        "set": {
          "type": "object",
          "description": "Assign variables. Keys are variable names: plain names for flow-local (counter), LOCAL.-prefixed (LOCAL.counter), CONTEXT.-prefixed (CONTEXT.correlation_id), or GLOBAL.-prefixed (GLOBAL.request_count). LOCAL. is equivalent to no prefix. Keys MUST NOT be the reserved binding names LOCAL, CONTEXT, GLOBAL, RUNTIME, SERVICES, ERROR, EVENT, MIGRATION, YIELD, RESULT, RESULTS, or RESOURCES (without a dot suffix). Keys MUST NOT use the ENV. or RUNTIME. prefix (those scopes are read-only).",
          "minProperties": 1,
          "propertyNames": {
            "allOf": [
              {
                "not": {
                  "anyOf": [
                    {
                      "enum": [
                        "LOCAL",
                        "CONTEXT",
                        "GLOBAL",
                        "RUNTIME",
                        "SERVICES",
                        "ERROR",
                        "EVENT",
                        "MIGRATION",
                        "YIELD",
                        "RESULT",
                        "RESULTS",
                        "RESOURCES"
                      ]
                    },
                    {
                      "pattern": "^(ENV|RUNTIME|SECRET)\\."
                    }
                  ]
                }
              },
              { "not": { "pattern": "\\$" } },
              { "not": { "pattern": "-" } }
            ]
          },
          "properties": {
            "_id_": {
              "$ref": "#/$defs/stepId"
            },
            "_label_": {
              "type": "string",
              "description": "Human-readable step name for logs and visualization.",
              "maxLength": 500
            },
            "_meta_": {
              "$ref": "#/$defs/meta"
            },
            "_notes_": {
              "type": "string",
              "description": "Human-readable documentation for this entry. Plain text or Markdown. Underscore-wrapped to avoid collision with variable names.",
              "maxLength": 10000
            }
          },
          "additionalProperties": {
            "description": "CEL expression (= prefix), pass-through literal, or typed declaration ($kind + $value).",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "boolean"
              },
              {
                "type": "null"
              },
              {
                "type": "array"
              },
              {
                "$ref": "#/$defs/typedVar"
              },
              {
                "type": "object",
                "not": {
                  "required": [
                    "$kind"
                  ]
                }
              }
            ]
          }
        }
      }
    },
    "logStep": {
      "description": "Emit a log message at the specified level",
      "type": "object",
      "required": [
        "log"
      ],
      "additionalProperties": false,
      "properties": {
        "log": {
          "oneOf": [
            {
              "type": "string",
              "description": "Shorthand: CEL expression for the log message. Level defaults to info."
            },
            {
              "type": "object",
              "required": [
                "message"
              ],
              "additionalProperties": false,
              "properties": {
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "condition": {
                  "$ref": "#/$defs/expression",
                  "description": "Guard condition. Step is skipped if this CEL expression evaluates to false."
                },
                "level": {
                  "description": "Log level. Static: TRACE, DEBUG, INFO (default), WARN, ERROR. CEL: expression (=) evaluating to one of those strings at runtime (e.g., \"=GLOBAL.verbose ? DEBUG : INFO\").",
                  "oneOf": [
                    {
                      "type": "string",
                      "enum": [
                        "TRACE",
                        "DEBUG",
                        "INFO",
                        "WARN",
                        "ERROR"
                      ]
                    },
                    {
                      "type": "string",
                      "pattern": "^=",
                      "minLength": 2,
                      "maxLength": 10000
                    }
                  ],
                  "default": "INFO"
                },
                "message": {
                  "$ref": "#/$defs/expression"
                }
              }
            }
          ]
        }
      }
    },
    "logWarnStep": {
      "description": "Shorthand for log with level: WARN. Desugars to: log: { level: WARN, message: <value> }.",
      "type": "object",
      "required": [
        "logWarn"
      ],
      "additionalProperties": false,
      "properties": {
        "_id_": {
          "$ref": "#/$defs/stepId"
        },
        "_label_": {
          "type": "string",
          "maxLength": 500
        },
        "_notes_": {
          "type": "string",
          "description": "Human-readable documentation. Plain text or Markdown.",
          "maxLength": 10000
        },
        "_meta_": {
          "$ref": "#/$defs/meta"
        },
        "condition": {
          "$ref": "#/$defs/expression",
          "description": "Guard condition. Step is skipped if this CEL expression evaluates to false."
        },
        "logWarn": {
          "$ref": "#/$defs/expression",
          "description": "CEL expression or template for the log message. Level is WARN."
        }
      }
    },
    "logErrorStep": {
      "description": "Shorthand for log with level: ERROR. Desugars to: log: { level: ERROR, message: <value> }.",
      "type": "object",
      "required": [
        "logError"
      ],
      "additionalProperties": false,
      "properties": {
        "_id_": {
          "$ref": "#/$defs/stepId"
        },
        "_label_": {
          "type": "string",
          "maxLength": 500
        },
        "_notes_": {
          "type": "string",
          "description": "Human-readable documentation. Plain text or Markdown.",
          "maxLength": 10000
        },
        "_meta_": {
          "$ref": "#/$defs/meta"
        },
        "condition": {
          "$ref": "#/$defs/expression",
          "description": "Guard condition. Step is skipped if this CEL expression evaluates to false."
        },
        "logError": {
          "$ref": "#/$defs/expression",
          "description": "CEL expression or template for the log message. Level is ERROR."
        }
      }
    },
    "switchStep": {
      "description": "Multi-branch value matching with optional default",
      "type": "object",
      "required": [
        "switch"
      ],
      "additionalProperties": false,
      "properties": {
        "switch": {
          "type": "object",
          "required": [
            "value",
            "match"
          ],
          "additionalProperties": false,
          "properties": {
            "_id_": {
              "$ref": "#/$defs/stepId"
            },
            "_label_": {
              "type": "string",
              "maxLength": 500
            },
            "_notes_": {
              "type": "string",
              "description": "Human-readable documentation. Plain text or Markdown.",
              "maxLength": 10000
            },
            "_meta_": {
              "$ref": "#/$defs/meta"
            },
            "condition": {
              "$ref": "#/$defs/expression",
              "description": "Guard condition. Step is skipped if this CEL expression evaluates to false."
            },
            "value": {
              "$ref": "#/$defs/expression",
              "description": "Expression evaluated once and compared against each match: key."
            },
            "match": {
              "type": "object",
              "minProperties": 1,
              "description": "Map of literal match values to step lists. Keys are string literals (no = prefix, no {{ }}). YAML key order = evaluation order.",
              "propertyNames": {
                "not": {
                  "anyOf": [
                    {
                      "pattern": "^="
                    },
                    {
                      "pattern": "\\{\\{"
                    }
                  ]
                }
              },
              "additionalProperties": {
                "$ref": "#/$defs/stepListOrStep"
              }
            },
            "default": {
              "$ref": "#/$defs/stepListOrStep"
            }
          }
        }
      }
    },
    "assertStep": {
      "description": "Assert a condition is true. Short form: bare CEL condition expression. Full form adds a custom message expression. Throws AssertionError if false. Engines MUST NOT elide or skip assert evaluation.",
      "type": "object",
      "required": [
        "assert"
      ],
      "additionalProperties": false,
      "properties": {
        "assert": {
          "oneOf": [
            {
              "$ref": "#/$defs/expression",
              "description": "Shorthand: CEL condition expression (= prefix required). Throws AssertionError with an auto-generated message if false."
            },
            {
              "type": "object",
              "required": [
                "condition"
              ],
              "additionalProperties": false,
              "properties": {
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "condition": {
                  "$ref": "#/$defs/expression"
                },
                "message": {
                  "$ref": "#/$defs/expression"
                }
              }
            }
          ]
        }
      }
    },
    "throwStep": {
      "description": "Raise an error with type, message, and optional data",
      "type": "object",
      "required": [
        "throw"
      ],
      "additionalProperties": false,
      "properties": {
        "throw": {
          "type": "object",
          "required": [
            "error"
          ],
          "additionalProperties": false,
          "properties": {
            "_id_": {
              "$ref": "#/$defs/stepId"
            },
            "_label_": {
              "type": "string",
              "maxLength": 500
            },
            "_notes_": {
              "type": "string",
              "description": "Human-readable documentation. Plain text or Markdown.",
              "maxLength": 10000
            },
            "_meta_": {
              "$ref": "#/$defs/meta"
            },
            "condition": {
              "$ref": "#/$defs/expression",
              "description": "Guard condition. Throw is skipped if this CEL expression evaluates to false."
            },
            "error": {
              "type": "string",
              "minLength": 1,
              "pattern": "^[A-Z].*Error$"
            },
            "message": {
              "$ref": "#/$defs/expression"
            },
            "data": {
              "description": "Structured payload attached to the error. Accessible in catch blocks via error.data.<key> (e.g. error.data.field).",
              "type": "object",
              "additionalProperties": {},
              "maxProperties": 100
            }
          }
        }
      }
    },
    "returnStep": {
      "description": "Terminate flow execution and return output values",
      "type": "object",
      "required": [
        "return"
      ],
      "additionalProperties": false,
      "properties": {
        "return": {
          "oneOf": [
            {
              "type": "null",
              "description": "Bare return: terminate flow, use already-set output variables."
            },
            {
              "description": "Single-value return: a bare CEL expression. Use when the flow's output contract is a single-value outputContract. Examples: raw_image (variable), result.content (field access), \"'Hello World'\" (CEL string literal).",
              "type": "string",
              "minLength": 1
            },
            {
              "description": "Multi-value return: map of output param names to expressions or literals. Null values use same-name convention: key: (null) → key: =key.",
              "type": "object",
              "properties": {
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "description": "Human-readable step name for logs and visualization.",
                  "maxLength": 500
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation for this entry. Plain text or Markdown. Underscore-wrapped to avoid collision with variable names.",
                  "maxLength": 10000
                },
                "condition": {
                  "$ref": "#/$defs/expression",
                  "description": "Guard condition. Return is skipped if this CEL expression evaluates to false."
                }
              },
              "additionalProperties": {}
            },
            {
              "description": "List form: each name produces output_name: =output_name mapping. Equivalent to object form with all-null values.",
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^[a-z][a-z0-9_]*$"
              },
              "minItems": 1
            }
          ]
        }
      }
    },
    "yieldStep": {
      "description": "Produce a value to the flow's caller without terminating. Semantics depend on consumer: onYield handler receives each value with backpressure; $yields in output collects all values into a list; no consumer discards the value. Inside transaction: true groups, yields are buffered until commit.",
      "type": "object",
      "required": [
        "yield"
      ],
      "additionalProperties": false,
      "properties": {
        "yield": {
          "oneOf": [
            {
              "type": "null",
              "description": "Bare yield: sends null (heartbeat/keepalive)."
            },
            {
              "description": "Single-value yield: a bare CEL expression.",
              "type": "string",
              "minLength": 1
            },
            {
              "description": "Multi-value yield: map of param names to expressions or literals. Dollar-prefixed keys are annotations.",
              "type": "object",
              "properties": {
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "description": "Human-readable step name for logs and visualization.",
                  "maxLength": 500
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Underscore-wrapped to avoid collision with yield param names.",
                  "maxLength": 10000
                },
                "condition": {
                  "$ref": "#/$defs/expression",
                  "description": "Guard condition. Yield is skipped if this CEL expression evaluates to false."
                }
              },
              "additionalProperties": {}
            }
          ]
        }
      }
    },
    "onYield": {
      "description": "Stream consumer handler on action steps. Processes each value yielded by a sub-flow or service. Full form: per-value handler with variable binding and optional filter. FORWARD shorthand: pass-through all yields verbatim to the containing flow's caller. Mutually exclusive with $yields in step output.",
      "oneOf": [
        {
          "type": "object",
          "required": [
            "do"
          ],
          "additionalProperties": false,
          "properties": {
            "as": {
              "description": "Variable binding for the yielded value. String: single-value binding (snake_case name). Object: multi-value destructuring (yield key → local variable name).",
              "oneOf": [
                {
                  "type": "string",
                  "pattern": "^[a-z][a-z0-9_]*$"
                },
                {
                  "type": "object",
                  "additionalProperties": {
                    "type": "string",
                    "pattern": "^(GLOBAL\\.|CONTEXT\\.|LOCAL\\.)?[a-z][a-z0-9_]*$"
                  }
                }
              ]
            },
            "index": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9_]*$",
              "description": "Optional variable name for the 0-based yield counter. Incremented per yield invocation."
            },
            "condition": {
              "$ref": "#/$defs/expression",
              "description": "Post-receive filter (CEL). If false, value is discarded and producer is unblocked without running do:."
            },
            "buffer": {
              "type": "integer",
              "minimum": 0,
              "default": 0,
              "description": "Yield buffer size. 0 (default) = synchronous backpressure (producer blocks until handler completes). N = producer may get up to N items ahead before blocking. Only meaningful on onYield (streaming); not available on $yields (materialized list collects all values regardless)."
            },
            "do": {
              "$ref": "#/$defs/stepListOrStep"
            }
          }
        },
        {
          "type": "string",
          "const": "FORWARD",
          "description": "FORWARD shorthand: pass-through all yields verbatim to this flow's caller. Requires the containing flow to declare yields:."
        }
      ]
    },
    "yieldsContract": {
      "description": "Flow-level streaming output contract. Declares the element kind of the yield stream. Consumed via onYield (streaming) or $yields in step output (materialized list). Independent of output: (which declares the return value from return directives).",
      "oneOf": [
        {
          "description": "Single-value: each yield produces one typed value.",
          "type": "object",
          "required": [
            "$kind"
          ],
          "additionalProperties": false,
          "properties": {
            "$kind": {
              "$ref": "#/$defs/varKindExpr"
            },
            "$type": {
              "$ref": "#/$defs/mimeType",
              "description": "MIME content type for the yielded value."
            },
            "_notes_": {
              "type": "string",
              "maxLength": 10000
            },
            "$schema": {
              "$ref": "#/$defs/schemaRef"
            },
            "$enum": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "minItems": 1,
              "uniqueItems": true
            }
          },
          "not": {
            "required": [
              "$schema",
              "$enum"
            ]
          }
        },
        {
          "description": "Multi-param: each yield produces a named-param map.",
          "type": "object",
          "required": [
            "params"
          ],
          "additionalProperties": false,
          "properties": {
            "params": {
              "$ref": "#/$defs/paramContract"
            }
          }
        }
      ]
    },
    "waitStep": {
      "description": "Pause execution for a specified duration",
      "type": "object",
      "required": [
        "wait"
      ],
      "additionalProperties": false,
      "properties": {
        "wait": {
          "oneOf": [
            {
              "$ref": "#/$defs/expression",
              "description": "Duration literal matching ^\\d+(ms|[smhd])$ (e.g. '5s', '500ms'), or a bare CEL expression producing a duration string or integer ms. The engine matches the pattern first; non-matching strings are evaluated as CEL."
            },
            {
              "type": "integer",
              "minimum": 0,
              "description": "Wait duration in milliseconds."
            },
            {
              "type": "object",
              "required": [
                "duration"
              ],
              "additionalProperties": false,
              "description": "Full form: allows label, meta, and condition alongside the duration.",
              "properties": {
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "condition": {
                  "$ref": "#/$defs/expression",
                  "description": "Guard condition. Wait is skipped if this CEL expression evaluates to false."
                },
                "duration": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/expression"
                    },
                    {
                      "type": "integer",
                      "minimum": 0
                    }
                  ],
                  "description": "Duration literal matching ^\\d+(ms|[smhd])$ (e.g. '5s', '500ms'), a bare CEL expression producing a duration string, or an integer millisecond count. The engine matches the pattern first; non-matching strings are evaluated as CEL."
                },
                "maxDuration": {
                  "$ref": "#/$defs/durationOrExpr",
                  "description": "Maximum wait duration (DURATION literal, e.g. '24h'). Default: 24h. Computed durations exceeding maxDuration raise ResourceLimitError at runtime. Literal durations exceeding maxDuration raise ValidationError at load time. Engine configurable maximum: 30d."
                }
              }
            }
          ]
        }
      }
    },
    "breakStep": {
      "description": "Exit the enclosing loop (forEach/while/repeat)",
      "type": "object",
      "required": [
        "break"
      ],
      "additionalProperties": false,
      "properties": {
        "break": {
          "oneOf": [
            {
              "type": "null"
            },
            {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "condition": {
                  "$ref": "#/$defs/expression"
                }
              }
            }
          ]
        }
      }
    },
    "continueStep": {
      "description": "Skip to the next iteration of the enclosing loop",
      "type": "object",
      "required": [
        "continue"
      ],
      "additionalProperties": false,
      "properties": {
        "continue": {
          "oneOf": [
            {
              "type": "null"
            },
            {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "condition": {
                  "$ref": "#/$defs/expression"
                }
              }
            }
          ]
        }
      }
    },
    "stepId": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9_]*$",
      "description": "Stable machine-readable step identifier. snake_case. Unique within the flow document. Used for cross-referencing, graph node keys, test assertions, diff tracking. Uniqueness enforced by SA-ID-1."
    },
    "serviceRef": {
      "description": "Service reference. Two forms: (1) bare service alias name (e.g., db, payment_svc — resolved to SERVICES.<name> by engine), (2) CEL expression with = prefix (e.g., =SERVICES.db, =SERVICES[ENV.AI_SERVICE]). Bare alias names MUST match ^[a-z][a-z0-9_]*$ (spec §2.6).",
      "type": "string",
      "oneOf": [
        {
          "type": "string",
          "pattern": "^[a-z][a-z0-9_]*$"
        },
        {
          "type": "string",
          "pattern": "^=",
          "minLength": 2,
          "maxLength": 10000
        }
      ]
    },
    "operationRef": {
      "description": "Operation (method) name to invoke on the service. Required. Two forms: (1) bare operation name — a valid method identifier following programming-language conventions (e.g., query, findAll, get_status), (2) CEL expression with = prefix for dynamic dispatch (e.g., =selected_op). Bare names follow standard identifier rules: start with a letter or underscore, then alphanumeric/underscore. SECURITY: Operation names SHOULD be static strings for auditability. Dynamic dispatch (= prefix) prevents static analysis from enumerating callable operations — SA-REQ-17 flags dynamic operation names as a warning. Engines SHOULD log dynamic operation dispatch at INFO level.",
      "type": "string",
      "oneOf": [
        {
          "type": "string",
          "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$"
        },
        {
          "type": "string",
          "pattern": "^=",
          "minLength": 2,
          "maxLength": 10000
        }
      ]
    },
    "parseAsExpr": {
      "description": "parseAs field. Two forms: (1) string form — same as contentTypeExpr (bare constant, MIME type, or CEL expression); (2) object form — one or more of $kind (parse format + result kind annotation), $type (MIME type metadata), $name (filename metadata).",
      "oneOf": [
        {
          "$ref": "#/$defs/contentTypeExpr"
        },
        {
          "type": "object",
          "minProperties": 1,
          "additionalProperties": false,
          "description": "Object form: optional $kind specifies parse format and result kind; $type and $name attach metadata to the parsed result.",
          "properties": {
            "$kind": {
              "$ref": "#/$defs/varKindExpr",
              "description": "Parse format and result kind. Same parse-format values as the string form (JSON, YAML, XML, CSV, TSV, TEXT, BINARY). Also accepts any varKind value for kind annotation without parsing (e.g., $kind: MAP)."
            },
            "$type": {
              "type": "string",
              "minLength": 1,
              "description": "MIME type to attach to the result value as $type metadata. Useful for semantic types like application/vnd.api+json. Does not affect parsing."
            },
            "$name": {
              "type": "string",
              "minLength": 1,
              "description": "Filename to attach to the result value as $name metadata (CEL expression or plain string). Useful when the parsed value will later be used as a mail attachment or storage put with a filename."
            }
          }
        }
      ]
    },
    "contentTypeExpr": {
      "description": "Content type for stdout/stderr. Three valid forms: (1) bare UPPERCASE constant (JSON, YAML, XML, CSV, TSV, TEXT, BINARY), (2) bare MIME type string (image/png, application/json), (3) CEL expression with = prefix for runtime values (=my_type, ='video/mp4'). Unknown uppercase identifiers fail fast.",
      "type": "string",
      "oneOf": [
        {
          "type": "string",
          "enum": [
            "JSON",
            "YAML",
            "XML",
            "CSV",
            "TSV",
            "TEXT",
            "BINARY"
          ]
        },
        {
          "type": "string",
          "pattern": "^[a-zA-Z][a-zA-Z0-9+.-]*/[a-zA-Z0-9][a-zA-Z0-9!#$&+.-]*$"
        },
        {
          "type": "string",
          "pattern": "^=",
          "minLength": 2,
          "maxLength": 10000
        }
      ]
    },
    "varKindExpr": {
      "description": "Variable kind identifier. Three valid forms: (1) bare UPPERCASE constant (STRING, TEXT, MARKDOWN, NUMBER, INTEGER, BOOLEAN, BINARY, ARRAY, MAP, DIRECTORY, ANY, EMAIL, URL, URI, UUID, IP, FILENAME), (2) compound shorthand (JSON, YAML, XML, CSV, TSV), (3) CEL = expression for runtime kind (=my_kind). Unknown uppercase identifiers fail fast.",
      "type": "string",
      "oneOf": [
        {
          "type": "string",
          "enum": [
            "STRING",
            "TEXT",
            "MARKDOWN",
            "NUMBER",
            "INTEGER",
            "BOOLEAN",
            "BINARY",
            "ARRAY",
            "MAP",
            "DIRECTORY",
            "ANY",
            "EMAIL",
            "URL",
            "URI",
            "UUID",
            "IP",
            "FILENAME",
            "JSON",
            "YAML",
            "XML",
            "CSV",
            "TSV"
          ]
        },
        {
          "type": "string",
          "pattern": "^=",
          "minLength": 2,
          "maxLength": 10000
        }
      ]
    },
    "encodingExpr": {
      "description": "Encoding identifier for binary↔string transformation on typedVar/set declarations. Bare static constant or CEL expression with = prefix. Static constants: BASE64, BASE64URL, HEX. UTF variants (UTF8, UTF16BE/LE, UTF32BE/LE) are $charset values, not $encoding — use them only as CEL encode()/decode() function arguments. Unknown uppercase identifiers fail fast.",
      "type": "string",
      "oneOf": [
        {
          "type": "string",
          "enum": [
            "BASE64",
            "BASE64URL",
            "HEX"
          ]
        },
        {
          "type": "string",
          "pattern": "^=",
          "minLength": 2,
          "maxLength": 10000
        }
      ]
    },
    "meta": {
      "description": "Application-defined metadata. Ignored by the engine. Use for tooling-specific data (e.g. diagram coordinates, annotations).",
      "type": "object",
      "additionalProperties": true
    },
    "scope": {
      "description": "Scope level. Default varies by context: emit/waitFor/lock default to LOCAL; circuitBreaker/rateLimit default to GLOBAL at flow level, LOCAL at action level. See specification for context-specific defaults. LOCAL: within this flow instance only. CONTEXT: across the execution chain (root flow + all transitive sub-flows). GLOBAL: engine-wide (all flow instances).",
      "oneOf": [
        {
          "type": "string",
          "enum": [
            "LOCAL",
            "CONTEXT",
            "GLOBAL"
          ]
        },
        {
          "type": "string",
          "pattern": "^=",
          "minLength": 2,
          "maxLength": 10000
        }
      ]
    },
    "eventName": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9_]*$",
      "minLength": 1,
      "description": "Event type. Static string (not a CEL expression). Must be snake_case."
    },
    "emitStep": {
      "description": "Fire an event with optional data payload. Leaf directive — no sub-steps.",
      "type": "object",
      "required": [
        "emit"
      ],
      "additionalProperties": false,
      "properties": {
        "emit": {
          "oneOf": [
            {
              "$ref": "#/$defs/eventName",
              "description": "Shorthand: event type string. Scope defaults to self, no data."
            },
            {
              "type": "object",
              "required": [
                "event"
              ],
              "additionalProperties": false,
              "properties": {
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "condition": {
                  "$ref": "#/$defs/expression",
                  "description": "Guard condition. Emit is skipped if this CEL expression evaluates to false."
                },
                "event": {
                  "$ref": "#/$defs/eventName"
                },
                "scope": {
                  "$ref": "#/$defs/scope",
                  "default": "LOCAL"
                },
                "data": {
                  "$ref": "#/$defs/stepParams"
                }
              }
            }
          ]
        }
      }
    },
    "waitForStep": {
      "description": "Block until a matching event is received. Leaf directive — no sub-steps. Throws TimeoutError if timeout is exceeded.",
      "type": "object",
      "required": [
        "waitFor"
      ],
      "additionalProperties": false,
      "properties": {
        "waitFor": {
          "oneOf": [
            {
              "$ref": "#/$defs/eventName",
              "description": "Shorthand: event type string. Scope defaults to self, no output, no timeout."
            },
            {
              "type": "object",
              "required": [
                "event"
              ],
              "additionalProperties": false,
              "properties": {
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "event": {
                  "$ref": "#/$defs/eventName"
                },
                "scope": {
                  "$ref": "#/$defs/scope",
                  "default": "LOCAL"
                },
                "timeout": {
                  "$ref": "#/$defs/timeout"
                },
                "condition": {
                  "$ref": "#/$defs/expression",
                  "description": "Post-receive filter, analogous to catch clause condition. Evaluated BEFORE capture binding, with event data available as EVENT.DATA.*. If false, the event is discarded and the wait continues."
                },
                "source": {
                  "$ref": "#/$defs/expression",
                  "description": "Engine-level source filter. CEL expression evaluating to a FlowHandle (instance-level filter: events from that instance and its transitive sub-flow descendants) or a string (flow-path filter: matches EVENT.SOURCE against canonical flow path). When omitted, all events of the matching type are delivered."
                },
                "capture": {
                  "$ref": "#/$defs/captureMapping"
                }
              }
            }
          ]
        }
      }
    },
    "lockStep": {
      "description": "Read/write lock block. mode: EXCLUSIVE (default) acquires a write lock — single holder, buffered global.*/context.* writes committed atomically on success, discarded on error. mode: SHARED acquires a read lock — multiple concurrent readers allowed, no exclusive holder can enter concurrently. name is a CEL expression identifying the lock — static literal (\"'balance'\") or dynamic (\"'file:' + path\"). Reentrant: nested lock with same resolved name is a no-op for acquisition.",
      "type": "object",
      "required": [
        "lock"
      ],
      "additionalProperties": false,
      "properties": {
        "lock": {
          "type": "object",
          "required": [
            "name",
            "do"
          ],
          "additionalProperties": false,
          "properties": {
            "name": {
              "$ref": "#/$defs/expression",
              "description": "Lock identifier (CEL expression). Steps with the same evaluated name and scope contend on the same lock. May be a static string literal (e.g., \"'account_balance'\") or a dynamic expression (e.g., \"'file:' + file_path\")."
            },
            "_id_": {
              "$ref": "#/$defs/stepId"
            },
            "_label_": {
              "type": "string",
              "maxLength": 500
            },
            "_notes_": {
              "type": "string",
              "description": "Human-readable documentation. Plain text or Markdown.",
              "maxLength": 10000
            },
            "_meta_": {
              "$ref": "#/$defs/meta"
            },
            "condition": {
              "$ref": "#/$defs/expression",
              "description": "Guard condition. Step is skipped if this CEL expression evaluates to false."
            },
            "mode": {
              "description": "Lock mode. Static: SHARED (read lock, multiple concurrent readers) or EXCLUSIVE (write lock, single holder — default). CEL: expression (=) evaluating to one of those strings at runtime (e.g., \"=is_read_only ? SHARED : EXCLUSIVE\").",
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "SHARED",
                    "EXCLUSIVE"
                  ]
                },
                {
                  "type": "string",
                  "pattern": "^=",
                  "minLength": 2,
                  "maxLength": 10000
                }
              ],
              "default": "EXCLUSIVE"
            },
            "scope": {
              "$ref": "#/$defs/scope",
              "default": "LOCAL"
            },
            "timeout": {
              "$ref": "#/$defs/timeout"
            },
            "do": {
              "$ref": "#/$defs/stepListOrStep"
            }
          }
        }
      }
    },
    "waitUntilStep": {
      "description": "Block until a specific timestamp is reached. Shorthand: bare CEL expression or timestamp string. Full form: object with timestamp property. If the target timestamp is already in the past, the step completes immediately (no error).",
      "type": "object",
      "required": [
        "waitUntil"
      ],
      "additionalProperties": false,
      "properties": {
        "waitUntil": {
          "oneOf": [
            {
              "$ref": "#/$defs/expression",
              "description": "Shorthand: CEL expression or timestamp string evaluating to the target timestamp."
            },
            {
              "type": "object",
              "required": [
                "timestamp"
              ],
              "additionalProperties": false,
              "properties": {
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "condition": {
                  "$ref": "#/$defs/expression",
                  "description": "Guard condition. Step is skipped if this CEL expression evaluates to false."
                },
                "timeout": {
                  "$ref": "#/$defs/timeout"
                },
                "timestamp": {
                  "$ref": "#/$defs/expression",
                  "description": "CEL expression evaluating to a timestamp. The step blocks until the current time reaches this timestamp."
                }
              }
            }
          ]
        }
      }
    },
    "cancelStep": {
      "description": "Cancel a sub-flow instance referenced by a FlowHandle. Sends a cancellation signal; the sub-flow's finally: still runs (graceful cancellation). If already completed/cancelled, no-op. Non-blocking — returns immediately. Only valid for sub-flows started with handle: on a run step.",
      "type": "object",
      "required": [
        "cancel"
      ],
      "additionalProperties": false,
      "properties": {
        "cancel": {
          "oneOf": [
            {
              "$ref": "#/$defs/expression",
              "description": "Shorthand: CEL expression evaluating to a FlowHandle (e.g., =payment_proc)."
            },
            {
              "type": "object",
              "required": [
                "handle"
              ],
              "additionalProperties": false,
              "properties": {
                "_id_": {
                  "$ref": "#/$defs/stepId"
                },
                "_label_": {
                  "type": "string",
                  "maxLength": 500
                },
                "_notes_": {
                  "type": "string",
                  "description": "Human-readable documentation. Plain text or Markdown.",
                  "maxLength": 10000
                },
                "_meta_": {
                  "$ref": "#/$defs/meta"
                },
                "handle": {
                  "$ref": "#/$defs/expression",
                  "description": "CEL expression evaluating to a FlowHandle."
                },
                "condition": {
                  "$ref": "#/$defs/expression",
                  "description": "Guard condition. Cancel is skipped if this CEL expression evaluates to false."
                }
              }
            }
          ]
        }
      }
    },
    "triggerDef": {
      "description": "Flow trigger: starts a new flow instance on an event, cron schedule, or human-readable schedule.",
      "oneOf": [
        {
          "type": "object",
          "required": [
            "event"
          ],
          "additionalProperties": false,
          "properties": {
            "event": {
              "$ref": "#/$defs/eventName"
            },
            "condition": {
              "$ref": "#/$defs/expression",
              "description": "Optional filter evaluated against the event. Event data available as EVENT.DATA.*. If false, the event does not trigger a new flow instance."
            },
            "concurrency": {
              "type": "string",
              "enum": ["ALLOW", "SKIP", "QUEUE", "REPLACE"],
              "default": "ALLOW",
              "description": "Trigger overlap behavior when a trigger fires while a previous invocation is still running."
            },
            "maxQueued": {
              "type": "integer",
              "minimum": 1,
              "default": 10,
              "description": "Maximum queue depth when concurrency is QUEUE. Ignored for other concurrency modes."
            }
          }
        },
        {
          "type": "object",
          "required": [
            "cron"
          ],
          "additionalProperties": false,
          "properties": {
            "cron": {
              "type": "string",
              "pattern": "^[0-9*,/\\-]+ [0-9*,/\\-]+ [0-9*,/\\-]+ [0-9*,/\\-A-Za-z]+ [0-9*,/\\-A-Za-z]+$",
              "description": "Standard 5-field cron expression: minute hour day-of-month month day-of-week. e.g. '0 9 * * MON-FRI'"
            },
            "concurrency": {
              "type": "string",
              "enum": ["ALLOW", "SKIP", "QUEUE", "REPLACE"],
              "default": "ALLOW",
              "description": "Trigger overlap behavior when a trigger fires while a previous invocation is still running."
            },
            "maxQueued": {
              "type": "integer",
              "minimum": 1,
              "default": 10,
              "description": "Maximum queue depth when concurrency is QUEUE. Ignored for other concurrency modes."
            }
          }
        },
        {
          "type": "object",
          "required": [
            "schedule"
          ],
          "additionalProperties": false,
          "properties": {
            "schedule": {
              "type": "string",
              "minLength": 1,
              "description": "Human-readable schedule. Supported patterns: 'every 5m', 'hourly', 'hourly at :30', 'daily at 09:00', 'weekdays at 08:00', 'weekly on monday at 09:00', 'monthly on 1 at 00:00'. Parsed and validated by the engine at runtime."
            },
            "concurrency": {
              "type": "string",
              "enum": ["ALLOW", "SKIP", "QUEUE", "REPLACE"],
              "default": "ALLOW",
              "description": "Trigger overlap behavior when a trigger fires while a previous invocation is still running."
            },
            "maxQueued": {
              "type": "integer",
              "minimum": 1,
              "default": 10,
              "description": "Maximum queue depth when concurrency is QUEUE. Ignored for other concurrency modes."
            }
          }
        }
      ]
    }
  }
}