Loading...
Loading...
Graph engineering for parallel task execution: convert a task, PRD, SPEC, or issue set into a dependency graph (DAG), layer it into supersteps, then implement each independent node concurrently with subagents — each node runs /goal → /review-it → /ship-it in an isolated git worktree, with a fan-in barrier between waves. Triggers on: graph, graph engineering, build a graph, task graph, dependency graph, DAG, parallel implement, 并发实现, 并行实现, 任务图, 把任务变成图, fan-out fan-in, superstep, dynamic workflow.
npx skill4agent add smallnest/goal-workflow graph/goal → /review-it → /ship-it/loop-it/loop-it/graph| Concept | Here |
|---|---|
| Node | One implementable unit of work (an issue / subtask) |
| Edge | A dependency: |
| Superstep / wave | A set of nodes whose deps are all satisfied — run concurrently |
| Fan-out | Dispatch one subagent per node in the current wave |
| Fan-in (barrier) | Wait for all nodes in the wave before starting the next |
| State channel | |
| Live tracker | |
| Dynamic re-plan | After a wave, revise the graph if new work/deps emerged |
Input (task / PRD / SPEC / issues)
│
▼
1. Decompose into nodes ─────────► nodes = {id, title, deps, criteria, scope}
│
▼
2. Build DAG + validate ─────────► detect cycles, orphan deps
│
▼
3. Topological layering ─────────► waves = [[n1,n2,n3], [n4,n5], [n6]]
│
▼
4. Render graph + confirm with user
│
▼ (write .graph_state + graph.html — open graph.html to watch live)
┌──────────── per wave (superstep) ────────────┐
│ │
│ FAN-OUT: 1 subagent per node (parallel) │
│ each subagent, in its own git worktree: │
│ /goal (inline implement) → /review-it │
│ → /ship-it │
│ │
│ FAN-IN barrier: wait for ALL nodes │
│ integrate, update .graph_state │
│ re-render graph.html │
│ re-plan next wave if graph changed │
│ │
└───────────────────────────────────────────────┘
│
▼
All waves done → final summary.md/to-issuesDepends on: #3Dependencies: #3, #5Node #N
title: short imperative title
deps: [list of node ids] or []
criteria: acceptance criteria (checklist) — how the subagent knows it's done
type: backend | frontend | fullstack | ui | infra | docs
scope_hint: which files/dirs this node is expected to touch (for conflict analysis)scope_hintdeps| Check | Action on failure |
|---|---|
Cycle ( | Print |
Dangling dep ( | Print warning, drop the phantom edge. |
| Scope collision (two dep-free nodes edit same files) | Add a soft edge to serialize them (lower id first), OR flag for user. Never let two parallel worktrees fight over the same files. |
router.gomain.gomod.rs__init__deps == []📊 Graph: 6 nodes, 3 waves
Wave 0 (parallel ×3): #1 db schema #2 config loader #3 logging util
Wave 1 (parallel ×2): #4 API handler (deps #1) #5 CLI flags (deps #2)
Wave 2 (parallel ×1): #6 integration (deps #4,#5)
Max parallelism: 3 subagents in Wave 0.```mermaid
graph LR
n1[#1 db schema] --> n4[#4 API handler]
n2[#2 config loader] --> n5[#5 CLI flags]
n3[#3 logging util]
n4 --> n6[#6 integration]
n5 --> n6
```/loop-itgit rev-parse --is-inside-work-tree # in a repo?
git status --porcelain # clean tree? (dirty → stash/abort)
git branch --show-current # on main/master?
git ls-remote --heads origin # remote reachable?
gh auth status # if shipping to GitHub# 1. Write the initial checkpoint (all nodes pending, current_wave 0).
cat > .graph_state <<'JSON'
{ "version": 1, "task": "...", "repo": "owner/repo",
"waves": [[1,2,3],[4,5],[6]], "current_wave": 0,
"nodes": { "1": {"title":"...","deps":[],"status":"pending","wave":0}, ... } }
JSON
# 2. Keep it out of git.
grep -qxF '.graph_state' .gitignore || printf '.graph_state\ngraph.html\n' >> .gitignore
# 3. Render the Claude-style light-theme dashboard.
python3 skills/graph/scripts/render_graph_html.py .graph_state graph.htmlgraph.html# The orchestrator creates a worktree per node BEFORE dispatching:
git worktree add -b feat/node-{N}-{slug} ../.graph-worktrees/node-{N} mainYou are implementing ONE node of a task graph, working in an ISOLATED git worktree.
Worktree: ../.graph-worktrees/node-{N} (already created on branch feat/node-{N}-{slug})
Node #{N}: {title}
Type: {type}
Scope: {scope_hint} — stay within these files; do not touch other nodes' scope
Acceptance criteria (all must pass):
- [ ] {criterion 1}
- [ ] {criterion 2}
Context (deps already merged into main, pull first):
{summaries of dependency nodes' outputs, or the referenced PRD/SPEC excerpt}
Your pipeline (run all three, in order):
1. IMPLEMENT (inline /goal): read the node + any referenced PRD/SPEC, read adjacent
code, implement to satisfy EVERY acceptance criterion, run build + tests + lint
(e.g. go build ./... && go vet ./... && go test ./...). Iterate until all green.
2. REVIEW (/review-it): run code review on your changes, apply accepted findings,
re-run focused tests, repeat until review is clean (max 2 rounds).
3. SHIP (/ship-it): commit (message references the node/issue), push branch,
create PR, merge, close the issue.
Constraints:
- Work ONLY inside your worktree. Do NOT edit files outside {scope_hint}.
- Do NOT try to call `goal` via the Skill tool (it's a UI command, not a skill) —
"implement" means you write the code yourself. /review-it and /ship-it ARE skills.
- If you cannot satisfy a criterion, STOP and report what's blocking — don't fake it.
Return: node id, PASS/FAIL, PR/commit refs, files changed, and — if you discovered new required work or a dependency the graph didn't capture — a `NEW_WORK:` line describing it (title + which nodes it blocks). Emit `NEW_WORK: none` if there's nothing.Why worktrees, not branches alone:mutates the working tree. Two subagents editing the same checkout would corrupt each other. A worktree per node gives each its own filesystem checkout on its own branch — that's what makes the wave genuinely parallel and safe./goal
shippedfailedgit checkout main && git pullgit worktree remove ../.graph-worktrees/node-{N}.graph_statepython3 skills/graph/scripts/render_graph_html.py .graph_state graph.htmlgraph.htmlNEW_WORK:noneblocked.graph_stategraph.html.graph_state.gitignoregraph.htmlgraph.html.graph_state{
"version": 1,
"updated_at": "2026-07-21T10:30:00Z",
"task": "Add user auth",
"repo": "owner/repo",
"waves": [[1, 2, 3], [4, 5], [6]],
"current_wave": 1,
"nodes": {
"1": { "title": "db schema", "deps": [], "status": "shipped", "branch": "feat/node-1-db-schema", "pr": 43, "wave": 0 },
"2": { "title": "config loader", "deps": [], "status": "shipped", "wave": 0 },
"3": { "title": "logging util", "deps": [], "status": "failed", "wave": 0, "error": "test TestLog failed", "attempts": 2 },
"4": { "title": "API handler", "deps": [1], "status": "in_progress", "wave": 1 },
"6": { "title": "integration", "deps": [4, 5], "status": "blocked", "wave": 2, "reason": "depends on #3 (failed)" }
}
}pending | in_progress | shipped | failed | blocked | skippedtitledepsgraph.htmlpython3 skills/graph/scripts/render_graph_html.py .graph_state graph.html.graph_stateshippedfailedgraph.html/goal/ship-it/loop-it| Mistake | Fix |
|---|---|
| Dispatching subagents in separate responses | One response, multiple calls = parallel. Separate = sequential. |
| No worktree → parallel edits corrupt the tree | One |
| Two "independent" nodes edit the same file | Add a soft edge; put them in different waves. |
| Starting the next wave before all nodes merge | Enforce the fan-in barrier. |
| Over-decomposing into 20 trivial nodes | Merge tiny units; a node should be a meaningful shippable unit. |
| Ignoring a failed node's dependents | Mark them |
/prd → /prd-to-spec → /to-issues ─┬─► /loop-it (sequential: one node at a time)
└─► /graph (parallel: whole wave at once)
│
each node: inline /goal → /review-it → /ship-it (in its own worktree)/to-issues/loop-it/graph/review-it/ship-itundefined