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.
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.
- The
reviewstep has no single agent — it declares a panel. The three panelists (security-reviewer,performance-reviewer,maintainability-reviewer) each run the samesubject— the diff you passed in. - Each panelist is a
readonlyagent withoutputFormat: json, so the panel produces a merged, structured result: amax_severitygate and a flatfindingslist tagged with theirsource. max_parallel: 3lets all three reviewers run concurrently.- The
summarizestep (thewriteragent, no tools) readssteps.review.max_severityand loops oversteps.review.findingsto 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)"
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.
- The
investigatestep runs thefix-bugagent oninputs.promptand explicitly tells it to stop without making edits and report the root cause as plain text. - The
proposestep runs the same agent again, but feedssteps.investigate.outputstraight into the prompt — the second pass is grounded in the first. This is the data-flow pattern: one step'soutputbecomes the next step's input. - Because
fix-bugruns inaskmode, 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"
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.
- The
review_eachstep usesfor_each: "{{ inputs.files }}"to fan onecode-revieweragent out across the list — one independent run per file. - Inside the loop the prompt can reach
item(the current file) plusloop.indexandloop.lengthfor positional context. max_parallel: 4caps how many file reviews run at the same time.- The
summarizestep iteratessteps.review_each.results— the collected outputs of every iteration — and thewriteragent 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)"
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.
- On each remote machine, run the Control Plane API with a bind address and a shared token. (Put it behind TLS —
cp servespeaks plain HTTP.) - On your central machine, register each remote as a host. The built-in
localhost (this machine) is always present; you're adding peers next to it. - 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.
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.
- The
review_eachstep adds adistribute:block naming the fleet hosts. rupu assigns thefor_eachunits round-robin across them (file 1 →gpu-box, file 2 →build-box, file 3 →gpu-box, …) and dispatches each to its host. - Each unit’s run is attributed to the host it ran on (visible in the control plane), and
max_parallelstill caps total concurrency. - Results stream back and aggregate into
steps.review_each.resultsexactly like a local fan-out, so thesummarizestep is unchanged. A remote unit failure is handled per the step’scontinue_on_error. - 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)"
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.