pablo formoso FUTURE / DATA & AI
ES EN Streaming –:–:– UTC

The Creature No Longer Waits in Line: dynamic workflows and the jump from chain to graph

Claude Code writes the orchestration script, launches a fleet of subagents, and the coordination doesn’t cost a single token. Third installment: from the loop to a graph that runs itself.

We gave it a loop and it emptied the backlog. We gave it a graph and it stopped forgetting. Now the graph runs itself: Claude Code writes the orchestration script, launches a fleet of subagents, and the coordination doesn’t cost a single token. Third installment in the series, and the one that closes the circle.

This series started with a loop. The creature that emptied your backlog while you slept was, looked at closely, homeostasis with a GPU: gather context, act, verify, repeat. Then came the graph, once it was clear that the agent forgets and the graph doesn’t, and we moved memory out of the context window.

Today’s movement is different from the other two. The graph stops being a drawing and becomes the program. Not a whiteboard diagram describing how work ought to flow, but a script that runs it: nodes that think, edges that carry results, and a runtime that deploys the fleet while your session stays free for something else.

The tool is called dynamic workflows, and it has shipped inside Claude Code since the end of May. But before the syntax, there’s an uncomfortable autopsy to perform: the one on almost every multi-step agent we’ve written so far.

“And then” is not a dependency

Open any multi-step agent of yours and you’ll find a queue. Step one, step two, step three, each politely waiting for the previous one to finish.

Now look closely. Half of those waits are waiting for nothing. Step three never reads step two’s output: it’s standing in line because that’s the order you happened to type it in.

A graph has exactly two pieces, and both fit in a sentence. A node is a unit of work: one bounded job, one input, one output. An edge is a data dependency: what this node produces feeds the next one. That’s it. With that definition in hand, the question you put to every “and then” in your agent is brutally simple: does the next step read what the previous one produced? If it doesn’t, there’s no edge there. There’s dead time.

Your linear script is already a graph, by the way. It’s the worst graph available: a branchless chain where every node has one edge in and one out, where all the context lives in a single head, and where a failure at step C orphans forever whatever A produced.

Which is why the first exercise in graph engineering isn’t adding agents. It’s cutting arrows: walk the chain, find the two or three edges that carry no data, and cut them. The chain collapses sideways into something wider — a handful of independent nodes that can run at once and flow into the single node that genuinely needs them all.

The script that doesn’t think

This is where the tool comes in. A dynamic workflow is a plain JavaScript script that Claude writes for the task you describe, executed by a runtime in the background, isolated from your conversation. The script holds the plan — the loop, the branching, the intermediate results all live in script variables — and only the final answer comes back to Claude’s context.

The analogy that works for me is the spinal cord. When you pull your hand off a hot pan, the cerebral cortex isn’t involved: the reflex arc resolves downstairs, without going through the brain, because going through the brain would be slow and expensive. A workflow behaves the same way: judgment lives in the nodes, reflex lives in the edges. Each node is a subagent thinking through its job in a clean context; each edge is code — a filter, a flat, a dedupe, an if. And code burns no tokens. Coordinating eighteen agents costs zero model thought, because a script isn’t a conversation.

The primitives you’ll use ninety percent of the time fit in one hand:

  • agent(prompt, opts) spawns one subagent with its own context. Attach a JSON schema and the result comes back validated and structured. If you stop it mid-run or it hits an unrecoverable API error, it resolves to null instead of sinking the whole batch.
  • parallel([...]) runs a set of tasks at once and waits for all of them. It’s a barrier.
  • pipeline(items, stage1, stage2, …) streams each item through all of its stages independently, with no barrier between them: item A can be in stage 3 while item B is still in stage 1, and fast items finish early instead of queuing behind the slow one.
  • phase(title) groups what follows under a heading in the progress view, log() prints a message above the phases, and args is the input you hand a saved workflow.

And there’s one detail that captures the entire design philosophy: inside the script, Date.now(), Math.random() and a no-argument new Date() throw. It isn’t a quirk. They’re banned so that relaunching a run repeats exactly the same calls and can reuse the results already completed. The script is deterministic by contract: the creature improvises inside the node, the map never improvises.

In the same spirit, the script doesn’t touch disk and doesn’t run commands, and a script containing import() fails before the run even starts. The agents are the ones that read, write and execute. The script only coordinates them.

Contracts on the nodes, data on the edges

A node you can’t reason about is a node you can’t parallelize. The fix is a contract: explicit input, output with a defined shape, exactly one job. In practice the contract is the schema: force a subagent to return validated JSON and its output becomes something the next node consumes without guessing. That’s the whole difference between a node you can wire into a graph and a node that only works when a human reads it.

The edge has a contract too. It isn’t “B comes after A”: it’s a promise about what crosses. A produces this shape, B was built to consume this shape. Name your edges after their data rather than their order, and two good things happen: you can tell instantly whether the edge is real, and you can swap the node at either end without touching the rest, as long as the shape holds.

Save the agents for judgment. Plumbing is code, and code comes free.

This is one of the quiet savings of thinking in graphs. A surprising share of what we currently burn in tokens is, in truth, an edge: flattening a list, dropping duplicates, filtering nulls, picking a branch with a switch over an already-validated output. A graph where every edge is an agent is a graph paying rent on its own wiring.

The diamond

Put a fan-out and a fan-in together and you get the topology of nearly every serious graph: the diamond. One node splits the job, many nodes work in parallel, one node merges the results. That shape sits behind a security audit, a research report, a five-hundred-file migration and a code review. Swap the sources and the prompts and the skeleton holds.

The canonical form is worth memorizing: fan out in parallel for breadth, reduce with code to compress it, close with one agent that synthesizes. Nine sources researched at once, and Claude’s context never holds the nine at any point: each subagent carries its own, and only the result crosses.

From chain to diamondTwo shapes for exactly the same workChainStep 1Step 2Step 3Step 4A single context holds everythingA failure halfway orphans what came beforeEvery step waits for the one in frontLatency = the sum of all the stepsDiamondverifier on the edgesplits1 agentwork in parallelown context per nodereducecode: filter, flatten, dedupesynthesizes1 agent writes the answerContract on the node: a JSON schemaEdges are code: zero tokensA node that blows up resolves to nullRuntime walls: up to 16 concurrent agents · 4,096 items per parallel() or pipeline() call · 1,000 agents per runSource: Claude Code documentation · code.claude.com/docs/en/workflows
From chain to diamond: the same workload, two topologies.

The fine-grained decision is at the close. A barrier — parallel() — makes everything wait for the slowest node before the next stage begins, and it only earns its keep when the stage genuinely needs the complete set: a cross-source dedupe, an early exit if everything came back empty, a prompt that compares each finding against all the others. For everything else, pipeline(): each item flows at its own pace. “The code looks cleaner” is not a reason. Barrier latency is real, measurable dead time.

Skeptics on the edge

In the loop installment I wrote that the cook doesn’t taste the salt: doer and verifier, kept apart. The graph turns that idea into structure, because the verifier moves onto the edge. Before a finding crosses into the report, a node whose only job is to try to kill it blocks the way. If it survives, it crosses. If it doesn’t, it never reaches your answer.

Three variants worth a place on the belt:

  • Adversarial verification. For each finding, N independent skeptics with an explicit instruction to refute it. It only passes if it survives a majority.
  • Verification through different lenses. Each verifier looks from one angle — correctness, security, reproducibility — because diversity catches failure modes that N identical copies never will.
  • Judge panel. N attempts from different angles, judges scoring in parallel, and a synthesis that starts from the winner while grafting in the best of the runners-up.

This isn’t theory: /deep-research, the workflow that ships with Claude Code, is exactly this in production. It splits your question into angles, searches in parallel, cross-checks the sources, votes on each claim and filters out of the final report whatever didn’t survive. And when a verifier can’t check something — a rate limit, an API error — the claim is listed as unverified rather than counted as refuted, which is the honest distinction.

Failure, meanwhile, stays contained in its node. The subagent that blows up inside a parallel() resolves to null and the other eight come back with their work; a .filter(Boolean) at the exit acts as the dam. Design every fan-in to tolerate gaps instead of assuming the full set.

Cycles that run dry

Some jobs have a size you don’t know until you’re inside them: a bug sweep where finding one uncovers three. That calls for a cycle, a controlled edge back to an earlier node. And a cycle is a loop, so the laws of the loop apply: if you can’t say when it converges, you don’t have a cycle; you have an invoice.

The pattern that does converge is running the well dry: keep launching finders until K consecutive rounds surface nothing new, then stop. And the detail that separates the converging cycle from the runaway one — the one almost everybody gets wrong the first time — is what you dedupe against. Dedupe against everything seen, not just against what was confirmed. If each round compares only with the findings that survived verification, the rejected ones resurface round after round, the well never runs dry, and you’ve built a machine that pays to rediscover the same dead ends forever.

What it costs, and where the walls are

The graph distributes spend in a way a monolithic agent can’t. There are bounded, repetitive nodes — extract this field, classify this ticket — and nodes where the real judgment lives — synthesize the report, adjudicate the finding. Each agent() call can set its own model, so the boring nodes drop to a cheap one and the expensive tokens stay where there’s actual judgment to exercise. By default everything inherits your session’s model, so a big run bills entirely at that tier unless the script says otherwise: worth a glance at /model before releasing the fleet.

It also pays to know where the runtime’s walls are:

LimitValue
Concurrent agentsUp to 16, fewer when fewer CPUs are available
Items per parallel() or pipeline() call4,096; a longer list is rejected with an error
Total agents per run1,000
Large-run warningMore than 25 agents, or over 1.5 million projected tokens

That last one is a warning, not a brake: it neither pauses nor limits anything, it just points you at /workflows, where you can see the tokens each agent has burned and, per phase, how many agents, how many tokens and how much time has elapsed, with keys to pause, resume or stop. Two caveats about the warning: if you’ve set a size guideline yourself, that guideline’s agent count replaces the 25-agent threshold; and with ultracode on it doesn’t appear at all, because turning ultracode on is already saying yes to big runs.

If you’d rather cap things before you start, /config lets you set a size guideline for the graphs Claude designs: small aims for fewer than 5 agents, medium — the default — for fewer than 15, large for fewer than 50, and unrestricted lets Claude size it to the task. It’s advice passed to the model, not a hard cap; the runtime limits still apply underneath.

The good news about stopping is that stopping doesn’t hurt much. On relaunch, completed nodes return their saved result and only what changed or failed runs again — with a piece of fine print worth knowing: from the first agent whose prompt changed onward, everything behind it runs again too, even the parts that finished cleanly.

How to turn it on

Very little ceremony. Three doors in:

  1. Ask for it. Type the keyword ultracode in your prompt — or just say “use a workflow”, in your own words — and Claude writes the script for that task instead of working through it turn by turn.
  2. Run a ready-made one. /deep-research ships built in and never invokes itself: it runs only when you call it.
  3. Let it decide. With /effort ultracode, Claude plans a workflow for every substantial task in the session. More tokens and more time per task: it’s the long gear.

Before anything starts, Claude Code shows you the planned phases and asks for permission, with the option to read the raw script or tweak the prompt. How often it asks depends on your permission mode: in auto mode, only the first time; under bypass permissions, with claude -p or from the SDK, it never asks.

On availability: they’re on every paid plan, with Anthropic API access, and on Amazon Bedrock, Google Cloud’s Agent Platform and Microsoft Foundry. On Pro you have to switch them on by hand from the Dynamic workflows row in /config.

One touch I find elegant: the keyword is an opt-in only if you typed it. A prompt arriving via -p, from a scheduled task or from a pull request comment doesn’t start a workflow just by mentioning ultracode (heads-up if you’re on a Claude Code older than v2.1.210: before that, it did fire from any of those routes). And if you want none of this, it switches off entirely from /config, from settings.json or with an environment variable — for you or for the whole organization.

Saving the map

The lifecycle close is my favourite part. When a run comes out right, you press s in /workflows and the script is saved as a command: in the project’s .claude/workflows/, versioned with the repo and available to anyone who clones it, or in your home directory to carry it everywhere. It takes input via args, relaunches by name, and can travel inside a plugin to distribute it across teams.

So the orchestration that yesterday was a good improvisation is today a named artifact, reviewable in a pull request like any other code. And if you ever edit it by hand, one detail breaks things silently: the export const meta block has to be the first statement in the file and a plain object literal. Put a variable or a function call in there and the command quietly disappears from / autocomplete, with no explanation.

Judgment × topology

All of the above points at the same place: your agent’s ceiling is almost never the model, it’s the shape of the work you handed it. A chain forces one context to hold everything, one failure to halt everything, and every fast step to wait behind the slowest. A graph spreads the context across a fleet, contains failure at the node, and hands you structure to wrap confidence around: contracts on the nodes, skeptics on the edges, routes that resolve the same way every time.

In the first installment I left a formula: your leverage is skill × clarity. The graph adds a factor.

Your leverage is no longer just skill × clarity. It’s judgment × topology: what the nodes think, and the shape of what connects them.

The loop was the creature’s metabolism. The graph is its anatomy. And the final turn of the screw is the same one that closed the first installment: you no longer draw the graph by hand. You describe the objective and Claude writes the script — decompose, fan out, verify, merge — tailored to that run. A graph creating a graph.

The creature no longer waits in line. It spreads the work, contains its failures, verifies its own findings and saves the map for next time. The one thing it still can’t do is decide what’s worth building. That edge, for now, still ends at you.


References

Pablo Formoso
author

Pablo Formoso

Field notes from the intersection of data, AI, and applied philosophy.

posts
64
since
2024

Leave a Reply

Your email address will not be published. Required fields are marked *