Skip to content

Step types reference

Every workflow step type: agent, shell, python, email, notify, resolve, conditional, parallel, goto, output, approvals, a2a, and orchestration patterns.

Every Revka workflow is an ordered list of steps. Each step shares a common envelope — id, depends_on, retry, timeout, and the rest documented in the Workflow YAML reference — plus exactly one type-specific config block named after its type. This page is the reference for those config blocks: what each step type does, its fields, and a copy-pasteable example.

Use this page when you are writing the body of a step and need to know which keys it accepts. For the top-level schema, action shorthand, and the validator, see the YAML reference. For ${...} interpolation and ${{ ... }} expressions used inside these fields, see Variables, expressions & triggers. If you are brand new, start with Your first workflow.

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

CategoryTypes
Agent & codeagent, shell, python
Communicationemail, notify, human_approval, human_input
Control flowconditional, parallel, goto
Output & dataoutput, resolve
Externala2a
Orchestrationmap_reduce, supervisor, group_chat, handoff

Spawns an LLM agent that runs a prompt, optionally uses tools, and returns text. This is the workhorse step type. The agent can be a claude or codex backend, and its output can be captured loosely or held to a strict structured-output contract.

- id: research
type: agent
depends_on: []
agent:
agent_type: claude # claude or codex
role: researcher # coder, researcher, reviewer, tester, etc.
prompt: |
Research ${inputs.topic} and summarize the findings.
output_fields: [summary, score] # optional structured-output contract
model: null # optional model override
timeout: 300 # seconds
template: my-template # optional agent pool template
skills:
- "kref://CognitiveMemory/Skills/some-skill.skilldef"
retry: 1 # retry once on failure
retry_delay: 10 # seconds between retries
FieldTypeDefaultMeaning
agent_typeenumclaudeAgent backend: claude or codex.
rolestringAgent persona (coder, researcher, reviewer, tester, …).
promptstringThe LLM prompt. Supports ${...} interpolation.
output_fieldslist<string>[]When set, declares required structured output fields.
modelstringnullOptional model override.
timeoutnumber300Execution timeout in seconds.
templatestringAgent pool template name to inherit from.

skills, retry, and retry_delay are part of the shared step envelope, not the agent block.

When you set agent.output_fields, the executor appends structured-output instructions to the prompt and requires every declared field to be present in the response. Any missing field fails the step with structured_output_missing. This turns an agent into a typed function you can branch on.

- id: auditor
type: agent
agent:
role: reviewer
prompt: "Review canon consistency and report a verdict."
output_fields: [verdict, production_ready]

The agent may satisfy the contract in any of three accepted formats:

  • Full JSON object{"verdict":"APPROVED","production_ready":true}
  • Fenced JSON block — a ```json code block containing the object
  • FINAL_OUTPUT: block — a trailing YAML block introduced by FINAL_OUTPUT:

Parsed fields are available downstream as ${auditor.output_data.verdict}, or inside an expression as auditor.output_data.verdict:

- id: gate
type: conditional
depends_on: [auditor]
conditional:
branches:
- condition: "${{ auditor.output_data.production_ready == true }}"
goto: publish
- condition: default
goto: fix

Runs a shell command in a subprocess. The exit code controls success: a non-zero exit fails the step unless allow_failure is set.

- id: build
type: shell
shell:
command: "cd ${inputs.project_dir} && npm run build"
timeout: 60
allow_failure: false # true = a non-zero exit doesn't fail the workflow
FieldTypeDefaultMeaning
commandstring— (required)Command to run. Supports ${...} interpolation.
timeoutnumberinheritedTimeout in seconds.
allow_failureboolfalseWhen true, a non-zero exit code does not fail the step.

Executes a Python script file or inline Python code. Arguments are passed in as keyword arguments resolved from workflow state.

- id: transform
type: python
python:
script: "scripts/transform.py" # or use `code:` for inline Python
args:
topic: "${inputs.topic}"
timeout: 60
FieldTypeDefaultMeaning
scriptstringPath to a Python script file.
codestringInline Python source (use instead of script).
argsmap{}Key/value pairs passed as keyword arguments; values support ${...}.
timeoutnumberinheritedTimeout in seconds.

Provide either script or code, not both.

Sends an outbound email over SMTP using the configured email provider. Set dry_run: true to render and log the message without actually sending it — useful while testing.

- id: outreach
type: email
email:
subject: "Revka report"
body: "${report.output}"
dry_run: true
FieldTypeDefaultMeaning
tostringRecipient address.
subjectstringSubject line; supports ${...}.
bodystringMessage body; supports ${...}.
dry_runboolfalseWhen true, the message is rendered and logged but not sent.

See Email, iMessage, Linq & automation for configuring the email provider.

Sends a non-blocking notification to one or more channels and continues immediately — it never pauses the workflow. Use it for progress pings; use human_approval or human_input when you actually need to wait.

- id: heads_up
type: notify
notify:
channels: [dashboard, slack]
title: "Workflow update"
message: "Run ${run_id} completed."
FieldTypeDefaultMeaning
channelslist<string>Channel names to deliver to (e.g. dashboard, slack).
titlestringNotification title.
messagestringNotification body; supports ${...}.

See Channels overview for available channel names.

Looks up a Kumiho entity by kind, tag, and optional filters — with no LLM call. It is the deterministic counterpart to an agent step, used to find prior run state (for multi-run continuity) or to inject a dependency entity.

- id: resolve_cursor
type: resolve
resolve:
kind: "qs-episode-final" # entity kind (exact match)
tag: "published" # revision tag (exact match)
name_pattern: "" # optional glob on entity name
space: "" # space path filter (default: Revka/WorkflowOutputs)
mode: latest # latest = single newest | all = list
metadata_source: revision # revision | item | artifact
fields: [part, episode_number] # metadata fields to extract (empty = all)
fail_if_missing: false # false = don't fail if nothing is found
FieldTypeDefaultMeaning
kindstringEntity kind to match (exact). Required for an entity match.
tagstring"ready"Revision tag to match (exact).
name_patternstring""Optional glob filter on the entity name.
spacestringRevka/WorkflowOutputsOptional space-path prefix filter.
modeenumlatestlatest returns the single newest match; all returns a list.
metadata_sourceenumrevisionWhere to read metadata: revision, item, or artifact.
fieldslist<string>[]Specific metadata fields to extract; empty = all.
fail_if_missingboolfalseWhen true, the step fails if nothing matches.

Output data: found, item_kref, revision_kref, name, metadata_source, plus one entry per field in fields (e.g. ${resolve_cursor.output_data.episode_number}).

Evaluates boolean expressions against step outputs and routes execution to a target step. Branches are checked in order; the first match wins. Use a default branch as the catch-all.

- id: gate
type: conditional
depends_on: [review]
conditional:
branches:
- condition: "${review.output} contains APPROVED"
goto: publish
- condition: "${review.status} == 'failed'"
goto: fix
- condition: default # catch-all
goto: fix
FieldTypeMeaning
brancheslistOrdered list of {condition, goto, value?} items.
branches[].conditionstringExpression to evaluate, or the literal default.
branches[].gotostringStep id to jump to, or "end" to terminate the run.
branches[].valuestringOptional value expression recorded with the matched branch.

Supported operators in a condition: ==, !=, contains, >, <, >=, <=. For richer logic, use a ${{ ... }} expression — see Variables, expressions & triggers.

Run output fields: a completed conditional records matched_branch_index, matched_branch_label, matched_goto, matched_condition, matched_value_expr, and matched_output. The dashboard graph and step inspector display the matched branch and target. Branch goto targets must point to real steps (or end), which the validator checks.

Runs several steps concurrently and joins on a configurable strategy. The named sub-steps are owned by this wrapper — they are excluded from the normal top-level execution order so they don’t also run on their own.

- id: fan_out
type: parallel
parallel:
steps: [step_a, step_b, step_c]
join: all # all | any | majority
max_concurrency: 5 # 1-10
FieldTypeDefaultMeaning
stepslist<string>Step ids to run in parallel (must be ≥2 existing steps that don’t cross-depend).
joinenumallall waits for all and fails if any fails; any returns on the first success; majority needs >50% to succeed.
max_concurrencyint (1–10)5Maximum simultaneous sub-steps.

Jumps back to an earlier step, enabling iterative loops with a hard safety cap. The jump only happens while condition is true and the iteration count is under max_iterations.

- id: retry_loop
type: goto
depends_on: [check_quality]
goto:
target: improve # step id to jump back to
condition: "${check_quality.output} contains NEEDS_WORK"
max_iterations: 3 # safety cap (1-20)
FieldTypeDefaultMeaning
targetstringStep id to jump back to (must exist).
conditionstringExpression that must be true to loop.
max_iterationsint (1–20)Hard cap on loops to prevent runaway execution.

The current loop count is available as ${loop.iteration} inside the loop body.

Renders a template and optionally publishes the result as a Kumiho entity. Publishing tags a revision, which fires a revision.tagged event that can trigger downstream workflows — this is how workflow chaining works.

- id: report
type: output
depends_on: [analyze]
output:
format: markdown # text | json | markdown
template: |
# Analysis Report
${analyze.output}
entity_name: "analysis-${inputs.topic}"
entity_kind: "analysis-report"
entity_tag: "ready"
entity_space: "Revka/WorkflowOutputs"
metadata_target: item # item | revision | artifact
entity_metadata:
topic: "${inputs.topic}"
summary: "${analyze.output}"
FieldTypeDefaultMeaning
formatenumtextRender format: text, json, or markdown.
templatestringRendered content; supports ${...}.
entity_namestringEntity name. Set entity_name + entity_kind (+entity_tag) to publish.
entity_kindstringEntity kind for downstream resolve/trigger matching.
entity_tagstringRevision tag applied on publish.
entity_spacestringRevka/WorkflowOutputsKumiho space path to publish into.
metadata_targetenumitemWhere entity_metadata is written: item, revision, or artifact.
entity_metadatamap{}Key/value metadata; supports ${...}.

Output data: entity_kref, entity_revision_kref.

Pauses the workflow and waits for an operator to approve or reject. A checkpoint is written before pausing, so the run survives a restart. This is the canonical human-in-the-loop gate.

- id: approve
type: human_approval
human_approval:
message: "Deploy to production?"
timeout: 3600 # seconds (1 hour)
FieldTypeMeaning
messagestringPrompt shown to the approver.
timeoutnumberSeconds to wait before the gate times out.

The run pauses with status paused. Resolve it from the dashboard, from a channel, or via the API:

POST /api/workflows/runs/{run_id}/approve
Authorization: Bearer <token>
Content-Type: application/json
{ "approved": true, "feedback": "LGTM" }

Pauses the workflow and waits for a freeform text response from the operator. The reply becomes available downstream as ${ask_user.output}, so you can fold human guidance into later prompts.

- id: ask_user
type: human_input
human_input:
message: "What changes do you want?"
channel: dashboard
timeout: 3600
FieldTypeMeaning
messagestringQuestion shown to the operator.
channelstringChannel to ask on (e.g. dashboard).
timeoutnumberSeconds to wait for a reply.

Calls an external agent over the A2A (agent-to-agent) protocol using HTTP. The remote agent runs the task and returns a result that becomes this step’s output.

- id: external
type: a2a
a2a:
url: "https://agent.example.com/a2a"
skill_id: "analyze-data"
message: "Analyze: ${inputs.data}"
timeout: 300
FieldTypeDefaultMeaning
urlstringA2A endpoint of the external agent.
skill_idstringSkill on the remote agent to invoke.
messagestringTask message; supports ${...}.
timeoutnumberinheritedTimeout in seconds.

Four higher-level step types coordinate multiple agents in one step, instead of you wiring agents and conditionals by hand. They wrap the operator’s multi-agent patterns — see Spawning & coordinating agents and Built-in workflows & patterns for the full mechanics.

TypePurpose
map_reduceFan out over N splits to parallel mapper agents, then a reducer agent synthesizes all results. Ideal for reviewing many files at once.
supervisorDynamic delegation loop — a supervisor agent chooses the next specialist based on results so far, up to a max-iteration cap.
group_chatModerated multi-agent discussion with turn-taking; returns a transcript, summary, and conclusion.
handoffExtract findings and context from one agent and inject them into another agent’s prompt, tracking the chain in Kumiho.
- id: review_all
type: map_reduce
map_reduce:
task: "Review each module for correctness and safety."
splits: ["src/auth", "src/api", "src/db"]
mapper: reviewer # role/template for the parallel mappers
reducer: summarizer # role/template that synthesizes results
concurrency: 3 # parallel mappers (max 10)

The dashboard step inspector renders the group_chat transcript and per-agent results so you can audit how a multi-agent step reached its conclusion.