← blog.wrbriggs.cloud
DYNAMIC WORKFLOWS IN CLAUDE CODE · PART 5 OF 6

Conductors: Composing Predefined Workflows with Generated Orchestration

How to build the two-layer system: a library of leaf workflows composed at task time by a conductor script, hand-written or generated on demand.

Will Briggs·August 2026·14 min read

BEFORE YOU START

This post assumes part two (script anatomy), part three (patterns), and part four (contracts, review, shipping). Part six's phase three is the leader-side view of this post.

Here is a complete workflow that contains no agent() calls at all:

export const meta = {
  name: 'release-readiness',
  description: 'Audit, dependency check, and changelog draft before a release',
  phases: [{ title: 'Checks' }, { title: 'Draft' }],
}

const dir = args?.dir ?? 'src/'

const [audit, deps] = await parallel([
  () => workflow('audit-routes', { dir, severityFloor: 'medium' }),
  () => workflow('check-dep-upgrades', { since: args?.since ?? 'last-release' }),
])

if (!audit || audit.findings.some(f => f.severity === 'high')) {
  return { ready: false,
           blockers: audit ? audit.findings : ['audit failed to run'],
           deps }
}

const changelog = await workflow('draft-changelog', {
  confirmed: audit.findings,
  breaking: deps ? deps.breaking : [],
})

return { ready: true, findings: audit.findings, changelog }

Two saved workflows run concurrently. An if gates on the audit's verdict. A third workflow runs only when the release is clean, and the whole thing returns one structured answer. Every piece here is a part-four asset with a name and a contract; the glue is 26 lines, written for this release, disposable after it.

Parts two and three taught you to write one workflow. Part four taught you to ship it. This is what shipping was for: a named workflow is a function, and functions compose. Notice what composition didn't require. Child calls sit inside parallel thunks like any other work, and the gate between phases is an if on a structured field. No new concepts, just a new call.

The workflow() primitive

That new call is workflow(), and it comes in two forms. workflow('audit-routes', args) resolves a saved workflow by name, through the same lookup that serves /audit-routes at the prompt, so part four's resolution order applies unchanged: project beats personal, closest wins in a monorepo, plugin names carry their namespace. workflow({scriptPath: './draft.js'}, args) runs a script file directly, useful when a conductor needs a leaf you haven't saved yet. Either way, the second parameter becomes the child's args global and the return value is whatever the child returned. The contract you designed in part four is the whole interface.

There's an observability bonus. A child's agents appear under their own labeled group in the /workflows view, so a conductor's run reads as a tree: release-readiness at the top, audit-routes and its 90 agents under one branch, check-dep-upgrades under another. Big runs stay legible because the names you chose in part four become the map.

Now the failure model, which pays off a promise from part two. agent() returns null on failure; workflow() throws. It throws on an unknown name, an unreadable script path, or a child that fails to parse. The difference is deliberate. A dead agent is routine attrition inside a fan-out; a missing workflow is a configuration bug, and configuration bugs should be loud. The throw is catchable when a conductor wants to degrade gracefully, and inside a parallel thunk it resolves to null like any other thunk failure, which is exactly what the spine script's !audit check handles.

OBJECTION

“Composition means hierarchies.” Nesting stops at one level. A conductor calls leaves; a leaf that calls workflow() throws. There are no grandchildren, and two sections from now I'll argue that's a feature, not a ceiling.

One pool, one counter, one budget, one abort

Children inherit everything, and inherit it jointly. A child shares the parent run's concurrency cap, its 1,000-agent lifetime counter, its abort signal, and its token budget. Two children running concurrently don't get sixteen slots each; they contend for one pool. Stopping the conductor stops everything under it. A child's tokens draw down the same budget.spent() the conductor reads.

Three design consequences follow, and they're the conductor's actual job.

Scheduling. Running two heavy leaves in parallel halves each one's effective concurrency. Sometimes that's fine, because their agents interleave waits. Sometimes it's a fight, because two 90-agent audits want the same sixteen slots. The conductor decides, and the decision is one line: parallel for contention-tolerant children, a plain sequential await for the hogs.

Allocation. Budget arithmetic belongs in the conductor, because nothing below it can see the whole picture:

if (budget.total && budget.remaining() < 150_000) {
  log('skipping changelog draft: under budget floor')
  return { ready: true, findings: audit.findings, changelog: null }
}

Four lines, and the no-silent-caps rule from part three climbs the stack with you: skip a stage, log the skip.

Blast radius. One abort signal means a conductor is a single unit of work to the person watching, which is legible. It also means a long conductor should pause at child boundaries, never mid-child, for exactly the replay-order reasons part three covered. Nothing new to learn; the checkpoint pattern just moved up a layer.

OBJECTION

“Each child gets its own budget.” Nothing multiplies. One pool, one counter, one ceiling, drawn down by whoever runs. A conductor that launches three children has promised them the same tokens three times unless its own arithmetic says otherwise, and the arithmetic is four lines.

The nesting limit is a feature

One level of composition sounds like a ceiling. In practice it's the constraint that keeps the whole system reviewable, and it forces three good habits.

Flat libraries. Since leaves can't compose leaves, shared logic can't accrete into private call chains. When two workflows need the same sub-step, that step gets promoted into the library as a first-class named workflow, reviewed and discoverable. The library stays the single vocabulary, which is what makes the next section's generation story work.

Legibility. Any run, however large, is one conductor plus its named children in one progress view. A tree of depth two is something a person can watch; a tree of depth five is something a person scrolls past. The limit and the /workflows view are the same design decision seen from two sides.

Review scope. Reviewing a conductor means reading its children's contracts. It never means chasing a transitive closure of grandchildren, because there are none. Part four's two-minute review stays two minutes at every scale the system permits.

The honest cost: some processes genuinely want three tiers, and here they must flatten or split. The workarounds are real and usually better than the tiers would have been. Sequence conductors across turns when a process has genuine human decision points: run one, read its results in conversation, launch the next, with the session as the outer loop. Fatten a leaf when two leaves always run together; merging them is allowed, and the library may grow. Vary by args when two “levels” differ only by a parameter. If none of those fit, a second conductor in a later turn almost always does.

Generated conductors

Now the loop that gives this series its title back. Your agent can write the harness, and by this point it writes the harness over your own library.

The scenario: you state intent in conversation. “Release readiness for the payments service, audit at medium floor, skip the changelog if anything's red.” Claude can see your saved workflows and their whenToUse lines, so it writes a conductor over them rather than over raw agents. The library is the vocabulary the generator speaks, and everything part four made you write (names, contracts, whenToUse) is what makes the generated script short and correct. A good library turns conductor generation into a fill-in-the-glue exercise.

The review step is non-negotiable and cheap. A conductor is short; the spine above is 26 lines. Part four's checklist applies nearly unchanged, with three conductor-specific additions: children referenced by exact name, because a typo throws at runtime rather than save time; budget arithmetic present when children are heavy; and gate conditions that read fields the leaves actually return, which is the contract check. The approval dialog shows all of this before anything runs.

Iteration is where conductors get pleasant. Edit one and relaunch with resume, and completed child calls replay from the journal like any other cached prefix. Fixing the changelog stage costs zero audit agents. Conductors are the cheapest scripts in the system to polish, because their expensive parts cache.

Most conductors are disposable glue: written for this release, discarded after. Keep the ones that become a habit, and keep them by following part four exactly, because a conductor is a workflow. There is no second mechanism to learn.

OBJECTION

“Generated means unreviewed.” Generation changes who types, never who reviews. The raw script is one keystroke away in the approval dialog, and a 26-line read takes two minutes. Skipping that read because a model wrote the script is exactly backward: the model spent seconds writing it, which makes your two minutes the expensive input and the valuable one.

Contracts that compose

Part four told you to design args and returns like an API. Here's the consumer that advice was anticipating.

A leaf composes well when four things are true. Its return is schema-shaped, with fields a conductor can branch on; the spine gates on audit.findings.some(...), which is only possible because part two made findings structured data. It assumes nothing about being top-level; a leaf whose prompts say “present this nicely to the user” breaks quietly under a conductor that wanted fields. Running it twice is safe, or at least cheap, because conductors retry and resume. And its args carry defaults, so a conductor passes only what it means to override.

The failure economics change under composition, and it's the reason to hold the bar. A contract defect in a directly-invoked leaf costs one confused caller who asks you about it. The same defect under a conductor costs a gate that branches wrong without anyone noticing until the release that shouldn't have shipped. Composition raises the stakes on part four's advice without changing a word of it.

For the library owner, this is the moment the job title changes. The acme-tools team from part four now maintains an API surface, and every conductor in the org is a caller. Plugin versioning is what keeps a contract change a changelog entry instead of a Tuesday surprise.

The architecture, named

Zoom out once, because the pieces now have a shape, and the shape has a name.

CONDUCTORS
Per-task glue: deterministic where it counts, generated where it helps, disposable by default.
LEAF WORKFLOWS
Verified orchestration shapes: named, reviewed, shipped in the repo or a plugin, composed through args and returns.
SKILLS & AGENTS
Procedures for a single context: one agent, one question, prompts as contracts.
The session is the outer loop above all three: a person states intent, reads results, and decides what runs next between conductors.

Skills and agents hold procedures one context can follow. Leaf workflows hold orchestration shapes your team verified, named, and shipped. Conductors hold the per-task glue, deterministic where it counts, generated where it helps. Each layer is reviewable on its own terms, and the layer boundaries are the two contracts this series spent three posts on: prompts-as-contracts at the bottom, args-and-returns in the middle.

One honest paragraph about what this stack is not. Resume lives with the session, so this is not a durable execution engine. There's no cross-run state store, no event-driven triggers, no scheduler underneath. A process that needs those belongs in a Temporal-shaped system, and placing each where it belongs flatters both: that world is for processes that outlive any operator; this one is for work a person initiates and watches, at the cadence of a working session, with a model doing the judgment inside every node.

Which closes the loop on part one's title. The harness your agent writes is no longer a one-off script. It's a conductor over a reviewed library, and every layer beneath it is something your team chose, named, and can read.

What to do next

Homework composes what you already have. Take the audit workflow from parts two and three and the second workflow you promoted in part four's exercise, and write a ten-line conductor over them: run both, gate the second stage on a field from the first, return one summary. Watch the tree fill in the progress view. Then break a child's name on purpose and run it again, so you've seen the throw once before it matters.

That's the series. Part one gave the mental model, parts two through five built the practitioner's stack from one script to a composed library, and part six carries the economics and governance for whoever approves all this; part one plus six is the leader track, two through five the builder track. Two deep dives are queued for after the series: escalation patterns for human decisions inside a run's control flow, and resume internals. If the checkpoint paragraphs left you wanting the full mechanics, those are for you.

← Part 4: From One-Off Script to Team Asset: Saving, Versioning, and Shipping Workflows in Plugins
Part 6: The Economics and Governance of 100-Agent Runs →

Written against Claude Code v2.1.239 and the Agent SDK's Workflow tool reference. The changelog tracks what's moved since.