# Dynamic Workflows

Course: Agentic Coding: Run Coding Agents Like an Operator — Lesson 9 of 10

Type ultracode and the model writes a script that spawns sub-agents: phases in sequence, agents in parallel, parent context window almost empty.

> **The one line:** `ultracode` writes a JavaScript script that spawns sub-agents: phases in order, agents in a phase at once, parent context nearly empty.

## Settings first

Run default Opus at effort `high` and leave it. Levels are `low`, `medium`, `high`, `xhigh`, `max`, set with `/effort`, `--effort` or `CLAUDE_CODE_EFFORT_LEVEL`. The `effortLevel` key stops at `xhigh`; `max` is session-only ([model config](https://code.claude.com/docs/en/model-config)).

My opinion: nothing above `high` is worth paying for. `xhigh` buys more thinking inside one context window, which you get more of, and cheaper, by splitting the work across fresh agents, and `max` is close to a scam. Orchestration beats cranking effort. When usage is tight, drop to `medium` before you cut the workflow.

## What `ultracode` does

Type `ultracode` in the prompt and the turn becomes a dynamic workflow. `/effort ultracode` or `claude --effort ultracode` sets it for the session: `xhigh` plus orchestration. Needs a paid plan and Claude Code v2.1.154+; on Pro, enable it in the Dynamic workflows row of `/config`.

The model **writes a JavaScript script** and the harness runs it, so the steps are fixed code, not a fresh model decision: `agent()` spawns one sub-agent, `parallel()` a batch at once, `pipeline()` one agent per list item, top-level `await` works, and the script cannot reach the filesystem or the shell. Caps: 16 agents at once, 1,000 per run, a "Large workflow" warning past 25 agents or 1.5M projected tokens ([dynamic workflows](https://code.claude.com/docs/en/workflows)). The `medium` size guideline aims under 15 agents; `/config workflowSizeGuideline=large` allows under 50.

Ask for the script first: *"save the workflow code to the repo first so I can review it."* Workflows live in `.claude/workflows/` or `~/.claude/workflows/`, so a good one is reusable.

## Two axes

Phases run strictly in sequence. Agents inside a phase run in parallel.

```mermaid
graph LR
    P["Parent session near-empty context"] --> F1["Phase 1 inventory"]
    F1 --> A1["agent"]
    F1 --> A2["agent"]
    F1 --> A3["agent"]
    A1 --> F2["Phase 2 critique"]
    A2 --> F2
    A3 --> F2
    F2 --> B1["agent"]
    F2 --> B2["agent"]
    B1 --> R["Phase 3 report"]
    B2 --> R
```

The cap of 16 at once is per phase, not per run. Each agent starts with a clean context window and returns a short result. The parent reads only the summaries, so a job that would blow a single window ends with the main session mostly empty.

## Read the script it writes

A `meta` literal names the phases; plain JavaScript does the rest.

```js
export const meta = {
  name: "audit-agentic-surfaces",
  description: "Inventory every agentic surface, then critique the findings",
  phases: [
    { title: "Inventory", detail: "one agent per surface" },
    { title: "Critique", detail: "three lenses over the findings" },
  ],
};

const SURFACES = ["rules", "skills", "hooks", "mcp", "workflows"];

const FINDINGS = {
  type: "object",
  required: ["surface", "files", "findings"],
  properties: {
    surface: { type: "string" },
    files: { type: "array", items: { type: "string" } },
    findings: { type: "array", items: { type: "string" } },
  },
};

phase("Inventory");
// Fan-out: one agent per surface, all at once, each with a clean window.
const inventory = (
  await parallel(
    SURFACES.map((surface) => () =>
      agent(`Inventory the ${surface} config in this repo.`, {
        label: `inventory:${surface}`,
        model: "sonnet",
        schema: FINDINGS,
      })
    )
  )
).filter(Boolean);

phase("Critique");
// The next phase reads the previous phase's results as a JavaScript value.
const critiques = await parallel(
  ["skeptic", "cost", "security"].map((lens) => () =>
    agent(`Critique this inventory as a ${lens}: ${JSON.stringify(inventory)}`, {
      label: `critique:${lens}`,
    })
  )
);

return { inventory, critiques };
```

`parallel()` takes **thunks**, not promises, so the runner controls when each agent starts. `schema` returns a validated object instead of prose: give one to every agent feeding a later phase.

## The dials

Mechanical stages on cheap models: inventory, listing and rewrites on Sonnet, dumb passes on Haiku. Opus for planning, critique and the report.

Widen research phases: three or five agents on different parts of the problem cost one turn and give the planner real material.

Add a QA phase. My global rules send QA through [`agent-browser`](/directory/vercel-agent-browser), a CLI that drives a real browser and returns screenshots, so the check is real.

Wire a repair loop: a failed review or QA sends work back to implementation, then reviews and QAs again before PR.

Add a suggestion phase, filtered: most are noise. Ask two or three times, take the one naming an edge case you missed.

## Use one even for small tasks

My default now, even for a one-file change: *dynamic workflow, no parallel agents, one per phase, plan then implement then review, QA inside the implementation.* It costs almost nothing and buys a free QA pass.

## Portability

Nothing else ships this, so I ported it. [`pi-ultracode`](https://github.com/Mark-Life/pi-ultracode), my MIT-licensed extension for the [Pi coding agent](https://www.npmjs.com/package/@earendil-works/pi-coding-agent), runs the same orchestration scripts outside the chat context, same globals `agent`, `parallel`, `phase`, `log`. Pi runs any model, so the pattern survives on another harness from [Choose Your Coding Agent](/courses/agentic-coding/choose-your-coding-agent).

```bash
pi install github:Mark-Life/pi-ultracode
```

In Codex, tell it to manage several sessions with the main one coordinating the rest: worse than dynamic workflows, slightly better than plain sub-agents.

The extreme version is Bun's [rewrite from Zig to Rust](https://bun.com/blog/bun-in-rust): 535,496 lines of Zig, 11 days, 64 agents at peak across four worktrees, roughly $165,000 at API pricing.

## What to do

- Default Opus at effort `high`, `medium` only when usage is tight.
- Type `ultracode`, ask it to save the script first.
- Name the phases: plan, implement, review, QA, report.
- Give a `schema` to any agent feeding a later phase.
- Mechanical phases on Sonnet or Haiku, Opus for planning and critique.
- Install `agent-browser` (`npm i -g agent-browser && agent-browser install`), require screenshots before PR.

## Links

- Lesson page: https://andrey-markin.com/courses/agentic-coding/dynamic-workflows
- Course: https://andrey-markin.com/courses/agentic-coding.md
- Next lesson: https://andrey-markin.com/courses/agentic-coding/workflows-in-action.md
