Fan-Out with Finally
Use finally to run a follow-up step after all parallel work completes.
The Pattern
┌──────────────────────────────────────────────────────────┐
│ ListFiles (with finally) │
│ │
│ ListFiles ──┬──→ Refactor(main.rs) │
│ ├──→ Refactor(lib.rs) │
│ └──→ Refactor(utils.rs) │
│ │
│ ═══════════════════════════════════════════════════════ │
│ After ALL descendants complete: │
│ │
│ finally ──→ Commit ──→ Done │
└──────────────────────────────────────────────────────────┘
Example: Parallel Refactoring with Commit
List files, refactor them all in parallel, then commit the changes.
{
"entrypoint": "ListFiles",
"steps": [
{
"name": "ListFiles",
"value_schema": {
"type": "object",
"required": ["directory"],
"properties": {
"directory": { "type": "string" }
}
},
"action": {
"kind": "Command",
// Find all Rust files and emit one Refactor task per file.
"script": "jq -r '.value.directory' | xargs -I{} find {} -name '*.rs' | jq -R -s 'split(\"\\n\") | map(select(length > 0)) | map({kind: \"Refactor\", value: {file: .}})'"
},
// After all Refactor tasks finish, transition to Commit.
"finally": { "kind": "Command", "script": "echo '[{\"kind\": \"Commit\", \"value\": {\"message\": \"Apply refactors\"}}]'" },
"next": ["Refactor"]
},
{
"name": "Refactor",
"value_schema": {
"type": "object",
"required": ["file"],
"properties": {
"file": { "type": "string" }
}
},
"action": {
"kind": "Pool",
"instructions": { "inline": "Read the file at the path provided. Refactor it to improve readability and remove dead code. Write the changes back to disk. Return `[]`." }
},
"next": []
},
{
"name": "Commit",
"value_schema": {
"type": "object",
"required": ["message"],
"properties": {
"message": { "type": "string" }
}
},
"action": {
"kind": "Command",
// Stage all changes and commit.
"script": "MSG=$(jq -r '.value.message') && git add -A && git commit -m \"$MSG\" && echo '[]'"
},
"next": []
}
]
}
Running
barnum run --config config.json --entrypoint-value '{"directory": "src"}'
How It Works
- ListFiles runs
findto discover.rsfiles and fans out oneRefactortask per file. - Refactor tasks run in parallel (up to
max_concurrency). Each agent reads a file, makes changes, and writes it back. - finally fires after every
Refactordescendant completes. It emits a singleCommittask. - Commit stages and commits all the changes.
Key Points
finallyruns after ALL descendants complete (not just direct children)finallyreceives the original task's value on stdinfinallyoutputs a JSON array of next tasks to spawn follow-up work- The finally hook here is an inline script, no external file needed
- The pattern enables fan-out, then parallel work, then a single follow-up action