「Issue、デカすぎてコンテキスト溢れた...」
Claude Codeでgit worktreeを使った並列開発。「ブランチ分けて同時に実装すれば速い」という話はよく聞きます。実際、小さなIssueなら1つのworktreeで十分。でも、10ファイル以上に変更が及ぶ大きなIssueに出くわしたとき、「どう分割するか」「どう合流させるか」が急に難しくなる。
タスクの分解は手作業。worktreeの準備も手作業。マージしたらコンフリクト。型が通らない。テストが落ちる。並列にした分だけ統合コストが増えて、結局トータルで遅くなる...そんな経験、ありませんか?
私たちも何度もこの壁にぶつかりました。worktree並列開発の速度を活かすには、「分解」と「統合」の両方を設計しないといけない。そこで作ったのが、dev-decompose(自動タスク分解)とdev-integrate(自動統合パイプライン)です。
この記事で学べること
- 手動タスク分解の3つの限界と、自動化が必要な理由
- dev-decomposeのファイル境界分析による自動タスク分解の仕組み
- contract branchパターンで並列ワーカーの整合性を担保する設計
- dev-integrateの自動マージ・型チェック・統合テストパイプライン
- flow.jsonによる並列パイプラインの進捗追跡
- Agent Teamsとworktree並列開発の使い分け
前提知識
- Claude Codeの基本操作を理解している
- git worktreeの概念を知っている
- Skillsの基本概念と状態管理パターンを把握している
手動タスク分解の3つの限界
並列開発の「分解」を手作業で行うと、3つの問題が発生します。
1. ファイル重複による確定コンフリクト
「タスクAでauth.tsを修正して、タスクBでもauth.tsに手を入れて...」
2つのworktreeで同じファイルを触った瞬間、マージコンフリクトが確定します。人間が目視で「このファイルはこっち担当、あのファイルはあっち担当」と分けるのは、ファイル数が5を超えたあたりから怪しくなる。10ファイルの依存関係を正確に頭の中で管理できる人がいたら、それはもうIDEです。
2. 共有型の不在による型エラー地獄
タスクAがUserDTOという型を定義して、タスクBが同じUserDTOを別の形で定義する。マージした瞬間、TypeScriptコンパイラが怒涛の赤線を吐く。
並列に作業するなら、「共通の型定義を先に決めておく」のが鉄則です。教科書にはそう書いてある。でも教科書どおりにできる人は、そもそも締め切りに追われていない。
3. 統合テストの後回し
分解に集中するあまり、統合のことを考えていない。全タスクが完了してからマージしたら、テストが12件落ちている。どのタスクの変更が原因なのか、もう追えない。
「マージしてから直す」ではなく、「直せる順番でマージする」。この発想の転換が必要でした。
dev-decompose: ファイル境界分析による自動タスク分解
dev-decomposeは、大きなIssueをファイル境界で自動的にサブタスクに分割するスキルです。
核心のルール: 1ファイル = 1タスク
原則は単純です。各ソースファイルは、必ず1つのサブタスクにだけ所属する。
Good: ファイルの所属が明確
├── task1: src/models/user.ts, src/models/user.test.ts
├── task2: src/routes/auth.ts, src/routes/auth.test.ts
└── contract: src/types/auth.ts (共有型)
Bad: 同じファイルが複数タスクに登場
├── task1: src/models/user.ts, src/routes/auth.ts ← 重複!
└── task2: src/routes/auth.ts, src/middleware/jwt.ts ← コンフリクト確定
ファイル重複がゼロなら、テキストレベルのマージコンフリクトはゼロ。gitの仕組み上、異なるファイルへの変更は衝突しない。証明終わり。
依存グラフの構築
dev-decomposeはまず、変更対象ファイルのimport/require関係を解析して依存グラフを構築します。
1. 各ファイルのimport文を解析
2. 変更対象ファイル同士の参照関係を特定
3. 双方向依存(A→B かつ B→A)→ 「密結合」→ 同一タスクに同居
4. 一方向依存(A→B のみ)→ 「疎結合」→ contract経由で分離可能
密結合のファイルは無理に分けない。分けるとcontractが肥大化して、並列化の旨味がなくなります(分割したのに会議が増えた、みたいな話です)。逆に疎結合のファイルは積極的に分離して、並列実行のメリットを最大化する。
テストファイルの同居ルール
テストファイルは必ず実装ファイルと同じタスクに入ります。
task1:
- src/services/order.ts ← 実装
- src/services/order.test.ts ← テスト(必ずセット)
テストと実装を別タスクに分けるのは、見た目の並列度は上がりますが、「テストを書く人が実装の文脈を持っていない」状態になります(答えを知らない人が採点しているようなもの)。タスク数が増えただけで品質は下がる。6並列で全テスト赤より、2並列で全テスト緑。分割数は正義じゃない。
フォールバック: 分解しない勇気
dev-decomposeには分解をやめる判断も組み込まれています。
| 条件 | 判断 |
|---|---|
| 変更ファイルが4未満 | worktree分割のオーバーヘッドが利益を上回る |
| 全ファイルが密結合 | 分離不可能、single modeにフォールバック |
| サブタスクが1つ | 分割する意味がない |
| 単一コンポーネント内の変更 | 自然な分割境界がない |
{
"status": "single_fallback",
"subtask_count": 1,
"reason": "All files are tightly coupled"
}
「並列化ありき」ではなく、分解して速くなるときだけ分解する。包丁を買ったら何でも切りたくなるように、worktreeを覚えると何でも分割したくなる。その衝動にブレーキをかけるのもdev-decomposeの仕事です。
contract branch: 共通型定義で並列ワーカーの整合性を担保する
並列で作業するワーカーが、同じ型定義を参照していないと、マージ後に型エラーの嵐になります。この問題を解決するのがcontract branchパターンです。
仕組み
main ─── feature/issue-42-contract ← dev-decomposeが共有型をコミット
├── feature/issue-42-task1 ← contractから分岐
├── feature/issue-42-task2 ← contractから分岐
├── feature/issue-42-task3 ← contractから分岐
└── feature/issue-42-merge ← 統合先
- dev-decomposeが変更に必要な共有の型定義・インターフェースを特定
feature/issue-42-contractブランチを作成し、型定義だけをコミット- 各タスクのworktreeは、このcontractブランチから分岐
全タスクが同じ型定義を出発点にしているので、UserDTOの定義が食い違う問題が構造的に起きない。
contractに含めるもの / 含めないもの
| 含める | 含めない |
|---|---|
| 型定義・インターフェース | 実装ロジック |
| enum宣言 | テストコード |
| API境界の関数シグネチャ | 設定ファイル |
| 共有の定数 | ビジネスルール |
contractはあくまで「約束」です。実装は各タスクの責任。約束を先に決めておくから、並列に実装しても噛み合う。「実装じゃなくインターフェースに依存せよ」——SOLID原則、20年経っても現役です。
contractが不要なケース
全ファイルが1つのタスクに収まる場合(single_fallback)や、タスク間で共有する型がない場合は、contract branchを作りません。不要な抽象化はオーバーヘッドです。
git-prepare: worktree自動作成と環境同期
各タスクのworktreeはgit-prepareスキルが自動作成します。git-prepareの.envハードリンクやブランチ命名規則についてはSKILL.mdの書き方と設計パターンでも触れています。
# dev-decomposeが内部で呼び出す
$SKILLS_DIR/git-prepare/scripts/git-prepare.sh 42 \
--suffix task1 \
--base feature/issue-42-contract \
--env-mode hardlink
--suffix task1でworktreeの命名を自動化し、--baseでcontract branchからの分岐を指定。--env-mode hardlinkで.envファイルをハードリンクで共有し、環境変数の手動コピーも不要にしています。worktreeを作って.envをコピーし忘れ、「API繋がらないんだけど」とSlackに書く——チーム全員が一度は踏む地雷です。
dev-integrate: マージ・型チェック・統合テストの自動パイプライン
全タスクが完了したら、dev-integrateが依存関係に基づいた順序でマージします。
パイプラインの流れ
Step 1: flow.json確認 → 全タスク completed?
Step 2: ドリフト検出 → 予定 vs 実際の変更ファイル比較
Step 3: マージ順序決定 → トポロジカルソート(依存なしから先にマージ)
Step 4: マージworktree作成
Step 5: 順次マージ → conflict検出 → auto-resolve or abort
Step 6: 型チェック → tsc --noEmit / mypy / go vet
Step 7: 統合テスト → dev-validate
Step 8: flow.json更新
トポロジカルソートによるマージ順序
flow.json:
task1: depends_on: [] # 認証モジュール
task2: depends_on: [task1] # ユーザーAPI(認証が必要)
task3: depends_on: [] # ログユーティリティ
計算されたマージ順序: task1 → task3 → task2
理由:
- task1とtask3は依存なし(先にマージ)
- task2はtask1に依存(task1の後でマージ)
「直せる順番でマージする」 がここで実現されています。依存のないタスクから先にマージすることで、もしコンフリクトが起きても影響範囲が小さい。task2でコンフリクトが起きても、task1とtask3のマージは既に成功しているので、やり直しの範囲が限定されます。
自動解決可能なコンフリクト
lock系ファイルは自動解決します。
| ファイルパターン | 戦略 | 理由 |
|---|---|---|
package-lock.json | --theirsで受け入れ | npm installで再生成 |
yarn.lock | --theirsで受け入れ | yarn installで再生成 |
pnpm-lock.yaml | --theirsで受け入れ | pnpm installで再生成 |
*.sum(Go) | --theirsで受け入れ | go mod tidyで再計算 |
lock fileのコンフリクト解消に時間を使うのは完全に無駄です。数千行のdiffを真剣に読んだ5分後、npm install一発で全部上書きされる。それはもう写経です。
コードファイルのコンフリクト: 止まる勇気
コードファイルでコンフリクトが起きた場合、dev-integrateはマージを中断してユーザーに報告します。
{
"integration": {
"status": "failed",
"failed_at_task": "task2",
"error": "unresolvable merge conflict",
"conflict_files": ["src/auth/handler.ts"]
}
}
自動解決を試みて壊れたコードをコミットするより、「ここで詰まりました」と正直に報告するほうが、結果的に速い。コンフリクト解消をAIに丸投げすると、直したはずの箇所から新しいバグが生えてくる。
型チェックと統合テスト
マージが完了したら、プロジェクトの型チェックと統合テストが自動で走ります。
# プロジェクトタイプを自動検出
if [ -f "tsconfig.json" ]; then npx tsc --noEmit
elif [ -f "pyproject.toml" ]; then mypy .
elif [ -f "go.mod" ]; then go vet ./...
elif [ -f "Cargo.toml" ]; then cargo check
fi
# その後、dev-validateで統合テスト
Skill: dev-validate --worktree $MERGE_WORKTREE
型チェックで引っかかるのは、たいてい「contractでstringにしたのに実装側がstring | nullを返してる」みたいな微妙なズレです。contractの詰めが甘かった証。TypeScriptコンパイラは忖度しないので、いい先生になってくれます。
flow.json: 並列パイプラインの進捗追跡
並列開発のオーケストレーション全体を追跡するのがflow.jsonです。
{
"version": "1.0.0",
"issue": 42,
"status": "implementing",
"subtasks": [
{
"id": "task1",
"scope": "認証モジュール",
"files": ["src/models/user.ts", "src/models/user.test.ts"],
"status": "completed",
"actual_files_changed": ["src/models/user.ts", "src/models/user.test.ts"],
"checklist": [
{ "item": "Userスキーマ定義", "done": true },
{ "item": "マイグレーション追加", "done": true }
]
},
{
"id": "task2",
"scope": "認証エンドポイント",
"files": ["src/routes/auth.ts", "src/routes/auth.test.ts"],
"depends_on": ["task1"],
"status": "in_progress",
"checklist": [
{ "item": "POST /auth/login 実装", "done": true },
{ "item": "JWT検証ミドルウェア", "done": false }
]
}
],
"contract": {
"branch": "feature/issue-42-contract",
"files": ["src/types/auth.ts"]
},
"integration": {
"status": "pending"
}
}
なぜファイルベースなのか
flow.jsonがファイルで永続化されている理由は2つあります。
1. auto-compact耐性: Claude Codeのコンテキストウィンドウが圧縮されても、ファイルは消えない。cat flow.jsonで今どこまで進んだか、いつでも確認できる(翌朝の自分が感謝するやつ)。
2. 単一ライター原則: flow.jsonへの書き込みはdev-flow(オーケストレータ)だけが行い、各サブエージェントは自分のworktree内のkickoff.jsonだけを更新する。排他制御が不要なシンプルな設計です。
書き込み権限:
flow.json ← dev-flow, dev-decompose, dev-integrateのみ
kickoff.json (各WT) ← 各サブエージェント(自分のworktreeのみ)
読み取り:
flow.json ← 全サブエージェント(参照のみ)
kickoff.json ← dev-flow(集約のため)
この設計は、前回の状態管理記事で解説したkickoff.jsonの延長線上にあります。状態をファイルに永続化する、JSON Schemaで構造を保証する、人間がいつでもcatで読める。同じ原則を並列開発に拡張しただけです。
ステータスの遷移
analyzing → decomposing → implementing → integrating → pr → iterating → completed
各フェーズの完了条件が明確なので、途中で中断しても「次は何をすればいいか」がflow.jsonを見ればわかります。
Agent Teams との使い分け指針
Claude Codeには、worktree並列開発のほかにAgent Teams(SendMessage/TeamCreate/TaskListを使った複数エージェント協調)もあります。どちらを使うべきか、判断基準をまとめます。
| 観点 | worktree並列(dev-decompose) | Agent Teams |
|---|---|---|
| 得意パターン | ファイル分割可能な実装タスク | リアルタイム協調が必要な調査タスク |
| コンフリクト | ファイル境界で構造的に回避 | 同じファイルを触る可能性あり |
| コスト | worktree数 x dev-kickoff | エージェント数 x ターン数 |
| 状態共有 | flow.json(ファイルベース) | SendMessage(リアルタイム) |
| 適用例 | 認証+API+UIの3モジュール実装 | バグ調査、コード監査、インシデント対応 |
| フィードバック | 統合時にまとめて検出 | 調査中にリアルタイムで方向転換 |
経験則: 「分けて、それぞれ作って、最後に合わせる」ならworktree並列。「みんなで同じものを見ながら、発見を即座に共有する」ならAgent Teams。
具体例を挙げると、私たちはコード監査やバグ調査のように「調査中に発見を即座に共有したい」タスクにはAgent Teamsを使い、機能実装にはworktree並列を使っています。目的に応じた使い分けが大事です。
全体フロー: dev-flow --parallel
ここまでの仕組みを束ねるのがdev-flow --parallelです。
/dev-flow 42 --parallel
Step 1: dev-issue-analyze(要件分析)
↓
Step 2: dev-decompose(自動タスク分解)
├── contract branch作成・型定義コミット
├── worktree x N 自動作成
└── flow.json生成
↓
Step 3: 分解結果チェック
└── subtask_count == 1 → single modeにフォールバック
↓
Step 4: dev-kickoff x N(並列実装)
├── Batch 1: [task1, task3] ── 独立タスクを並列
└── Batch 2: [task2] ── task1完了後に実行
↓
Step 5: 結果集約(kickoff.json → flow.json)
↓
Step 6: dev-integrate(統合)
├── トポロジカルソートでマージ
├── 型チェック
└── 統合テスト
↓
Step 7: git-pr(PR作成)
↓
Step 8: pr-iterate(セルフレビューループ)
小さなIssue(変更ファイル4未満)なら従来どおりdev-kickoff 1本。大きなIssueのときだけ並列モードに入る。使い分けは自動判定なので、開発者は--parallelフラグを付けるだけです。
まとめ
worktree並列開発のボトルネックは「分解」と「統合」でした。dev-decomposeはファイル境界分析でコンフリクトを構造的にゼロにするタスク分解を行い、contract branchで並列ワーカーの型整合性を担保する。dev-integrateは依存順序に基づいた自動マージと型チェック・テスト実行で、統合の手間を最小化する。
並列開発は「速く作る」技術ではなく、「速く作って、ちゃんと合わせる」技術です。分解が雑なら統合で死ぬ。統合を考えない分解は、ただの分裂。dev-decomposeとdev-integrateは、この両面を1つのパイプラインに統合しました。
まずは/dev-flow 42 --parallelで試してみてください。「このIssue、並列にしたら速くなるかな?」という判断すら、dev-decomposeが自動でやってくれます。合わなければsingle modeに戻るだけ。最悪でも「分割しませんでした」と報告されて終わり。判断を委ねるのに失うものがない——それが、このスキルの一番の設計意図かもしれません。
なお、dev-flowを最大限に活用するにはsettings.jsonのhooksやCLAUDE.mdの参照分離でClaude Codeの基盤環境を整えておくことをおすすめします。また、個々のSkillを連携させるオーケストレーション設計の基本も参考になります。
あわせて読みたい
- 【2026年版】AIコーディングツール完全比較 — Claude Code・Codex・Antigravityの選び方
- 【Claude Code】auto-compact対策 完全ガイド — コンテキスト消失を防ぐ状態管理の設計パターン
Claude Code エージェント・安全設計 完全ガイド — この記事を含む10本の記事で、エージェント活用・Hooks安全設計・並列開発を体系的に解説しています。



