graph について
graphは、タスクや仕様書、Issue群を依存関係付きの作業グラフに変換し、独立した作業をサブエージェントで並行実装するスキルです。分離されたgit worktree、ウェーブ間のバリア、チェックポイントで実行を管理します。
主な機能
- 01タスクを依存ノードに分解
- 02DAGを検証しスコープ衝突を検出
- 03作業を並行ウェーブに編成
- 04分離されたgit worktreeでサブエージェントを実行
- 05チェックポイント保存とHTMLトラッカー生成
活用シーン
- 01PRDやIssue群の独立タスクを並列化
- 02タスク依存関係に沿って段階的に実装
- 03保存済みウェーブのチェックポイントから再開
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ smallnest/ goal-workflow --skill graphインストール後、Claude Code で/graphを入力して読み込まれたか確認します。
同梱ファイルscripts/render_graph_html.py
詳細
graph — タスク/PRD から並列実行グラフへ
タスク(または PRD/SPEC/イシューセット)を有向非巡回グラフに変換し、作業単位をスーパーステップ(ウェーブ)に分けて、各ウェーブ内の独立したノードをサブエージェントで並行して実装します。各ノードは独自の git worktree内で /goal → /review-it → /ship-it パイプライン全体を実行するため、並列ノード同士が互いの作業ツリーを上書きすることはありません。ウェーブ間では、ファンインバリアによって結果をマージし、次のウェーブを再計画します。
これは /loop-it と並行する仕組みです。/loop-it は厳密に逐次実行されます(1 つの worktree で一度に 1 つのイシュー)。/graph は、ウェーブ内の独立したノードをすべて同時に展開します。
メンタルモデル(LangGraph/グラフエンジニアリングに基づく)
| 概念 | ここでの意味 |
|---|---|
| ノード | 実装可能な作業単位(イシュー/サブタスク) |
| エッジ | 依存関係:B depends on A → エッジ A → B |
| スーパーステップ/ウェーブ | 依存関係がすべて満たされたノードの集合 — 並行して実行 |
| ファンアウト | 現在のウェーブ内のノードごとにサブエージェントを 1 つ割り当てる |
| ファンイン(バリア) | 次のウェーブを始める前に、ウェーブ内のすべてのノードを待つ |
| 状態チャネル | .graph_state — 共有チェックポイント。ウェーブ間で書き換えられます(再開元) |
| ライブトラッカー | graph.html — Claude 風のライトテーマのダッシュボード。各チェックポイントで .graph_state から再描画されます |
| 動的な再計画 | ウェーブの後に、新たな作業や依存関係が明らかになった場合、グラフを修正する |
基本原則: 同じウェーブ内の独立したノードには、共有状態も順序依存関係もありません。そのため、実際に並列実行できます。順序を決めるのは依存関係だけです。それ以外はすべて同時に実行されます。
概要
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
ステップ 1:入力を特定して分解する
自由形式のタスク説明、PRD/SPEC ファイル、既存のイシューセット(GitHub/ローカルの .md)を受け付けます。
- PRD/SPEC →
/to-issuesの分解ルールを再利用します(ユーザーストーリーごとに 1 ノード。大きなものは分割し、小さなものは統合します)。 - 既存のイシュー → 各イシューをノードとします。イシュー本文から依存関係を解析します(
Depends on: #3、Dependencies: #3, #5)。 - 自由形式のタスク → 自分で、独立してリリース可能な最小単位に分解します。
各ノードには必ず以下を含めてください。
Node #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_hint は重要です。依存関係を示すエッジがなくても、2 つのノードのファイル範囲が重複している場合、真に独立しているわけではありません。ステップ 3 を参照してください。
ステップ 2:DAG を構築して検証する
deps からエッジを構築します。その後、次の項目を検証します。
| 確認項目 | 失敗時の対応 |
|---|---|
循環(A → B → A) | ⚠️ 循環依存: #A ↔ #B を出力します。ノード ID の順序で循環を解消し、ユーザーに警告して確認または修正を求めます。 |
宙に浮いた依存関係(#7 depends on #99、該当ノードなし) | 警告を出力し、存在しないエッジを破棄します。 |
| スコープの衝突(依存関係のない 2 つのノードが同じファイルを編集する) | ノード ID の小さい順に直列化するソフトエッジを追加するか、ユーザーに通知します。同じファイルをめぐって 2 つの並列 worktree が競合しないようにしてください。 |
ホットファイルの例外: ほぼすべてのノードが編集する必要のある共通の配線ファイル(例:router.go、main.go、mod.rs、DI コンテナ、__init__ の再エクスポート)は、スコープの衝突とは見なしません。衝突として扱うと、グラフ全体が直列化されてしまいます。このようなファイルでは、追記のみの編集は問題なくマージされるとみなし、次のいずれかを優先します。(a)配線を担当する単一のノードを指定し、ほかのノードには登録フックを公開させる、または(b)最後のウェーブに小さな「すべてを配線する」ノードを追加する。衝突ルールは、同じファイル内の同じロジックを編集するノード(例:2 つのハンドラーが同じ関数を書き換える場合)に限定してください。
ステップ 3:トポロジカルなウェーブ分け
Kahn のアルゴリズムでウェーブを計算します。
- ウェーブ 0 =
deps == []であり、相互にスコープの衝突がないすべてのノード。 - ウェーブ 0 のノードを取り除き、ウェーブ 1 = 依存関係がすべて満たされたノード。
- すべてのノードが配置されるまで繰り返します。
- 同じウェーブ内で、2 つのノードが同じファイルの同じロジックを編集する場合(ステップ 2 のホットファイル例外に基づく実際の衝突)、ID の大きい方を次のウェーブに移します。配線のみのファイルの重複では、この処理は行いません。
ID の規則(常に一貫して適用): ID の小さい方を優先します。循環依存は最小 ID を先に処理して解消し(ステップ 2)、スコープの衝突は ID の小さい方を先に処理し、ID の大きい方を次のウェーブに延期します。
レイヤー化した計画を出力します。
📊 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 図も出力します。
```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
```
サブエージェントをディスパッチする前に、ユーザーの確認を待ちます。ノード、依存関係、または最大並列数の上限を調整できるようにします。
ステップ 4:実行前チェック
最初のウェーブの前に、/loop-it と同じ考え方で次を実行します。
git 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
ハードエラーがあれば、エラーを出力して停止します。ユーザーに最大同時実行数の上限を確認します(デフォルトはサブエージェント 3~4 個の並列実行です。それ以上ではレート制限やレビュー時のノイズのリスクが高まります)。
次に、状態チャネルとライブトラッカーを初期化します(計画の確認直後、最初のファンアウトの前に一度だけ実行します)。
# 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.html
ユーザーに、ブラウザーで graph.html を開くよう伝えます。5 秒ごとに自動更新されるため、ウェーブ、ノードのステータス、進捗バー、ステータス別に色分けされた Mermaid DAG で実行状況をリアルタイムに追跡できます。更新を反映するため、各チェックポイント(ステップ 5b を参照)でレンダリングコマンドを再実行します。
ステップ 5:ウェーブごとに実行(ファンアウト → ファンイン)
各ウェーブを順番に処理します。
5a. ファンアウト — ノードごとにサブエージェントを 1 つ並列実行
ウェーブ内のすべてのノードを1 回の応答でディスパッチします(1 つのメッセージ内で複数の Agent/サブエージェント呼び出しを行うと並行実行になります)。各サブエージェントはそれぞれ独自の git worktreeで作業するため、並行するファイル編集が衝突することはありません。
# The orchestrator creates a worktree per node BEFORE dispatching:
git worktree add -b feat/node-{N}-{slug} ../.graph-worktrees/node-{N} main
各サブエージェントには、自己完結したプロンプトを渡します(オーケストレーターのコンテキストは継承されません)。
You 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.
ブランチだけでなく worktree を使う理由:
/goalは作業ツリーを変更します。同じチェックアウトを 2 つのサブエージェントが編集すると、互いの作業を壊してしまいます。ノードごとに worktree を用意すれば、それぞれが独自のブランチ上で独立したファイルシステムのチェックアウトを持てます。これにより、ウェーブを安全かつ真に並行して実行できます。
5b. ファンイン — バリア、統合、再計画
ウェーブ内のすべてのサブエージェントが戻るまで待ちます(BSP バリア — このウェーブがコミットされるまで、次のウェーブは開始できません)。その後、次の手順を実行します。
- 各サブエージェントの要約を読みます。ノードを
shippedまたはfailedに設定します。 git checkout main && git pull— 次のウェーブでは、依存関係の成果物が main に反映されています。- 完了した worktree を削除します:
git worktree remove ../.graph-worktrees/node-{N}(調査用に、失敗したものは残します)。 .graph_stateにチェックポイントを書き込み、トラッカーを再レンダリングします。python3 skills/graph/scripts/render_graph_html.py .graph_state graph.html(開いているgraph.htmlは次回の自動更新時に変更を取り込みます)。- 動的に再計画します(LangGraph 形式の条件付きエッジ):各サブエージェントの
NEW_WORK:行を確認します。none以外のものがあれば、新しいノードやエッジを追加し、次のウェーブを開始する前に、残りのノードを再レイヤー化します。変更点をユーザーに提示します。 - ウェーブ内に失敗したノードがある場合、それに依存するすべてのノードを
blockedに設定し、スキップします(入力の準備ができていません)。
次のウェーブに進みます。
状態ファイル:.graph_state(+ライブトラッカー graph.html)
.graph_state はリポジトリのルートにあり、.gitignore に必ず追加してください。これは唯一の信頼できる情報源です。クラッシュ後にウェーブの境界から再開できるよう、各ウェーブの後にチェックポイントを保存し、ブラウザのダッシュボードを最新の状態に保つため、ここから graph.html を再生成してください。graph.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 | skipped。各ノードには title と deps が含まれており、graph.html はチェックポイントから DAG とカードを直接描画できます。
トラッカーはいつでも次のコマンドで描画できます。
python3 skills/graph/scripts/render_graph_html.py .graph_state graph.html
再開時は、.graph_state を読み込み、shipped をスキップし、failed の扱い(再試行/スキップ)を確認して、残りのウェーブを再導出し、graph.html を再描画してください。
安全ガード
- ワークツリーの分離は必須です — 同じチェックアウトで
/goalセッションを並行して実行しないでください。 - ファンインバリアは必須です — ウェーブ N のすべてのノードが戻り、マージされる前に、ウェーブ N+1 を開始しないでください。
- スコープの競合は直列化します — 依存関係のないノードでも、同じファイルを編集する場合は別のウェーブに配置してください。
/ship-itの前に/review-itをスキップしないでください。- 並行数に上限を設けてください — デフォルトは 3~4 です。それ以上にすると、レート制限やマージ競合が発生しやすくなります。
- main にフォースプッシュしないでください。 各ノードは、それぞれのブランチ/PR 経由で出荷します。
- 失敗したノードは依存ノードをブロックします — 未達の入力に依存する状態で出荷しないでください。
- ノードごとの最大再試行回数 —
/loop-itのエラー分類を再利用し、無限に繰り返さないでください。 - 最初のファンアウトの前に、計画を確認してください。
よくある間違い
| 間違い | 対処法 |
|---|---|
| 別々の応答でサブエージェントをディスパッチする | 1 回の応答で複数の呼び出しを行うと並列になります。応答を分けると逐次実行になります。 |
| ワークツリーを使わないため、並列編集でツリーが壊れる | ノードごとに git worktree を 1 つ使います。 |
| 「独立した」2 つのノードが同じファイルを編集する | ソフトエッジを追加し、別のウェーブに配置します。 |
| すべてのノードがマージされる前に次のウェーブを開始する | ファンインバリアを徹底します。 |
| 些細なノードを 20 個に分割しすぎる | 小さな単位を統合します。ノードは、出荷可能な意味のある単位にしてください。 |
| 失敗したノードの依存ノードを無視する | それらを blocked としてマークし、スキップします。 |
他のスキルとの関係
/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— このスキルです。DAG に真の並列性(独立したサブシステム)がある場合に使用します。/review-it、/ship-it— 各ノードのサブエージェントが呼び出す実際のスキルです。