Examples

Complete, real-world scenarios — from a single-machine review panel to a job fanned across a whole fleet.

The first three are workflow templates that ship with rupu init (dropped into .rupu/workflows/, with their agents in .rupu/agents/) — quoted verbatim, runnable the moment your project is set up. The last two show multi-host operation: running a fleet, and distributing one run across it. Pick the shape that matches your problem and adapt it.

Before you start. Run rupu init in your repo once. It writes the agents and workflows referenced here. List what you have with rupu workflow list, and inspect any one with rupu workflow show <name>.

1 · A specialist review panel

Goal. Put one diff in front of three reviewers at once — security, performance, maintainability — then collapse their structured findings into a single readable summary.

How it works.

  1. The review step has no single agent — it declares a panel. The three panelists (security-reviewer, performance-reviewer, maintainability-reviewer) each run the same subject — the diff you passed in.
  2. Each panelist is a readonly agent with outputFormat: json, so the panel produces a merged, structured result: a max_severity gate and a flat findings list tagged with their source.
  3. max_parallel: 3 lets all three reviewers run concurrently.
  4. The summarize step (the writer agent, no tools) reads steps.review.max_severity and loops over steps.review.findings to write the final report.
name: code-review-panel
description: Run a specialist review panel over one diff or review subject.
inputs:
  diff:
    type: string
    required: true
steps:
  - id: review
    actions: []
    panel:
      panelists:
        - security-reviewer
        - performance-reviewer
        - maintainability-reviewer
      subject: "{{ inputs.diff }}"
      max_parallel: 3

  - id: summarize
    agent: writer
    actions: []
    prompt: |
      Summarize this review panel result.

      Max severity: {{ steps.review.max_severity }}
      Findings count: {{ steps.review.findings | length }}

      {% for f in steps.review.findings %}
      - [{{ f.severity }}] {{ f.title }} ({{ f.source }}): {{ f.body }}
      {% endfor %}

Run it. The one required input is diff:

# pipe in a diff, or pass any review subject as a string
rupu workflow run code-review-panel --input diff="$(git diff main...HEAD)"
diff input security-reviewer json · readonly performance-reviewer json · readonly maintainability json · readonly merge max_severity writer summary
Three panelists review the same diff in parallel; the merged findings and max‑severity feed a single writer summary.

2 · Investigate, then fix

Goal. Hand a bug to a single agent twice — first to find the root cause without touching anything, then to apply the smallest possible fix that follows from what it found.

How it works.

  1. The investigate step runs the fix-bug agent on inputs.prompt and explicitly tells it to stop without making edits and report the root cause as plain text.
  2. The propose step runs the same agent again, but feeds steps.investigate.output straight into the prompt — the second pass is grounded in the first. This is the data-flow pattern: one step's output becomes the next step's input.
  3. Because fix-bug runs in ask mode, the actual edits in step two surface as approvals you confirm.
name: investigate-then-fix
description: Two-step bug fix — investigate, then propose minimal edit.
steps:
  - id: investigate
    agent: fix-bug
    actions: []
    prompt: |
      Investigate the bug described by:
      {{ inputs.prompt }}

      Stop without making edits. Report the root cause as text.
  - id: propose
    agent: fix-bug
    actions: []
    prompt: |
      Based on this investigation:
      {{ steps.investigate.output }}
      Propose and apply the minimal fix.

Run it. This workflow has no inputs block, but its prompt references inputs.prompt — so pass that key:

rupu workflow run investigate-then-fix \
  --input prompt="test_parse_config panics on an empty file"
inputs.prompt bug report investigate fix-bug · no edits .output propose fix-bug · ask · apply
Sequential: the root cause from investigate flows forward as the grounding for propose.

3 · Review every changed file in parallel

Goal. Take one list of files, run the same reviewer over each of them at once, then merge all the per-file notes into one engineering summary.

How it works.

  1. The review_each step uses for_each: "{{ inputs.files }}" to fan one code-reviewer agent out across the list — one independent run per file.
  2. Inside the loop the prompt can reach item (the current file) plus loop.index and loop.length for positional context.
  3. max_parallel: 4 caps how many file reviews run at the same time.
  4. The summarize step iterates steps.review_each.results — the collected outputs of every iteration — and the writer agent folds them into one summary.
name: review-changed-files
description: Fan out one reviewer across a list of files and aggregate the findings.
inputs:
  files:
    type: string
    required: true
steps:
  - id: review_each
    agent: code-reviewer
    actions: []
    for_each: "{{ inputs.files }}"
    max_parallel: 4
    prompt: |
      Review file {{ item }} ({{ loop.index }} of {{ loop.length }}).
      Return either `no issues` or a short bulleted list.

  - id: summarize
    agent: writer
    actions: []
    prompt: |
      Combine these per-file reviews into one concise engineering summary.

      {% for r in steps.review_each.results %}
      ---
      {{ r }}
      {% endfor %}

Run it. The required files input is the list to fan out over:

# review every file that changed against main
rupu workflow run review-changed-files \
  --input files="$(git diff --name-only main...HEAD)"
files for_each code-reviewer · file 1 item · loop.index code-reviewer · file 2 max_parallel: 4 code-reviewer · file 3 code-reviewer · file N readonly summarize writer · .results
One input list fans out to N concurrent reviewers; steps.review_each.results aggregates into a single summary.

4 · Run a fleet — one control plane, many hosts

Goal. Drive rupu running on several machines (a build box, a GPU host, a teammate's server) from a single Control Plane — launch work on a chosen host and watch every run in one browser. Multi-host · Slice 1

How it works.

  1. On each remote machine, run the Control Plane API with a bind address and a shared token. (Put it behind TLS — cp serve speaks plain HTTP.)
  2. On your central machine, register each remote as a host. The built-in local host (this machine) is always present; you're adding peers next to it.
  3. From the central CP, the launcher gets a host selector — pick a host and launch a run, agent, or session there. Run lists gain a host column and host filtering; Fleet → Hosts shows each host's health and active runs. The central CP keeps no remote state — it live-queries each host's API and merges, and degrades gracefully if one is offline.
# 1 · on each remote host — serve the CP API (bind + token), behind TLS
rupu cp serve --bind 0.0.0.0:7878 --token "$RUPU_HOST_TOKEN" --no-open

# 2 · on the central machine — register the remotes
rupu host add gpu-box   --url https://gpu.internal   --token "$RUPU_HOST_TOKEN"
rupu host add build-box --url https://build.internal --token-stdin < token.txt

# 3 · see the fleet (local is always first and can't be removed)
rupu host list

Then operate from the Control Plane. Open rupu cp serve on the central machine, pick a host in the launcher, and launch any of the workflows above on it — for example the parallel review panel on the build box. Cross-host launch is a Control-Plane action (the host selector); see Multi-host for the full picture and the transport roadmap.

Scale a single run across the fleet. Add a distribute: { hosts: [gpu-box, build-box] } block to a for_each step and its units fan out round-robin across those hosts — one workflow, the whole fleet doing the work. See the distributed fan-out example below.

central CP rupu cp serve HTTP host[0] · local this machine gpu-box rupu cp serve build-box rupu cp serve
One central control plane live-queries local + remote hosts over HTTP; launch and observe across the fleet from one browser.

5 · Distributed fan-out across the fleet

Goal. Same idea as #3 — review every changed file in parallel — but spread the units across multiple hosts so a big batch finishes in a fraction of the wall-clock time. One workflow, the whole fleet doing the work. Multi-host · distribute

How it works.

  1. The review_each step adds a distribute: block naming the fleet hosts. rupu assigns the for_each units round-robin across them (file 1 → gpu-box, file 2 → build-box, file 3 → gpu-box, …) and dispatches each to its host.
  2. Each unit’s run is attributed to the host it ran on (visible in the control plane), and max_parallel still caps total concurrency.
  3. Results stream back and aggregate into steps.review_each.results exactly like a local fan-out, so the summarize step is unchanged. A remote unit failure is handled per the step’s continue_on_error.
  4. Drop the distribute: block and the very same workflow runs entirely on the local host — fully backward compatible.
name: fleet-review
description: Review every changed file, fanned across the fleet.
inputs:
  files:
    type: string
    required: true
steps:
  - id: review_each
    agent: code-reviewer
    for_each: "{{ inputs.files }}"
    distribute:
      hosts: [gpu-box, build-box]   # round-robin across these fleet hosts
    max_parallel: 8
    prompt: |
      Review file {{ item }} ({{ loop.index }} of {{ loop.length }}).
      Return either `no issues` or a short bulleted list.

  - id: summarize
    agent: writer
    prompt: |
      Combine these per-file reviews into one engineering summary.

      {% for r in steps.review_each.results %}
      ---
      {{ r }}
      {% endfor %}

Run it. Register the hosts once (see the fleet example above / Multi-host), then launch as normal — the distribute: block does the rest:

# the host names in distribute: must match `rupu host list`
rupu workflow run fleet-review \
  --input files="$(git diff --name-only main...HEAD)"
files for_each · distribute gpu-box code-reviewer units 1 · 3 · 5 … build-box code-reviewer units 2 · 4 · 6 … summarize writer · .results
Round-robin distribution: for_each units split across gpu-box and build-box, then aggregate into one summary — host-attributed end to end.

Make your own

These three shapes — a parallel panel, a sequential hand-off, and a for_each fan-out — cover most real pipelines. To build your own, define the specialists you need as Agents, then wire them together with steps, panels, for_each, and {{ steps.<id>.output }} data flow as described in Workflows. Copy one of the bundled YAML files from .rupu/workflows/ and edit from there.