From pipelines to flows¶
A linear pipeline is refused with pipeline-format-removed, and there is no
converter: rewriting one takes a few minutes, and the flow it becomes says what
the pipeline only implied. This page rewrites the three pipelines the package
shipped, explore, split and build, each beside the flow that replaced it.
Flows has the whole format.
What moves where¶
The file keeps its shape: YAML frontmatter for the structure, one ## <id>
section per turn for the prose. What changes is what a node is.
Pipeline |
Flow |
|---|---|
|
|
|
|
a step naming a combinator, which spawns agents inside it |
one |
a |
a |
the request and the step before, handed to every step |
|
|
a |
|
an |
|
an |
|
a |
|
the loops, |
|
a |
|
|
|
|
A prompt that asked a model for a word (APPROVED, LGTM) asks for a tool call
now: a node with verdict: approves or raises obligations, and a condition
reads the verdict. Delete the sentence that named the word.
Once the file is in flows/, /flows lists it with its bound, or with its
faults, and /flows <name> prints its plan. Delete the old file: while it
stays, /flows keeps listing it as refused.
explore: a fan-out, then a synthesis¶
---
name: explore
description: Three scouts read the code in parallel, then one agent answers from what they found
steps:
- id: look
fanOut: scout
tasks:
- Find where the thing asked about is implemented, and name the files.
- Find how it is tested, and what the tests actually assert.
- Find what documents it, and whether that matches the code.
concurrency: 3
- id: answer
reduce: synthesiser
---
## look
Read only. Report what you found with file paths and line numbers.
## answer
Answer the question from the three reports below.
The fanOut becomes a map over the same three tasks, whose one node is the
scout’s turn; that node gets the section. The synthesiser reads the request and
the map, which hands on each item’s report. on-fail: continue on the scout
turns a failed branch into a failed report, as the fan-out did, rather than a
failed run.
---
name: explore
description: Three scouts read the code in parallel, then one agent answers from what they found
input: string
nodes:
- id: look
map:
- Find where the thing asked about is implemented, and name the files.
- Find how it is tested, and what the tests actually assert.
- Find what documents it, and whether that matches the code.
concurrency: 3
do:
- id: find
agent: scout
reads: [input, item]
on-fail: continue
- id: answer
agent: synthesiser
reads: [input, look]
---
## find
Do the task under `item` about the question under `input`. Report what you
found with file paths and line numbers.
## answer
Answer the question under `input` from the three reports under `look`.
The prose names the heading each read arrives under: a turn holding three reads has three sections, and “the reports below” points at none of them.
split: a planner, then a worker per task¶
---
name: split
description: A planner splits a read-only question between a scout and a reviewer, then one agent answers
steps:
- id: work
orchestrate: planner
workers: [scout, reviewer]
maxTasks: 4
concurrency: 2
- id: answer
reduce: synthesiser
---
## work
Split the request below. Everything here is read-only.
## answer
Answer the request from the reports below.
orchestrate hid two turns and a parser: the planner wrote a plan, our code
read it, then a worker ran per task. The flow writes both turns. The planner’s
output: is a schema, so its plan arrives through a tool call and is checked
against the schema; the worker’s name is an enum of the two agents, and
among: says which file each value runs. max: 4 is maxTasks: a plan of
more tasks fails the run before any worker starts.
---
name: split
description: A planner splits a read-only question between a scout and a reviewer, then one agent answers
input: string
nodes:
- id: plan
agent: planner
reads: [input]
output: { tasks: [{ worker: scout | reviewer, task: string }] }
- id: work
map-from: plan.output.tasks
max: 4
concurrency: 2
do:
- id: act
agent-from: item.worker
among: [scout, reviewer]
reads: [item.task]
on-fail: continue
- id: answer
agent: synthesiser
reads: [input, work]
---
## plan
Split the request under `input` into one to four tasks, each for a `scout`,
who locates code and reports where it lives, or a `reviewer`, who reads code
and says what is wrong with it. Everything here is read-only.
## act
Do the task under `item.task`.
## answer
Answer the request under `input` from the reports under `work`.
orchestrate handed the planner each worker’s description; a flow hands a turn
its reads: and nothing else, so the planner’s section says what each worker
does.
build: a delivery, written out¶
---
name: build
description: Locate the code, split the work, implement it in pairs, audit the whole
steps:
- id: locate
chain: scout
- id: work
deliver: planner
workers: [coder]
reviewer: reviewer
auditor: auditor
maxTasks: 6
concurrency: 2
maxRounds: 3
maxAuditRounds: 2
---
## locate
Find the code the brief below touches.
## work
Deliver the brief below, using the scout's report as the map of where things
live. The reviewer approves with APPROVED and nothing else.
One deliver step held a planner, a pair per subtask, a check and an audit,
none of it in the file. The flow writes each one as a node, which is what lets
/flows build print its bound (154 turns at most) and a dry run walk it:
planis the planner’s turn, its subtasks typed.deliveris the audit round,maxAuditRounds: 2becomingmax: 2. Itscarry:hands the first round the plan and the second what the audit raised and nobody closed, from the round’s ledger.workis amapover those subtasks,maxTasks: 6becomingmax: 6, each in its own copy of the repository (copies: true).pairis the coder and the reviewer,maxRounds: 3becomingmax: 3. They share a subagent each for the whole pair (memory: pair), and the reviewer decides throughverdict: pairrather than a word.testsis the check, a script of the project in place ofverify:.auditdecides the round throughverdict: deliver.reportis the synthesiser’s few lines on what was done and what is left.
Every agent node is asked once more (retry: 1) when its turn fails: a review
or an audit that ends without its call, a plan off its schema, a provider
error, or an answer cut by the output limit.
---
name: build
description: Locate the code, split the work, implement it in pairs, check and audit the whole
input: string
nodes:
- id: locate
agent: scout
retry: 1
reads: [input]
- id: plan
agent: planner
retry: 1
reads: [input, locate]
output: { subtasks: [{ text: string }] }
- id: deliver
loop: tests.output.passed && audit.output.approved
max: 2
ledger: deliver
carry: { first: plan.output.subtasks, next: deliver.ledger }
give-up: size(deliver.ledger) == 0
do:
- id: work
map-from: deliver.carry
max: 6
concurrency: 2
copies: true
do:
- id: pair
loop: review.output.approved
max: 3
ledger: pair
on-fail: continue
do:
- id: code
agent: coder
memory: pair
retry: 1
reads: [item.text, pair.previous.review, pair.ledger]
- id: review
agent: reviewer
memory: pair
verdict: pair
retry: 1
reads: [item.text, code, diff]
- id: tests
check: .pi/checks/test.sh
timeout: 10m
- id: audit
agent: auditor
verdict: deliver
retry: 1
reads: [input, work, tests, diff, deliver.ledger]
- id: report
agent: synthesiser
retry: 1
reads: [input, diff, deliver.output.last.work, deliver.output.last.audit]
---
The six sections, locate, plan, code, review, audit and report, are in the
shipped flows/build.md. The check is the part
each project writes: .pi/checks/test.sh runs the project’s own tests, and a
build launched where it is missing is refused before its first turn with
check-script-missing.