Skip to content

Workflow YAML reference

The complete top-level workflow schema, action shorthand, and the 6-pass validator.

A Revka workflow is a declarative YAML document: a set of typed steps with dependencies, optional triggers, and execution defaults. The operator-mcp backend parses the YAML against a Pydantic schema, runs a six-pass validator, and only then stores the definition as a revision in Kumiho graph memory and executes it deterministically.

This page is the schema reference for everything above the per-step config block: the top-level fields, the inputs/outputs/triggers shapes, the action shorthand that lets the editor write terse steps, and the validator that gates every save and run. For the body of each step type, see Step types reference; for ${...} interpolation and ${{ ... }} expressions, see Variables, expressions & triggers. If you just want to ship something, start with Your first workflow.

The smallest valid workflow has a name and at least one step:

name: hello-world
version: "1.0"
description: Greet a topic and return the text.
steps:
- id: greet
type: agent
agent:
agent_type: claude
role: researcher
prompt: "Say hello and introduce ${inputs.topic}."

A more complete shape, with typed inputs, named outputs, and a trigger:

name: daily-report
version: "1.0"
description: Summarise a topic and publish the result.
tags: [reporting]
inputs:
- name: topic
type: string
required: true
default: ""
triggers:
- cron: "0 9 * * 1" # every Monday 09:00
# timezone is set via the cron job, not the trigger block
steps:
- id: summarize
action: summarize # shorthand → type: agent, role: summarizer, claude
agent:
prompt: "Summarise ${inputs.topic} in five bullet points."
output_fields: [summary]
- id: report
type: output
depends_on: [summarize]
output:
format: markdown
template: |
# ${inputs.topic}
${summarize.output}
outputs:
- name: result
source: "${report.output}"

These fields live at the root of the document (WorkflowDef). Only name and steps are required.

FieldTypeDefaultMeaning
namestring— (required)Workflow identifier. The gateway slugifies the display name into the Kumiho item id.
versionstring"1.0"Semantic version label, free-form.
descriptionstring""Human-readable purpose; shown in the dashboard list and search.
tagslist<string>[]Classification labels for search and filtering.
triggerslist<Trigger>[]Cron or entity-event conditions that auto-launch the workflow.
inputslist<Input>[]Typed parameters supplied at run time.
outputslist<Output>[]Named results mapped from step output via source.
stepslist<Step>— (required)Ordered step definitions; at least one is required.
default_cwdstring""Default working directory for shell/agent steps when a run does not pass cwd.
default_timeoutnumber300.0Default per-step timeout in seconds.
max_total_timenumber3600.0Safety cap on total run wall-clock time, in seconds (1 hour).
checkpointbooltruePersist run state after each step so a failed run can be retried. See Runs, approvals & checkpoints.

Each entry in inputs is a typed parameter. A run supplies values through the inputs object on the trigger call; anything not supplied falls back to default.

inputs:
- name: topic
type: string # string | number | boolean | list
required: true
default: ""
description: "What to summarise."
FieldTypeDefaultMeaning
namestringParameter name; referenced as ${inputs.<name>}.
typeenumstringOne of string, number, boolean, list.
requiredbooltrueWhether a value must be present at run time.
defaultanynullFallback value when none is supplied.
descriptionstring""Documentation shown in the editor.

A required input with no default must be provided at run time. If the workflow also has triggers that don’t map it, the validator emits an advisory warning — it can still be auto-filled from upstream entity metadata.

outputs map a name to a step result via a ${...} source expression, so callers and downstream workflows get a stable contract:

outputs:
- name: result
source: "${report.output}"
- name: next_episode
source: "${{ int(resolve_cursor.output_data.episode_number) + 1 }}"
description: "Computed cursor for the next run."
FieldTypeDefaultMeaning
namestringOutput name.
sourcestring${...} or ${{ ... }} expression resolved at the end of the run.
descriptionstring""Documentation.

A trigger auto-launches the workflow. There are two shapes in one struct (TriggerDef): set cron for a time-based trigger, or set on_kind for an entity-event trigger. Saving a definition with cron triggers registers cron jobs automatically; deprecating or deleting it removes them.

triggers:
# Time-based
- cron: "0 9 * * 1"
# Entity-event: fire when an upstream output step publishes a matching entity
- on_kind: "qs-arc-plan" # entity kind to watch (exact match)
on_tag: "ready" # revision tag that fires the trigger
on_name_pattern: "qs-*" # optional glob on entity name
on_space: "Revka/WorkflowOutputs/QuantumSoul" # optional space prefix
input_map: # map trigger data → workflow inputs
arc_kref: "${trigger.entity_kref}"
arc_name: "${trigger.metadata.arc_name}"
FieldTypeDefaultMeaning
cronstring""Cron expression for time-based triggers. When set, the entity fields are optional.
on_kindstring""Entity kind to watch (exact match). Required for event triggers.
on_tagstring"ready"Revision tag that fires the trigger (exact match).
on_name_patternstring""Optional glob on entity name ("" = any).
on_spacestring""Optional space path prefix filter ("" = any).
input_mapmap<string,string>{}Maps a workflow input name to a ${trigger.*} template.

Event-trigger filters are cumulative (AND). If input_map is omitted, metadata keys on the triggering entity are auto-mapped to matching workflow input names. Full mechanics — including the chaining pattern — are in Variables, expressions & triggers.

Each step shares a common envelope (StepDef) and exactly one type-specific config block named after its type. The full body of every step type is documented in Step types reference; the envelope fields below apply to all of them.

steps:
- id: research # unique within the workflow (required)
name: "Research phase" # display label; defaults to id
type: agent # the step type (or omit and use `action`)
depends_on: [resolve_topic]
skills:
- "kref://CognitiveMemory/Skills/some-skill.skilldef"
retry: 1 # retry up to N times after the first attempt
retry_delay: 10 # seconds between retries
timeout: 600 # step-level override, pushed into the type config
agent:
prompt: "..."
FieldTypeDefaultMeaning
idstring— (required)Unique step identifier, referenced as ${id.output}.
namestringidDisplay label for the dashboard graph.
typeenumagentOne of the step types. May be omitted when action is set.
depends_onlist<string>[]Step ids that must finish first. Sibling steps with no depends_on run in parallel naturally.
actionstring""Shorthand that infers type and agent defaults.
agent_hintslist<string>[]Override the agent type/role chosen by action (for example [codex]).
skillslist<string>[]Skill krefs injected into an agent step.
assignstring""Pre-assigned agent pool template; wired to agent.template when that is unset.
descriptionstring""Used as the prompt when an action step has no explicit agent.prompt.
compressionboolfalseCompress large output before handing it to downstream steps.
retryint (0–5)0Retries after the first failed attempt.
retry_delaynumber5.0Seconds to wait between retries.
timeoutnumberinheritedStep-level timeout; pushed into the agent/shell/python/email/image/a2a/group_chat config.

The type field accepts these values (StepType). Each maps to a config block of the same name:

CategoryTypes
Agent & codeagent, shell, python, compute, image
Communicationemail, notify, human_approval, human_input
Control flowconditional, parallel, goto, for_each
Output & dataoutput, resolve, tag, deprecate
Kumiho graphkumiho_context, kumiho_bundle_update, kumiho_patch_apply
Orchestrationmap_reduce, supervisor, group_chat, handoff
Externala2a, manus

See Step types reference for the fields of each.

The editor and hand-written workflows can skip type and the agent block entirely by setting action. The schema maps each action name to a step type and, for agent actions, a default role and agent type. You can still add an agent: block to override the prompt and other fields, and agent_hints overrides the agent type.

# Terse form
- id: research
action: research # → type: agent, role: researcher, agent_type: claude
agent_hints: [codex] # override: run on codex instead of claude
description: "Research the competitive landscape."
# Equivalent explicit form
- id: research
type: agent
agent:
agent_type: codex
role: researcher
prompt: "Research the competitive landscape."

The complete ACTION_DEFAULTS mapping:

ActionStep typeRoleAgent type
researchagentresearcherclaude
codeagentcodercodex
reviewagentreviewerclaude
deployagentdeployercodex
testagenttestercodex
buildagentbuildercodex
summarizeagentsummarizerclaude
taskagentcoderclaude
notifynotify
approvehuman_approval
gateconditional
human_inputhuman_input
resolveresolve
computecompute
kumiho_contextkumiho_context
kumiho_bundle_updatekumiho_bundle_update
kumiho_patch_applykumiho_patch_apply

How resolution works:

  • type omitted, action settype is inferred from the table.
  • type set to an action alias (for example type: notify) → it is expanded to the real step type (notifynotify; an agent alias → agent) and the original is recorded as the action.
  • No agent block on an agent action → a default AgentStepConfig is synthesized from the action’s role and agent type, using description (or Execute <action> task: <name>) as the prompt.
  • agent_hints override the synthesized agent type: codex/codercodex; claude/researcher/reviewerclaude. A hint of coder, researcher, or reviewer also sets the role. task is the fallback action when an unknown action name is used.

Before a definition is stored or run, the operator’s validate_workflow runs a series of structural passes and returns { valid, errors, warnings, execution_order, all_step_ids }. Any error makes the definition invalid; warnings are advisory and never block a save.

  1. Duplicate step IDs — every step.id must be unique. (Hard duplicates are already rejected at parse time; this pass also reports them in the structured result.)

  2. Dependency references — every depends_on entry must name an existing step. Unknown references are errors and build the adjacency map for the next pass.

  3. Cycle detection — a depth-first topological sort over depends_on. A back-edge is a Dependency cycle detected error. The resulting topological order becomes execution_order (children owned by parallel/for_each wrappers are excluded so they don’t run twice).

  4. Per-step config — type-specific checks. For example: shell needs a command; image and map_reduce/supervisor/group_chat need their required fields; conditional branches and goto/handoff targets must point to real steps (or end); parallel needs ≥2 sub-steps that exist and don’t cross-depend; output requires both entity_name and entity_kind if either is set; agent.output_fields must be valid, non-reserved identifiers.

  5. Variable references — scans every interpolatable string for ${...} and ${{ ... }} references. Built-in namespaces (inputs, loop, env, trigger, for_each, previous, outputs, run_id, rejection) are allowed; any other leading namespace that isn’t a known step id produces a warning (not an error), so first-run patterns that reference not-yet-produced data still save.

  6. Agent dependency & contract checks — two finer agent passes: an agent step whose depends_on entry is never referenced in its prompt is an error (see the caution above); and an ${X.output_data.<field>} reference to an agent step that doesn’t declare <field> in agent.output_fields is a warning.

  7. Triggers — each trigger must be valid: a non-cron trigger needs on_kind and on_tag; a workflow with required inputs that a trigger doesn’t map produces a warning (they may be auto-filled from entity metadata).

  • Dashboard — saving validates automatically; errors render inline in the editor. See Workflows, editor & runs.
  • Gateway APIPOST /api/workflows, PUT /api/workflows/{*kref}, and POST /api/workflows/run/{name} validate before persisting or dispatching and reject with 400 on failure.
  • Operator MCP — the validate_workflow and dry_run_workflow tools (below) let an agent or the Architect panel check a definition without saving it.

The gateway calls the operator’s validator with a fixed timeout. If validation cannot run because of an infrastructure error (the operator is briefly unreachable), it is skipped rather than blocking the save (fail-open). Full HTTP behavior is in Workflows & Architect API.

POST/PUT on definitions and POST /api/workflows/run/{name} return 400 with:

{
"error": "Workflow validation failed: ...",
"valid": false,
"errors": [
{ "message": "depends_on references unknown step 'reserch'", "severity": "error", "step_id": "report", "field": "depends_on" }
],
"warnings": [
{ "message": "Variable reference '${draft.output}' — step 'draft' not found", "severity": "warning", "step_id": "report" }
]
}

Each entry carries message and severity (error or warning), plus optional step_id and field to locate the issue.

Operator tools: the declarative workflow engine

Section titled “Operator tools: the declarative workflow engine”

Workflows are executed by the operator-mcp engine, which exposes the full lifecycle as MCP tools the agent and dashboard call. The validation tools are the ones you reach for while authoring; the rest run and manage workflows. See Operator MCP for how the backend is wired.

Runs the six-pass validator without executing or persisting anything. Accepts a workflow by name, an inline definition, or raw YAML, and returns the ValidationResult (valid, errors, warnings).

validate_workflow(
workflow?, # name of a stored/builtin workflow
workflow_def?, # inline definition object
workflow_yaml?, # raw YAML string
cwd?, # working directory for discovery
)
→ { "valid": true, "errors": [], "warnings": [ ... ] }

Use this to lint a draft before saving. The gateway’s POST /api/architect/validate_yaml route is a thin wrapper over the related propose_workflow_yaml tool for the visual editor’s chat-fallback path.

Validates and previews execution without running any step: it reports the planned execution order, step count, and a cost estimate. Use it to confirm a workflow will do roughly what you expect — and what it might cost — before a real run.

dry_run_workflow(
workflow?, # name of a stored/builtin workflow
workflow_def?, # inline definition object
inputs?, # input values to plan against
cwd?,
)
→ preview of execution order, step count, and estimated cost
ToolPurpose
run_workflow(workflow, inputs, cwd, run_id?, max_cost_usd?)Execute by name or inline definition. max_cost_usd aborts the run if the cost cap is exceeded.
list_workflows(cwd?, tag?)List builtin + user + project-local workflows, optionally filtered by tag.
get_workflow_status(run_id, include_outputs?)Per-step results for a running or completed run.
cancel_workflow(run_id)Cancel a running or paused run at the next step boundary.
resume_workflow(run_id, approved?, cwd?)Resume after a human_approval step.
retry_workflow(run_id, cwd?)Retry from the first failed step; successful steps are preserved.
create_workflow(workflow_def, directory?)Create and save the YAML to ~/.revka/workflows/.
revise_workflow(workflow_kref, operations, rationale?)Apply structured add/edit/delete/reorder/wire operations, producing a new revision.
propose_workflow_yaml(proposed_yaml, intent_summary?, base_yaml?)Architect-only: validate + diff without persisting; powers the visual editor.
get_workflow_metadata(include?)Available step types, pool agents, auth profiles, skills, and channels.
recall_workflow_runs(workflow?, limit?) / get_workflow_run_detail(run_id)Recent runs and per-step detail from Kumiho memory.

Workflows can live on disk or in Kumiho. The operator scans these locations, with later sources overriding earlier ones by name:

PriorityLocationPurpose
Highest.revka/workflows/ (project-local)Per-project overrides.
~/.revka/workflows/ (user)User-global workflows.
Lowestoperator_mcp/workflow/builtins/Shipped defaults (see Built-in workflows & patterns).
FallbackKumiho space Revka/WorkflowsCloud-managed definitions.

When you save from the dashboard or via the API, the definition is stored as a Kumiho item of kind workflow in the Revka/Workflows space. The YAML itself is written as a workflow.yaml revision artifact (not inline metadata, to stay under Kumiho’s size limit). The dashboard also writes the YAML to disk at ~/.revka/workflows/{slug}.r{N}.yaml; the directory scanner picks up the base {slug}.yaml, while the .r{N}.yaml revision files are reached only through kref resolution. Every save writes a new revision and re-tags it published, so each run can be pinned to the exact YAML it executed.