Skip to content

Pipeline Reference

This document is the complete reference for Ripline's pipeline YAML/JSON format. It covers all node types, fields, edges, contracts, and template syntax.


Pipeline definition

A pipeline is a directed acyclic graph (DAG) declared in a YAML or JSON file. Each pipeline has metadata, a list of nodes, and a list of edges that connect them.

yaml
id: my_pipeline          # required — unique identifier (filename stem by convention)
version: 1               # optional — semver or integer for tracking schema changes
name: My Pipeline        # optional — human-readable name shown in listings
description: |           # optional — longer description
  Describe what this pipeline does.
entry:                   # required — one or more node IDs where execution starts
  - intake
tags:                    # optional — arbitrary labels for filtering
  - daily
  - agent
metadata: {}             # optional — arbitrary key/value bag (not used by the engine)

nodes:                   # required — list of nodes (at least one)
  - ...

edges:                   # required — list of directed edges connecting nodes
  - ...

contracts:               # optional — top-level input/output JSON Schema contracts
  input:
    type: object
    properties:
      task: { type: string }
    required: [task]
  output:
    type: object

Top-level fields

FieldTypeRequiredDescription
idstringUnique pipeline identifier. Must be non-empty. Used to trigger runs by ID.
versionstring | numberOptional version tag; not enforced by the engine.
namestringHuman-readable display name.
descriptionstringLonger description.
entrystring[]One or more node IDs that start execution. Each must exist in nodes.
nodesNode[]At least one node. Node IDs must be unique within the pipeline.
edgesEdge[]At least one edge. Both from.node and to.node must refer to existing node IDs.
contractsobjectTop-level input/output JSON Schema (applied to run inputs and final outputs).
tagsstring[]Labels used for filtering in the registry.
metadataobjectArbitrary key-value pairs; ignored by the engine.

Edges

Edges declare the data-flow direction between nodes. All edges are explicit — Ripline has no implicit fall-through.

yaml
edges:
  - from: { node: intake }          # required — source node ID
    to:   { node: enrich }          # required — target node ID
    id: e1                           # optional — label for the edge
    when: "inputs.debug === true"    # optional — conditional expression (JS); edge is skipped when falsy

Edge fields

FieldTypeRequiredDescription
from.nodestringSource node ID.
from.portstringReserved for future multi-port support.
to.nodestringTarget node ID.
to.portstringReserved for future multi-port support.
idstringOptional edge label.
whenstringJS expression evaluated with the run context. The edge is followed only when the expression is truthy.

Common node fields

Every node type shares these base fields:

FieldTypeRequiredDescription
idstringUnique node identifier within the pipeline (non-empty). Used in edges and template variables.
namestringOptional human-readable label shown in verbose output.
descriptionstringOptional description of what this node does.
contractsobjectPer-node input/output JSON Schema; validated at runtime.
retryobjectRetry config: { maxAttempts: number, delayMs?: number }.
metadataobjectArbitrary key-value pairs; ignored by the engine.

Node contracts

yaml
nodes:
  - id: do-work
    type: agent
    contracts:
      input:                      # JSON Schema validated against inputs fed to this node
        type: object
        properties:
          task: { type: string }
        required: [task]
      output:                     # JSON Schema validated against this node's output artifact
        type: object
        properties:
          text: { type: string }

Retry

yaml
nodes:
  - id: flaky-step
    type: agent
    retry:
      maxAttempts: 3     # max total attempts (including the first)
      delayMs: 1000      # optional delay between attempts in milliseconds
    prompt: "..."

Node types

input

Loads the run's initial inputs into the execution context. Every pipeline should have at least one input node as an entry point.

yaml
- id: intake
  type: input
  path: task          # optional — dot-path selector to extract a sub-key
  description: "Provide `task` and optional `context` fields"

Fields

FieldTypeRequiredDescription
pathstringDot-path selector. If set, only inputs.path is loaded into the artifact (e.g. path: "task" extracts inputs.task).

Output: The run's input object (or the selected sub-value when path is set) becomes this node's artifact.


transform

Evaluates a JavaScript expression in a sandboxed VM with a 5-second timeout. Use this for data shaping, enrichment, or filtering between nodes.

yaml
- id: enrich
  type: transform
  expression: "({ greeting: `Hello, ${inputs.person}!`, goal: inputs.goal ?? 'explore Ripline' })"
  assigns: greeting_data    # optional — artifact key name (defaults to node id)

Fields

FieldTypeRequiredDescription
expressionstringA JS expression (must be evaluatable; wrap multi-line logic in an IIFE).
assignsstringArtifact key to store the result under (default: the node's id).

Sandbox context

The expression runs with with (context) { return (expression); }, so the following variables are available directly:

VariableDescription
inputsThe run's initial inputs object.
artifactsAll artifacts produced so far, keyed by node ID.
envEnvironment key-value pairs passed at run start.
[nodeId]Shorthand: each prior node's artifact is available by node ID.

Security: require, process, and global are not available in the sandbox. Expressions that exceed 5 seconds are aborted.


agent

Calls an agent runner (LLM, Claude Code, Codex, or OpenClaw) with a prompt and returns a text artifact. This is the primary node type for agent-driven work.

yaml
- id: summarize
  type: agent
  agentId: main                   # optional — agent identifier for OpenClaw integrations
  prompt: |
    Summarize the following content:
    {{ enrich.text }}
  thinking: medium                # optional — thinking level
  timeoutSeconds: 120             # optional — per-node timeout
  resetSession: true              # optional — isolate session context (default: true)
  runner: codex                   # optional — use a built-in code runner for this node
  mode: plan                      # optional — plan | execute (Claude Code or Codex)
  cwd: "{{ run.inputs.repoPath }}" # optional — working directory (Claude Code or Codex)
  dangerouslySkipPermissions: false # optional — bypass permissions (built-in code runners)

Fields

FieldTypeRequiredDescription
promptstringThe prompt sent to the agent. Supports {{ }} template interpolation.
agentIdstringAgent ID passed to the OpenClaw runner when that integration is active. Ignored by LLM, Claude Code, and Codex runners.
thinkingstringThinking level: off, minimal, low, medium, high. Used by integrations that support it.
timeoutSecondsnumberPer-node deadline in seconds. The node is marked errored if exceeded.
resetSessionbooleanWhen true (default), use a fresh session UUID per node for context isolation. When false, share the run-level session ID for multi-turn continuity.
sessionIdstringReserved for future explicit session override.
channelstringReserved for future delivery channel configuration.
deliverbooleanReserved for future delivery configuration.
runner"claude-code" | "codex"Route this node to a built-in code runner. If that runner is not configured, the run fails with a clear error.
mode"plan" | "execute"Built-in code runners only. "plan" = read-only. "execute" = write-capable/default. Exact behavior depends on the runner.
cwdstringBuilt-in code runners only. Working directory; supports {{ }} interpolation. Must resolve to an existing directory. Must not contain ...
dangerouslySkipPermissionsbooleanBuilt-in code runners only. Enables the runner's dangerous bypass mode when the corresponding global gate is enabled. See Agent integration.
skillsstring[]Named skills to attach to this node. Each name is resolved in two ways: (1) as an MCP server from the skills registry (wires in a tool server), and (2) as a text file at <skillsDir>/<name>.md (injects usage instructions into the prompt). A skill may be one or both. See Skills.
mcpServersobjectExplicit MCP server configs keyed by name, merged on top of registry-resolved skills. Node-level entries win over agent-definition entries.

Output: { text: string, tokenUsage?: { input: number, output: number } } stored as the node's artifact.

See Agent integration for full runner selection rules and configuration.

If you run agent nodes in a container, the container is generic user-provided execution context. Ripline does not inject runner binaries or credentials into that container; your image must already contain the requested runner and any required auth/config.


output

Writes an artifact to the run's final outputs. Output nodes are typically the last nodes in a pipeline.

yaml
- id: finalize
  type: output
  source: enrich        # optional — artifact key to write (default: this node's id)
  path: hello.result    # optional — output key in run.outputs (default: source or node id)
  merge: false          # optional — merge artifact into existing outputs instead of replacing

Fields

FieldTypeRequiredDescription
sourcestringID of the node whose artifact to write. Defaults to this node's own ID.
pathstringKey under run.outputs where the artifact is stored. Defaults to source (or node id).
mergebooleanWhen true, deep-merge the artifact into run.outputs instead of replacing.

data

Injects a static literal value as an artifact. Useful for providing constants or default values.

yaml
- id: defaults
  type: data
  value:
    version: "1.0"
    environment: production

Fields

FieldTypeRequiredDescription
valueanyThe literal value to inject as this node's artifact.

loop

Iterates over a collection, running a sub-pipeline (inline or by reference) for each item or for a fixed number of iterations.

yaml
- id: process-items
  type: loop
  collection: "artifacts.breakdown.tasks"  # JS expression resolving to an array
  itemVar: item                             # optional — variable name for the current item (default: "item")
  indexVar: idx                             # optional — variable name for the current index (default: "index")
  maxIterations: 10                         # optional — safety ceiling
  exitCondition: "item.done === true"       # optional — JS expression; stop when truthy
  body:
    pipelineId: handle_task                 # use a separate pipeline by ID
    # — OR — inline body:
    entry: [step1]
    nodes:
      - id: step1
        type: agent
        prompt: "Process item: {{ item.title }}"
    edges:
      - from: { node: step1 }
        to:   { node: step1 }

Fields

FieldTypeRequiredDescription
collectionstringJS expression that resolves to an array. Evaluated against the run context.
itemVarstringName for the loop variable holding the current element (default: "item").
indexVarstringName for the loop variable holding the current index (default: "index").
maxIterationsnumberSafety ceiling on iterations.
exitConditionstringJS expression evaluated each iteration; the loop stops when truthy.
body.pipelineIdstringID of a pipeline to run as the loop body (mutually exclusive with inline body.nodes).
body.entrystring[]Entry node IDs for the inline body.
body.nodesNode[]Inline node definitions for the loop body.
body.edgesEdge[]Inline edges for the loop body.

Either body.pipelineId or body.nodes must be set.


run_pipeline

Invokes another registered pipeline as a child or inline sub-flow.

yaml
- id: delegate
  type: run_pipeline
  pipelineId: handle_task     # required — ID of pipeline to invoke
  inputMapping:               # optional — map parent context keys to child input keys
    task: "artifacts.breakdown.tasks[0]"
  mode: child                 # optional — "child" (separate run, async) or "inline" (same run context)

Fields

FieldTypeRequiredDescription
pipelineIdstringID of the pipeline to invoke. Must be registered in the pipeline directory.
inputMappingobjectMap of { childInputKey: jsExpression } used to build the child run's inputs from the current context.
mode"child" | "inline""child" creates a new run record tracked via childRunIds. "inline" runs the sub-pipeline in the current run's context (no separate run record).

If you need to trigger a follow-up pipeline from a shell or agent node instead of using run_pipeline, prefer Ripline's HTTP API over editing queue files directly. The supported path is POST /pipelines/:id/run, and Ripline includes a small helper for this:

bash
node /home/openclaw/ripline/scripts/enqueue-pipeline-run.mjs \
  --ripline-url http://localhost:4001 \
  --dedupe-input-key idea_id \
  --inputs-json '{"idea_id":"abc123"}' \
  verify_and_promote_build_from_plan_isolated

This keeps queueing logic on the API boundary and avoids hidden runtime dependencies like PyYAML inside build containers.


enqueue

Queues one or more child pipeline runs for asynchronous processing. Used for fan-out patterns where a breakdown node produces a list of tasks.

yaml
- id: dispatch
  type: enqueue
  pipelineId: handle_task     # required — child pipeline to run for each task
  tasksSource: tasks          # optional — artifact key containing the tasks array (default: "tasks")
  mode: per-item              # optional — "per-item" (one run per task) or "batch" (one run with full list)

Fields

FieldTypeRequiredDescription
pipelineIdstringID of the child pipeline to enqueue for each task.
tasksSourcestringArtifact key containing the tasks array (default: "tasks"). The artifact must be an array.
mode"batch" | "per-item""per-item" (default): one child run per task item; each child receives inputs.task = item. "batch": one child run with inputs.tasks = [ ...all items ].

Task item convention: Each task item is expected to be an object with at least id and title. The full shape:

typescript
{
  id: string;
  title: string;
  detail?: string;
  priority?: number | string;
}

collect_children

Loads the current run's child run records (from a prior enqueue node) from the store and writes an artifact with per-child status, outputs, and error. Used immediately after the parent is resumed so downstream nodes (e.g. a verification agent) can see all child results and handle partial failures.

The parent run is resumed by the scheduler when all children are in a terminal state (completed or errored). This node runs in the parent run after resume and aggregates those results.

yaml
- id: collect
  type: collect_children

Convention: Giving this node id: collect is recommended so downstream prompts can reliably reference artifacts.collect.childResults and artifacts.collect.summary without per-pipeline naming.

Artifact shape

The node writes an artifact (under its own id) with:

KeyTypeDescription
childResultsarrayOne entry per child run. Each entry has id, taskId (if per-item enqueue), status ("completed" | "errored"), and when applicable outputs (completed) or error (errored).
summaryobject{ completed: number, errored: number, total: number } for quick sanity checks in prompts.

Failed child runs appear with status: "errored" and error set, so the next node can decide whether to proceed or flag.

When to use

Place collect_children as the first node after the enqueue node in the graph. Connect it to your verification or aggregation node (e.g. an agent that runs build/tests and reasons over artifacts.collect.childResults and artifacts.collect.summary).

Requirements

Requires runId and store in executor context (i.e. the run must be a stored run, as when using the queue/scheduler). If the run has no childRunIds, the node writes an empty childResults array and zero counts in summary instead of failing.


checkpoint

Pauses the run for manual inspection or external approval. The run status becomes paused; execution resumes via ripline run --resume <runId> or the HTTP retry endpoint.

yaml
- id: await-approval
  type: checkpoint
  reason: "Waiting for manager sign-off"   # optional — shown in run record
  resumeKey: manager_approval              # optional — label for external systems

Fields

FieldTypeRequiredDescription
reasonstringHuman-readable reason for the pause, stored in the run record's waitFor.reason.
resumeKeystringArbitrary key stored in waitFor.resumeKey; use this to correlate with external approval flows.

To resume: ripline run --resume <runId> or POST /runs/<runId>/retry.


Template syntax

Ripline uses &#123;&#123; expression &#125;&#125; templates in prompt strings, cwd values, and edge when conditions.

Interpolation

{{ expr }}
  • The expression is evaluated with the run context using with (context) { return (expr); }.
  • undefined and null values produce an empty string.
  • Object values are serialized with JSON.stringify.
  • Errors produce [[error:message]].

Available context variables

VariableDescription
inputsThe run's initial inputs object.
inputs.fieldNameA specific input field.
artifactsAll artifacts produced so far, keyed by node ID.
artifacts.nodeIdArtifact from a specific prior node.
envEnvironment key-value pairs passed at run start.
runThe run record object (run.id, run.inputs, run.pipelineId, etc.).
nodeIdShorthand: each prior node's artifact is also accessible directly by its ID.
nodeId.textShorthand for the text field of a prior agent node's artifact.

Examples

{{ inputs.task }}                         # input field
{{ task }}                                # shorthand for inputs.task (works inside "with" context)
{{ break-down.text }}                     # text from a prior agent node named "break-down"
{{ JSON.stringify(headlines, null, 2) }}  # serialize an array to JSON
{{ run.inputs.repoPath }}                 # explicit access via run object
{{ new Date().toISOString().slice(0,10)}} # JS expression

JSON pointer syntax

Expressions starting with $ are treated as dot-path pointers:

{{ $.inputs.task }}         # same as inputs.task
{{ $.artifacts.step1.text}} # same as artifacts.step1.text

Contracts (JSON Schema)

Contracts are JSON Schema (Draft-07) objects that validate node inputs and outputs at runtime. A validation failure marks the node errored.

Per-node contracts

yaml
- id: do-work
  type: agent
  contracts:
    input:
      type: object
      properties:
        task: { type: string }
      required: [task]
    output:
      type: object
      properties:
        text: { type: string }
      required: [text]
  prompt: "{{ task }}"

Top-level pipeline contracts

yaml
contracts:
  input:
    type: object
    properties:
      task: { type: string }
    required: [task]
  output:
    type: object
    properties:
      result: { type: object }

Run output format

Completed runs are stored in .ripline/runs/<runId>/run.json:

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "pipelineId": "my_pipeline",
  "status": "completed",
  "startedAt": 1700000000000,
  "updatedAt": 1700000005000,
  "inputs": { "task": "build login" },
  "outputs": {
    "result": { "text": "Done.", "tokenUsage": { "input": 100, "output": 50 } }
  },
  "steps": [
    {
      "nodeId": "intake",
      "status": "completed",
      "startedAt": 1700000000100,
      "finishedAt": 1700000000200
    },
    {
      "nodeId": "do-work",
      "status": "completed",
      "startedAt": 1700000000300,
      "finishedAt": 1700000005000
    }
  ],
  "childRunIds": []
}

Status values

StatusDescription
pendingEnqueued, waiting for a worker.
runningCurrently executing.
pausedStopped at a checkpoint node; awaiting resume.
erroredA node failed; the run stopped. Use --resume or the retry endpoint to continue.
completedAll nodes finished successfully.

Full example: spec-then-build pipeline

yaml
id: spec_and_build
name: Spec then build
version: 1
description: Produce a design spec and then implement it.
entry: [intake]

nodes:
  - id: intake
    type: input
    description: "Provide `task` (string)"

  - id: validate
    type: transform
    expression: "({ task: inputs.task?.trim() })"

  - id: spec
    type: agent
    agentId: nova
    prompt: "Write a concise design spec for: {{ validate.task }}"
    timeoutSeconds: 90
    retry:
      maxAttempts: 2
      delayMs: 2000

  - id: implement
    type: agent
    agentId: vector
    runner: claude-code
    mode: execute
    cwd: "{{ run.inputs.repoPath }}"
    prompt: |
      Implement the following spec in the project at {{ run.inputs.repoPath }}.

      Spec:
      {{ spec.text }}
    timeoutSeconds: 180

  - id: approval
    type: checkpoint
    reason: "Review implementation before publishing"

  - id: result
    type: output
    source: implement
    path: implementation.result

edges:
  - from: { node: intake }
    to:   { node: validate }
  - from: { node: validate }
    to:   { node: spec }
  - from: { node: spec }
    to:   { node: implement }
  - from: { node: implement }
    to:   { node: approval }
  - from: { node: approval }
    to:   { node: result }

contracts:
  input:
    type: object
    properties:
      task: { type: string }
      repoPath: { type: string }
    required: [task, repoPath]

Released under the MIT License.