Dynamic Workflow Skill: Plan-in-Code Fan-Outs for Hermes Agent
Plan-in-code fan-outs, adversarial verification, waves.
Written by Neura Market from the official Hermes Agent documentation for Dynamic Workflow. Commands, paths, and version numbers are reproduced from the source unchanged.
Read the official documentationThe Dynamic Workflow skill turns large fan-out jobs into a scripted workflow: the plan, the loop, and every intermediate result live in a script and on disk, so the parent agent's context holds only verified results. It is for work where the unit is clear (a file, an endpoint, a record) and there are more units than one context can hold. If you are running a handful of tasks or a serial chain, you do not need it.
What it does
The skill splits work across two layers separated by a real capability boundary. Layer A is an execute_code script that handles deterministic steps: fetching N URLs, parsing N files, running N commands, templating outputs, building manifests, merging results. Layer B is a delegate_task batch where each task gets its own isolated agent for LLM-judgment work: classify, review, decide, write, refute, or refactor one unit.
The parent does the deterministic part first, fans out only the irreducibly-LLM step, and synthesizes the results. Because the loop and intermediate variables live in the script, the parent's context stays small. The skill covers three shapes: one-shot fan-outs, adversarial convergence with attempts and refuters, and multi-wave campaigns that integrate dozens of worker branches.
It does not make delegate_task durable across restarts. That is the kanban swarm's job.
Before you start
delegate_taskmust be available, anddelegation.max_concurrent_childrenmust be sized for the wave. The default is 10, and the runtime rejects atasks=[]larger than that with a clear error rather than queueing. Setdelegation.max_spawn_depth >= 2only if children must fan out themselves.- You need a writable run directory resolved from the terminal environment's temp dir (
$TMPDIR, else the platform temp dir). Never use a literal/tmp: Termux has no such directory and native Windows breaks on it. Use/wf__/, unique per run, so an interrupted earlier run cannot leave stale outputs to be misread. execute_codeis required for the deterministic layer. Onlyweb_search,web_extract,read_file,write_file,search_files,terminal, andpatchexist inside it.
The skill is optional and installed on demand. It ships at optional-skills/autonomous-ai-agents/dynamic-workflow, version 2.0.0, by Teknium + Hermes Agent, under the MIT license. It runs on linux, macos, and windows.
How to run
The two layers differ in what they hold, which tools they can reach, how concurrency is bounded, and what they cost.
Layer A - execute_code script | Layer B - delegate_task batch | |
|---|---|---|
| Use for | DETERMINISTIC work: fetch N URLs, parse N files, run N commands, template N outputs, build manifests, merge outputs | LLM-JUDGMENT work: classify, review, decide, write, refute, refactor one unit |
| Holds | loop, branching, intermediate variables | nothing; one call with tasks=[...], each task its own isolated agent |
| Tools | the sandbox set above; it can NOT call delegate_task | the parent's toolsets, inherited unchanged (no per-task narrowing); children lose delegate_task, clarify, memory, send_message, cronjob_manage |
| Concurrency | yours (ThreadPoolExecutor, batches) | bounded by delegation.max_concurrent_children |
| Cost | tool calls only | one full agent tree per task; multiplies linearly |
Do the deterministic part in Layer A first, fan out only the irreducibly-LLM step in Layer B, synthesize on the parent.
Background-first: results re-enter as messages
A top-level delegate_task returns immediately with one handle per task. Each child's result re-enters the conversation as a new message when it finishes. You cannot read out_*.csv on the line after the call. Finish whatever does not depend on the children, give a one-line status, and end your turn. Act on each result message as it lands. An ordinary follow-up user message does not cancel children; /stop, /new, and process exit do. Only a delegation issued by an orchestrator subagent (depth > 0) is synchronous.
Quick reference
- A unit must be answerable without sibling output, otherwise it is serial.
- The manifest has one unit per line in
/manifest.jsonl; print the count and run dir. - Size each child for roughly 8-12 mechanical edits, or about 2-3k lines of reading, or about 50-70 KB of corpus. Size by the largest unit. Structured output goes to files, never the
summaryfield, which truncates under load. Prefer delimiter-separated lines over JSON. - The parent verifies file count and per-run freshness before merging.
- A stalled child usually completed its write. Check the filesystem first.
- Run a scoped slice first (one directory, 20 records), report token cost, then scale.
Procedure
One-shot fan-out
- Decompose into independent units.
- Layer A pre-pass writes the manifest.
- Size chunks against the limits above. For more tasks than
max_concurrent_children, issue bounded waves yourself. - Layer B: one
delegate_task(tasks=[...]). Each task reads its slice, writes/out_.csv, prints a status word, and stops. - End the turn. As result messages arrive, read the files, verify, and merge. The cross-cutting synthesis stays on the parent.
Adversarial convergence (finding-quality work)
- Independent attempts: the same question to N children (2-4) with different framings in each
context. Each writes one claim per line to/attempt_.md. Claims must be located and individually falsifiable ("POST /api/users/:id/roleinsrc/routes/users.ts:142has no role check"). A refuter cannot break "the auth layer has problems". - Merge and dedupe on the parent, noting the agreement count per claim.
- Refuters: a second batch told to break each claim with counter-evidence, emitting
claim_idx|survives|counter_evidence. Give them the sources, not the attempts' reasoning. - Surface only survivors. Drop refuted claims with a one-line reason.
- Feed new claims from round 2 through one more refutation. Stop when a round adds no survivors, and cap at 3 rounds.
The same mechanic protects the parent from its own wrong premises. When you hand children a heuristic ("every patch target on a facade is a dead seam"), tell them to refute it with evidence before acting on it. Four squads doing so turned a 647-site blanket rewrite into 59 real fixes and saved 130+ green tests.
Campaign shape (dozens of workers, several waves, hours)
The one-shot recipe does not scale to a whole-codebase pass. What did:
- Measure first (LOC, hotspots, dead symbols, oracle corpora) and write one shared
BRIEF.mdplus a per-clustertask_.md. Every child reads both. When the fleet drifts (children shaving docstrings instead of cutting code), patch the brief once and steer; re-dispatched children inherit the fix. - Exclusive ownership: one cluster of files per worker, edits outside it are discarded at integration. Sub-fan-outs inside one file own line ranges and define helpers inside their range so diffs merge cleanly.
- Commit per verified step, locally, no push, no PR, no rebase from children. Committed state is the only handoff; every worker that died mid-campaign lost exactly its uncommitted tail. A worker sharing a worktree index commits with
git commit --only; a bare commit swept a sibling's staged hunks. - Fleet size 12-16 concurrent. Above about 40 processes on one OAuth grant the hourly token refresh stampedes into 401s and kills the wave. Queue the rest.
- Parent liveness: a child reporting
completedwith a few dozen log lines, or with 0 commits on its branch, has not finished. Look for its sub-branches or re-dispatch it with the predecessor's worktree and diff. - Integration per round: freeze a base SHA, rebase clean branches mechanically, give each conflicting branch its own rebase worker ("main's behaviour wins, re-applied inside the new structure"), merge onto one integration branch, and run the full suite on the combined tree. Collisions that every branch passed alone appear only here. The next round branches from the integrated commit so its workers cannot conflict with each other.
- Before declaring a round integrated:
git rev-list --count ..is 0 for every branch. Workers keep committing after you merge their tip; 168 commits across six slices were once left behind that way. - Test runs: exactly one runner on the box, behind a lock file, at high
-j. Many parallel low--jrunners were slower and killed each other's process groups. Red files are re-run on a bareorigin/mainworktree in the same venv; identical per-file failure sets are pre-existing, not yours. - Forward-port at the end, not per round: freeze main's SHA, fan out the conflicted files by directory to workers editing one merge worktree with no commits, then the parent commits the merge once. CI never runs on a conflicted PR, so re-merge main before every push.
- Live QA is its own wave: one squad per surface, isolated
HERMES_HOME, expectation written before the check, evidence on disk, report only, and a PR-vs-main difference is the only thing that counts as a regression. Green unit tests missed the one P0 (a logged-in code path no test exercised). - Reviewer claims get the same treatment as child claims: A/B against the base before "restoring" anything. Several confidently stated review deltas already behaved that way on base.
- A parent restart needs a
HANDOFF.md: why it died, which handles are dead, per-branch scorecard (LOC delta, import smoke, targeted tests), and the exact re-dispatch text. Snapshot every dirty worktree into awip:commit first.
When not to use it
Skip the skill for under about 10 units or for serial chains. If you are running a refactor or fix campaign on hermes-agent itself, load hermes-agent (the dev workflow) alongside; this skill owns the fan-out shape. If you need durable work that survives restarts, use the kanban swarm instead. For a one-off scheduled job, use cronjob.
Limits and gotchas
- Calling
delegate_taskinside anexecute_codescript does not work: it is not in the sandbox. - Synthesizing on the same turn as the fan-out call fails because the files do not exist yet.
- Promising background-durable-for-days from
delegate_taskis wrong: it is turn-scoped and dies with the process. Durable graph = kanban swarm; one-off =cronjob. - Do not trust
summaryfor content, orstatus=completedfor completion. - Same framing in every "independent" attempt makes them collapse to one answer.
git stashanywhere in a worktree campaign is dangerous:refs/stashis shared across worktrees and another worker will pop your edits. Compare via a temp worktree.- Reporting a hit target when the honest number is lower is a failure. Say "16% so far, here is the path to 30%" and run the next round.
Verification
- Manifest line count matches the expected unit count.
- Every
out_*.csvexists and was written this run. - Every dropped claim has recorded counter-evidence; every surfaced claim went through refutation.
- Campaign: every branch at 0 unmerged commits, full suite on the integrated tree with reds triaged against bare main, live QA report per surface, token cost reported on the scoped slice before the full run.
What pairs with it
The source lists hermes-agent and simplify-code as related skills. Load hermes-agent when the campaign targets the agent's own codebase, and reach for simplify-code when the fan-out work is a cleanup pass.