# Graph Workflow

> Breadth-shaped work, in-session, one machine: many independent units of the same job, coordinated as code, not agent chatter. Triggers: 'audit every route', 'across every endpoint', 'one agent per file', 'each file', 'sweep the codebase for X', 'fan them out and verify', 'in parallel across every service', 'all routes', 'for each of the packages'. Instructs the Workflow tool: find real data edges, default pipeline(), parallel() only when a stage needs the whole prior set, one JSON-schema contract per node, flatten/dedupe/filter in plain code (never an agent), tier models via Rule 1, verify findings before reporting, isolate with worktrees only when nodes write concurrently. Pick this over fleet-dispatch-and-watch or orchestration when work stays in-session, one machine, zero extra tokens. fleet-dispatch-and-watch: work leaves this machine (tmux+SSH+ worktrees). orchestration: Orca-app-native coordination (ask/reply, worker_done, task DAGs). Compose, don't compete — a node may dispatch to the fleet.

- Skill: `artsmc-claude-dev-agents/graph-workflow` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add artsmc-claude-dev-agents/graph-workflow`
- Raw SKILL.md: https://api.skillmd.com/api/skills/artsmc-claude-dev-agents/graph-workflow/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: artsmc (https://skillmd.com/u/artsmc-claude-dev-agents)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/artsmc-claude-dev-agents/graph-workflow

---


# Graph Workflow

This skill is the opt-in entry point for Claude Code's `Workflow` tool: dynamic multi-agent graphs
scripted in plain JavaScript. Workflows are **never** auto-selected — they fire only because this
skill (or the breadth cue) told Claude to reach for one. If the task is one file, one job, or a
quick conversational answer, do it directly; see "When not to use this" below.

**Before writing a script, read `docs/graph-orchestration.md` once.** It is the single source of
truth for the schemas, the hard runtime constraints, and the topology-selection guide referenced
throughout this skill — this file applies that mapping, it does not restate it. In particular:

- **Invocation.** Repo-authored workflows resolve only via `scriptPath`, never `name`:
  `Workflow({ scriptPath: '~/.claude/workflows/<file>.js' })`. Named lookup (`Workflow({name: '...'})`)
  does not see user-level workflows — confirmed empirically (`docs/graph-orchestration.md` §6).
- **Runtime constraints** (§2): plain JavaScript only (no TS syntax), no filesystem/Node API access
  inside the script body (schemas must be inlined per script, copy-pasted from §3 — they cannot be
  imported), `Date.now()`/`Math.random()`/argless `new Date()` throw, concurrency capped at
  `min(16, cores-2)`, keep to roughly ≤15 agents per workflow, `meta` must be a pure literal, and a
  thunk or `agent()` call that fails resolves to `null` rather than rejecting the batch — **every
  result array must be `.filter(Boolean)`-ed** before use.

Now the seven-step procedure for shaping the graph itself.

## 1. Find the edges

For every "and then" in the task, ask: does the next step read the previous step's output? If no
data crosses the boundary, there is no edge, and the two steps can run concurrently instead of
waiting on each other.

- "Audit every route for auth bugs, **then** write one report" — the per-route audits don't read
  each other's output, so they have no edges between them; only the final synthesis step has an
  edge from all of them (a fan-in).
- "Read the schema, **then** generate migrations from it" — real edge; the second step needs the
  first step's actual output as input.
- Draw this out before touching topology. Most breadth-shaped tasks turn out to be one fan-out with
  zero internal edges plus a single fan-in at the end — not a long serial chain.

## 2. Pick a topology

Default to `pipeline()`. Reach for a `parallel()` barrier inside a stage only when that stage
genuinely needs the whole prior set together — cross-set dedupe, an early-exit on a total count, or
a prompt that compares one finding against "the other findings." Full selection guide with worked
examples (pipeline, diamond, loop-until-dry, router, plus the judge-panel/adversarial-verify
variants) is `docs/graph-orchestration.md` §4 — use that table to pick, don't re-derive it here.

Quick heuristic: "one agent per file/route/service, then combine" is almost always a **diamond**
(fan-out → reduce in code → synthesize), not a pipeline of pipelines.

## 3. Give every node a contract

Every node gets: a JSON schema for its output, a bounded input passed explicitly in `args` (never
"figure out the context yourself"), and exactly one job. Copy the exact inline schema you need from
`docs/graph-orchestration.md` §3 — `TASK_RESULT` for execution nodes, `FINDING` for discovery nodes,
`VERDICT` for verifier nodes. Do not invent a fourth shape; extend one of these three if a field is
missing, and note the extension in your script's comments.

## 4. Keep edges in code — never spawn an agent to do plumbing

Flatten, dedupe, and filter are `flatMap` and a `Set`. They are free, deterministic, and belong in
the script body between nodes — **not** a job for another agent call.

Wrong:
```js
// DON'T: spawning an agent to merge/dedupe results is plumbing-by-agent
const merged = await agent({ prompt: `Combine and dedupe these findings: ${JSON.stringify(results)}` });
```

Right:
```js
const merged = results
  .filter(Boolean)                                  // drop nulls from failed/skipped agents
  .flatMap(r => r.findings)
  .filter((f, i, arr) => arr.findIndex(g => g.id === f.id) === i); // dedupe by id, plain code
```

If you catch yourself drafting a prompt whose job is "combine/merge/dedupe/filter the following,"
stop and rewrite it as the plain-JS equivalent above. This is a hard constraint, not a style
preference (TR §1, `docs/graph-orchestration.md` §2/§4 principle 4).

## 5. Tier the models

Use the existing Rule 1 worker-routing table verbatim —
`skills/start-phase-execute/references/lean-orchestrator.md` — as each node's `model`/`effort`.
Do not author a second tiering table for graph nodes. Down-route to haiku only when a node's spec is
fully complete, self-contained, and has a runnable gate; default to sonnet/medium for ordinary
implementation or per-item audit nodes; escalate to opus only for cross-cutting-design or
security-sensitive nodes, per that table's own criteria — never blanket-escalate a whole graph
because the task "feels big."

## 6. Verify before reporting, when the output is findings

If a node's output is a claim someone will act on (a bug, a duplication, a security finding), run it
through a refute-by-default verifier fan-out before it reaches the final report: independent
verifier nodes each try to **refute** the finding using the `VERDICT` schema (§3 of the mapping
doc), defaulting to `refuted: true` under uncertainty. A finding survives only on majority
non-refutation. This is the diamond-with-a-verification-stage pattern (`docs/graph-orchestration.md`
§4, "judge panel" / "adversarial verify"). Skip this step only when the node's output is not a
finding someone will act on (e.g. a mechanical transform, a status contract).

## 7. Isolate only when nodes write in parallel

`isolation: 'worktree'` on an `agent()` call costs roughly 200-500ms plus disk per agent — it is a
seatbelt, not a default. Apply it only to nodes that write files or repo state concurrently with
other nodes in the same fan-out. Read-only nodes (auditing, scanning, reviewing) never need it, even
inside a wide fan-out.

## When not to use this skill

Do the work directly instead of authoring a graph when: it's a single-file change with no
independent sub-units to fan out; it's a conversational turn answerable in the current response; or
the coordination overhead (node contracts + topology choice) would take longer than just doing the
two or three things directly. Full rationale: `docs/graph-orchestration.md` §5. `--team` in
`start-phase-execute` remains the default execution path — this skill is additive and opt-in, never
auto-selected.

## Choosing among the three orchestration skills

| Skill | Domain | Reach for it when |
|---|---|---|
| `graph-workflow` (this skill) | In-session; coordination in code, zero extra tokens | Breadth within one session on one machine — "audit every route," "one agent per file," "fan them out and verify" |
| `fleet-dispatch-and-watch` | Cross-machine; tmux + SSH + remote worktrees | Work must leave this machine — heavy compute, battery, hardware limits, background/long-running workers to poll |
| `orchestration` | Orca app coordination | Orca-managed workers, threaded ask/reply, `worker_done`/escalation waits, Orca task DAGs |

These compose rather than compete: a `graph-workflow` node's `agent()` call may itself kick off fleet
dispatch or Orca coordination as part of doing its one job — picking this skill for the in-session
graph doesn't preclude a node reaching into either of the other two.

## Red flags

| Anti-pattern | Fix |
|---|---|
| Spawning an agent to combine/merge/dedupe/filter results | Plain code: `flatMap` + `Set`/`findIndex` between nodes (§4) |
| `parallel()` barrier on every stage "to be safe" | Default `pipeline()`; barrier only when a stage needs the whole prior set (§2) |
| A new per-node model-tiering scheme | Reuse Rule 1 verbatim (§5) |
| Reporting a finding straight from one node | Refute-by-default verifier fan-out first, when output is findings (§6) |
| `isolation: 'worktree'` on every node | Only nodes that write concurrently (§7) |
| `Workflow({ name: '...' })` for a repo-authored script | Always `scriptPath` (see invocation note above) |
| Using this skill for a one-file change | Do it directly — see "When not to use this skill" |

