English | 简体中文
Split "planning by chatting with an agent" from "visualizing + executing" into two independent modules:
- skill (
skill/SKILL.md): teaches your own agent (Claude Code / kimi / …) how to break a requirement down into an Orca orchestration task DAG, plus the graph-building conventions. The planning "brain" stays in your agent — no embedded Claude Agent SDK. - viewer (
server/+web/, shipped as theorca-dagnpm package and a standalone binary): connects to Orca's orchestration state and visualizes the DAG live; each node picks its own harness (claude / kimi / opencode / grok …) and optionally a model; click "▶ Run with Orca" and the viewer's built-in self-driven coordinator dispatches ready tasks in parallel along the dependencies to autonomous workers spun up on demand, until the whole graph is done.
Core flow: agent builds the graph → pick a Run and per-node harnesses in the viewer → Run → the DAG executes in dependency-parallel. To change a task or a dependency, have the agent redraw the DAG — Orca has no interface for editing a single task.
⚠️ Requires Orca ≥ 1.4.160. That release (2026-07-29, PR #9925) rewrote the whole orchestration contract; this project targets it and no longer supports older Orca.
⚠️ Why the viewer still acts as its own coordinator: not becauseorca orchestration runis buggy — that command (along withcoordinator-start) has been officially retired (calling it has no side effects; it just says "go read the skill"). Orca deliberately ships no scheduler — the official skill's words: "Agents still choose placement and concurrency; Orca does not schedule workers." So the DAG loop belongs to the viewer, but every step inside that loop now uses Orca's own Run / Task / Dispatch primitives.
your agent (loads the orca-dag skill) orca-dag viewer (npx orca-dag)
┌───────────────────────────────┐ ┌──────────────────────────────┐
│ chat → decompose → build DAG │ │ poll task-list → draw DAG │
│ Bash: orca orchestration │ │ pick harness per node │
│ task-create / gate-* │ │ ▶ Run → self-driven │
└───────────────┬───────────────┘ └───────────────┬──────────────┘
│ writes orchestration state │ poll + worker-start (parallel)
▼ ▼
┌──────────────────── Orca orchestration state ────────────────────┐
│ tasks / deps / gates · on-demand autonomous workers (per-node │
│ harness) │
└─────────────────────────────────────────────────────────────────────┘
- You chat in your own agent. It loads the
orca-dagskill, opens a Run withorca orchestration run-create, then builds the tasks and dependencies into that Run viatask-create --deps …. - Open the viewer (
npx orca-dag). Pick the Run in the top bar; it pollsorca orchestration task-list --run <id> --jsonevery 2 seconds, lays out with dagre, renders with React Flow, and recolors statuses live. - In the viewer, pick a harness per node (or rely on a default fallback), set "Max parallel", and click "▶ Run with Orca".
- The viewer's coordinator loop takes over: it binds one of its own Orca terminals as the Run's coordinator (gaining mutation authority), then on each tick finds every
readytask and callsorca orchestration worker-start --task <id> --agent <harness>in parallel — Orca itself creates the worker terminal, waits for readiness, injects the dispatch, and returns a Dispatch (one attempt). The worker finishes withworker_done --outcome→ Orca automatically marks the task and dispatch completed/failed → dependents flip toready→ repeat until the graph is done, thenworker-stopreclaims the workers. - To change the plan: go back to the agent conversation and have it redraw the DAG.
| Layer | What it is | Owned by |
|---|---|---|
| Run | Namespace + coordinator inbox; only one coordinator is bound at a time (consumer_generation does the fencing) |
Orca |
| Task | A unit of work; deps define the DAG edges, run_id scopes it to a Run |
Orca |
| Dispatch | One attempt (id shaped like ctx_*); carries failure_count (circuit-breaks at 3), heartbeats, pane identity, capability credentials. A retry mints a new Dispatch |
Orca |
| Per-node harness & model, canvas positions, default harness, max parallel, current Run | The viewer's own preferences | .orca-dag.config.json |
A Run is a namespace, not a DAG — several unrelated graphs can live in one Run. "One Run = one DAG" is a convention from skill/SKILL.md, not an Orca constraint.
Every Orca orchestration call goes through resolveRunScope:
- Reads (
task-list/gate-list) skip the consumer check as long as they pass--run <id>— any process can read. That's all the viewer's polling needs. - Mutations (
dispatch/gate-resolve/task-create/worker-start) require the caller to be the Orca terminal currently bound to that Run, proven by resolving--from <handle>to a pane.
The viewer is an ordinary process with no terminal identity, so every mutation would fail with run_required. The fix: the viewer opens its own Orca terminal titled orca-dag coordinator, binds it with run-use, and passes --from on every mutation. Binding fences the previous coordinator (usually the agent terminal that drew your graph), so the viewer asks for explicit confirmation before starting; the agent can reclaim the Run anytime with orca orchestration run-use --id <run>. On stop, the viewer closes that terminal and releases the Run.
- Orca ≥ 1.4.160 (
result.runtime.appVersioninorca status --json). The Run/Dispatch contract landed in 1.4.160; older versions lackrun-create/worker-startand the viewer cannot run. - The orchestration experimental feature is enabled: Settings → Experimental.
- Orca is running:
result.runtime.stateinorca status --jsonshould be"ready"; otherwise runorca openfirst. - The project is an Orca-managed worktree: adding workers / executing requires the current directory to be a registered repo/worktree (else
orca terminal createfails withselector_not_found). Register withorca repo add <path>ororca worktree …. - An agent that can run the skill (graph-building side): Claude Code, or anything that can read
SKILL.mdand run Bash. - The viewer side depends only on the
orcaCLI — noclaude, noANTHROPIC_API_KEY. - Node.js ≥ 20 to run
npx orca-dag— or none at all if you use a release binary. Bun only if you want to build a binary yourself.
One command, both halves:
cd ~/any/orca-managed/project
npx orca-dagThat installs the orca-dag skill into every coding agent on your machine (Claude Code, Codex, Cursor, OpenCode, Gemini CLI, Droid, and the shared ~/.agents/skills directory — whichever of them exist), then starts the viewer on http://localhost:8787 with the current directory as the workspace. It re-runs safely: the skill is only rewritten when it actually changed, and a skill directory you symlinked yourself is left untouched.
Then just chat your requirement to the agent. It builds the DAG into Orca per SKILL.md and tells you to open the viewer.
Needs only Node.js ≥ 20 — the package is a ~500 KB dependency-free bundle, and bunx orca-dag works too. Keep it around with npm i -g orca-dag.
No Node on the machine? Grab a standalone binary from the releases page — same behaviour, bundles its own runtime, needs only the orca CLI on PATH:
tar xzf orca-dag-darwin-arm64.tar.gz && sudo mv orca-dag /usr/local/bin/ && orca-dagSwitches: PORT (default 8787), NO_OPEN=1 (don't open the browser), --no-skill / ORCA_DAG_NO_SKILL=1 (don't touch the agent skill directories), WORKSPACE_DIR (overrides the active worktree).
Want the skill without the viewer, or managed by the standard tooling? npx skills add ZinkLu/Orca-Orchestration --skill orca-dag --global — the open agent skills CLI, the same one orca skills install shells out to.
npx orca-dag uninstall # add --dry-run first if you want to see the listRemoves the skill from every agent directory it was installed into and closes any orca-dag coordinator terminal a crashed viewer left bound to a Run (that one matters — a stale coordinator keeps your own agent fenced out). A skill directory you symlinked yourself is unlinked, never followed, so your checkout is safe.
Two things it won't delete on its own: .orca-dag.config.json (your per-node harness/model choices and canvas layout — pass --purge to drop it) and the program itself, since a running process can't remove its own binary. It prints the right command for that: npm rm -g orca-dag, rm $(which orca-dag), or nothing at all if you only ever ran it through npx.
npm install
npm run dev # frontend :5173 + backend :8787 (vite proxies /api) → http://localhost:5173
npm run build:npm # stage the publishable package → dist-npm/ (Node only)
npm run build:binary # portable single binary → dist/orca-dag (~100 MB, frontend embedded; needs Bun)
npm run release 0.2.0 # tag + push; CI publishes to npm and attaches every binary to a GitHub releaseCross-compile a binary for another platform with TARGET=bun-linux-x64 npm run build:binary; bash scripts/build-all-binaries.sh does every target at once, which is what the release workflow runs.
An end-to-end pass, starting from nothing installed:
-
Get your project under Orca (once per repo) and make sure Orca is up:
cd ~/code/my-project orca repo add . # skip if already Orca-managed orca status --json # runtime.state should be "ready"; otherwise `orca open`
-
Start the viewer from that same directory and leave it running:
npx orca-dag # installs the skill into your agents, serves :8787, opens the browser -
Plan in your agent. In Claude Code (or any agent that just got the skill), describe what you want and ask for a DAG:
Use the orca-dag skill: break "add CSV export to the reports page" into a task DAG.
The agent will ask a few clarifying questions, write
docs/PRD.md/docs/TECH_SPEC.md, then runorca orchestration run-create+task-create --deps …. When it's done it tells you the Run id (likerun_ab12cd34ef56). -
Pick the Run the agent just named in the top-bar dropdown. The DAG appears and refreshes every 2 seconds — you can keep chatting with the agent to reshape it and watch nodes pop in live.
-
Choose harnesses. Set the toolbar's Default harness (fallback for every node), and optionally click individual nodes to override harness/model per node. Set Max parallel.
-
Click "▶ Run with Orca" and accept the confirmation (it explains that the viewer takes over the Run's coordinator slot, fencing your agent's terminal — that's expected). Ready tasks fire in parallel; running nodes get the crayon scribble; the graph advances as workers report
worker_done. -
Resolve gates when they pop. If the plan includes approval gates, approve/reject buttons float over the DAG at the right moment.
-
Change the plan? Go back to the agent conversation. It reclaims the Run with
orca orchestration run-use --id <run>(or just opens a fresh Run and redraws), and the viewer follows along. Then hit Run again.
- Live DAG visualization — node statuses
pending / ready / dispatched / completed / failed / blockedmap to colors; each node wears its harness on its corner. - Switchable layout algorithms: the "Layout" segment in the toolbar toggles layered horizontal / vertical (dagre / Sugiyama) and force-directed (Fruchterman–Reingold); ↻ Re-layout reruns auto-layout (clearing manual drags). The choice persists.
- Drag to arrange: nodes drag freely and hold their positions across live polling refreshes (only untouched nodes follow auto-layout).
- Execution animations:
dispatched(running) nodes get scribbled over and over with diagonal crayon strokes; edges flowing out of a running node start as a swimming dashed draft, then pencil strokes trace them solid toward the downstream node. - Per-node harness: click a node and pick
claude / kimi / opencode / grok / codexor a custom command in its panel (persisted to the workspace's.orca-dag.config.json); nodes without an explicit choice fall back to the toolbar's default harness. - Per-node model override: for harnesses that support it — opencode gets a dropdown enumerated from
opencode models; claude / codex / cursor get free-text (passed viaworker-start --model). Others run on their default model. - ▶ Run with Orca / ⏹ Stop + Max parallel: start/stop the viewer's built-in self-driven coordinator; worker count follows the DAG's parallelism (whatever is ready runs together, capped by "Max parallel"), spun up on demand, reused while idle, reclaimed when done — no manual worker management. While running it shows "N workers".
- Approval gates: after the agent runs
gate-create, approve/reject buttons float over the DAG. - Node details (read-only spec): click a node to see its spec / status / result. To change the spec or deps, have the agent redraw the DAG.
- Hand-drawn crayon style: 🖍️ SVG feTurbulence wobbled strokes on a cream sketchbook canvas.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/dag?run=<id> |
The Run's DAG: { runId, nodes, edges, gates, generatedAt } |
GET |
/api/runs |
List orchestration Runs |
POST |
/api/runs |
{ objective }: create a Run (via a throwaway coordinator terminal) |
GET |
/api/terminals |
List Orca terminals |
POST |
/api/run |
{ runId, harnessByTask?, modelByTask?, defaultHarness?, maxConcurrency? }: start the self-driven coordinator |
POST |
/api/run-stop |
Stop the coordinator and reclaim its workers |
GET |
/api/run-status |
Live coordinator status: { running, busy, attempts, error, … } |
POST |
/api/gates/:id/resolve |
{ resolution, runId }: resolve an approval gate |
POST |
/api/reset |
{ confirmAllRuns: true }: orca orchestration reset --tasks — clears tasks in all Runs |
GET |
/api/models/:harness |
Models selectable for a harness (currently only opencode enumerates) |
GET |
/api/config |
Viewer config (harness/model choices, max parallel, layout, last Run), stored in the workspace's .orca-dag.config.json |
PUT |
/api/config |
Merge-write the viewer config |
GET |
/api/health |
Health check (returns the workspace directory) |
skill/SKILL.md graph-building conventions + spec-writing rules + how execution works + boundaries
server/src/
index.ts Express: dag / runs / run / run-stop / run-status / gates / reset / models / config; serves the SPA
coordinator.ts self-driven coordinator loop: polls the DAG, fires ready tasks in parallel via worker-start
orca.ts orca CLI wrapper: task-list→DAG, worker-start/legacy/opencode workers, gates, terminals, models
config.ts viewer config persistence: .orca-dag.config.json in the workspace (/api/config)
skill.ts installs skill/SKILL.md into the agents on this machine, on startup
uninstall.ts `orca-dag uninstall`: the exact mirror of skill.ts, plus stale-terminal cleanup
webAssets.ts loader for the frontend assets (and the skill) embedded at build time
web/src/
App.tsx full-width DAG shell, 2s polling, hand-drawn SVG filter defs
components/DagView.tsx React Flow graph + status nodes (harness label, crayon animations)
components/ExecControls.tsx default harness + max parallel + Run/Stop + live status
components/NodePanel.tsx node details + per-node harness & model pickers
components/GatePanel.tsx approval-gate overlay
components/RunPicker.tsx Run selector + "New Run"
components/DoodleSelect.tsx hand-drawn select (portal dropdown, search, keyboard nav)
harness.ts reactive config store: per-node harness/model, default, max parallel, layout (persisted via /api/config)
layout.ts layout algorithms: dagre layered (LR/TB) + force-directed (Fruchterman–Reingold)
types.ts / api.ts
scripts/
build-binary.mjs vite build → embed assets + skill → bun --compile → dist/orca-dag
build-npm.mjs vite build → esbuild the server → dist-npm/ (the publishable `orca-dag` package)
build-all-binaries.sh every Bun target + archives + checksums (what the release workflow runs)
check-skill.mjs guards SKILL.md's frontmatter, which the skills CLI installs by
release.mjs `npm run release <version>`: checks, tags, pushes — CI does the rest
- The brain lives outside: planning is done by the agent you already have (the skill provides the conventions); the viewer embeds no Claude Agent SDK.
- The viewer is its own coordinator: Orca deliberately ships no scheduler, so
server/src/coordinator.tsdrives the loop with Orca's Run/Task/Dispatch primitives. Parallelism follows the DAG (everything ready fires together, capped bymaxConcurrency); workers are spun up on demand, reused while idle, reclaimed at the end. - Workers must be autonomous agents: hands-off execution requires the worker to run
orca orchestration send --type worker_doneon its own — otherwise it stalls on a permission prompt.worker-startlaunches Orca-configured TUI agents with their autonomous flags; for custom commands the legacy path usesHARNESS_LAUNCHinorca.ts(onlyclaude --dangerously-skip-permissionsis verified — add and verify flags for others before relying on them). - The
dispatch --injectquirk (legacy path): it types the preamble into the agent's input box but often doesn't submit it (a readiness race). The coordinator waits ~2s after dispatch and sends an extra Enter; a stray Enter on already-submitted input is a harmless no-op. - opencode goes through its own path:
worker-start --agent opencodeopens the TUI but the injected preamble never lands, so the coordinator opens a bare shell, mints a tracking dispatch, and runsopencode run --auto "$(cat <preamble>)"(--autois mandatory — the default permission policy silently auto-rejects tool calls). - Per-node harness/model live in a workspace config file: Orca tasks have no harness/metadata field (
task-createonly takes spec/title/display-name/deps/parent), so the viewer stores harness and model choices, max parallel, and layout in.orca-dag.config.jsonat the workspace root (server/src/config.ts,GET/PUT /api/config) — surviving browser switches and cleared localStorage. The frontend'sharness.tsis a reactive store that hydrates from the server and migrates old localStorage values once. At Run time the choices are passed to the backend asharnessByTask/modelByTask. - Created tasks can't be edited:
orca orchestration task-updateonly changes--status/--result— no interface to edit spec/title/deps, and no single-task delete (resetclears everything, across all Runs). So "change a task" = have the agent redraw the DAG in a fresh Run.
