
Loop Engineering を Claude Code ネイティブ機能(Skill / Subagent / Hooks)で実装する
コーディングエージェントにターンごとにプロンプトを打つ立場から自分を引き上げ、「仕事を発見し、サブエージェントに割り当て、結果を検証し、状態を永続化し、次のアクションを決める」システム=ループを設計する側に回る。これが Loop Engineering です。
2026年6月、Addy Osmani のエッセイをきっかけに広まりました。Claude Code 責任者の Boris Cherny も「自分はもう Claude にプロンプトを打たない。ループを走らせている」と語っています。進化の系譜は Context Engineering → Harness Engineering → Loop Engineering の三層で、Loop はハーネスの一つ上——タイマーで走り、ヘルパーを spawn し、自分自身に入力を供給し続ける層です。
本記事では、この考え方を Claude Code ネイティブ機能(Skill / Subagent / Hooks)だけでどう実装するかを整理します。
ループの階層(stacking loops)
- Loop 1 エージェントループ — モデルが完了までツールをループ内で呼ぶ
- Loop 2 検証ループ — grader が rubric に照らし、不合格ならフィードバックを差し戻す
- Loop 3 発見・ディスパッチ — 仕事を発見し並列エージェントに割り当てる
- Loop 4 自己改善(hill-climbing) — trace を分析し、戻り矢印がハーネス自体(プロンプト・rubric・skill)を書き換える。価値が複利で効く層
肝は次の一文に集約できます。
駆動は Stop フック、隔離は worktree、検証は maker/checker 分離、改善はハーネス自身の編集。
Claude Code への対応は次のとおりです。
| Loop | 役割 | Claude Code での実装 |
|---|---|---|
| Loop 1(エージェント) | 実装 | maker subagent(isolation: worktree) |
| Loop 2(検証) | 合否判定 | checker subagent + 決定論チェックの Stop フック |
| Loop 3(発見&ディスパッチ) | キュー処理 | orchestrator subagent + Stop フックで継続 + /loop |
| Loop 4(自己改善) | 改善案 | optimizer subagent。直接編集は PreToolUse で禁止し人間がマージ |
/loop は最大3日間ローカルでタスクをスケジュールする機能です(例: /loop 30m /triage)。
flowchart TB
subgraph L3["Loop 3: orchestrator"]
Q[".claude/tasks/"] --> O[orchestrator]
O -->|Task| M[maker]
M -->|実装結果| C[checker]
C -->|FAIL| M
C -->|PASS| Commit[commit & キュー削除]
end
L3 --> Ledger["ledger/runs.jsonl"]
Ledger --> Opt[optimizer]
Opt --> Prop["proposals/"]
Prop -->|人間がマージ| Harness["agents / skills / rubric"]
ディレクトリ構成
.claude/
├── agents/
│ ├── orchestrator.md # Loop 3
│ ├── maker.md # Loop 1(worktree隔離)
│ ├── checker.md # Loop 2(独立した検証者)
│ └── optimizer.md # Loop 4(改善案を出すだけ)
├── skills/
│ └── rubric/SKILL.md # 合格基準。Loop 4 が書き換える対象
├── commands/
│ ├── triage.md # /loop が叩くエントリ
│ └── optimize.md # 自己改善の周回
├── hooks/
│ ├── ledger.sh # trace 永続化
│ ├── stop-gate.sh # ループ継続判定(駆動の中核)
│ ├── budget-guard.sh # コスト上限
│ └── protect-harness.sh # ハーネス自己改変の禁止ゲート
├── settings.json # フック登録
├── tasks/ # 仕事キュー(1ファイル=1タスク)
└── ledger/runs.jsonl
Loop 1 — maker subagent
isolation: worktree で各 subagent が一時 worktree を持ち、変更なしなら自動削除されます。並列で同じファイルを触っても衝突しません。
<!-- .claude/agents/maker.md -->
---
name: maker
description: タスク仕様を受け取り、worktree内で実装する。Use when実装タスクを処理する時。
tools: Read, Write, Edit, Bash, Grep, Glob
model: sonnet
isolation: worktree
maxTurns: 40
---
あなたは実装担当です。受け取った仕様を満たすコードを書き、
このworktree内で完結させてください。他のworktreeの状態は仮定しないこと。
完了したら「何を・なぜ変えたか」を3行で要約して返してください。
Loop 2 — checker subagent + rubric skill
checker は maker と別の context window で動き、合否だけ返します。rubric を skill にするのは、後で Loop 4 がここを書き換えて改善するためです。
<!-- .claude/agents/checker.md -->
---
name: checker
description: makerの変更をrubricに照らして検証する独立した評価者。Use this proactively after maker完了時。
tools: Read, Bash, Grep, Glob
disallowedTools: Write, Edit
model: sonnet
skills: rubric
---
あなたは検証担当です。実装は見ず、テスト・lint・型・rubricの観点で
合否を判定してください。出力は必ず PASS / FAIL と、FAILなら失敗理由のみ。
コードは書かない。自分で直そうとしないこと。
<!-- .claude/skills/rubric/SKILL.md -->
---
name: rubric
description: 変更の合格基準。テスト・lint・型・スコープ逸脱の判定に使う。
allowed-tools: Read, Bash
---
合格条件(すべて満たすこと):
1. `npm test` が全通過
2. `npm run lint && npm run typecheck` がエラー0
3. 差分が仕様の範囲内(無関係なファイルを触っていない)
失敗時は、どの条件が・なぜ落ちたかを1〜2行で報告する。
skills: を subagent に書くと、その skill 本文が起動時に subagent の context に注入され、毎回探し直す必要がなくなります。
Loop 3 — orchestrator subagent
Task ツールを持たせると subagent を起動できます。maker → checker →(FAIL なら再試行)を回します。
<!-- .claude/agents/orchestrator.md -->
---
name: orchestrator
description: tasks/のキューを発見し、makerとcheckerに割り当てて完了まで回す。
tools: Read, Bash, Glob, Task
model: sonnet
---
`.claude/tasks/` の TASK-*.md を1件ずつ処理してください。各タスクで:
1. maker subagent に仕様を渡して実装させる
2. checker subagent に検証させる
3. FAIL なら失敗理由を添えて maker を再起動(最大3回)
4. PASS なら commit し、そのタスクファイルを削除する
5. 結果(task / passed / attempts)を1行で報告する
1件ずつ順に処理し、キューが空になったら停止してよい。
ループ駆動の中核 — Stop フック
Stop フックが exit 2(または decision: block)を返すと、Claude は停止せず作業を続行させられます。「キュー or 予算が残る間は止めない」条件にすれば、それ自体がループになります。停止条件を必ずスクリプト側に持たせる(無いと無限ループ化します)。
# .claude/hooks/stop-gate.sh
#!/usr/bin/env bash
# 停止条件①:未処理タスクが残っていれば継続
remaining=$(ls .claude/tasks/TASK-*.md 2>/dev/null | wc -l)
# 停止条件②:予算超過なら止める
spent=$(jq -s 'map(.cost_usd) | add // 0' .claude/ledger/runs.jsonl 2>/dev/null)
cap=5.0
over=$(echo "$spent > $cap" | bc -l)
if [ "$remaining" -gt 0 ] && [ "$over" -eq 0 ]; then
echo '{"decision":"block","reason":"未処理タスクが残っています。次のタスクを処理してください。"}'
else
exit 0 # キュー空 or 予算超過 → 停止を許可
fi
ガードレール — PreToolUse フック2種
後者(ハーネス保護)が、自己改善ループを安全にする決定的な仕掛けです。optimizer 自身も harness を直接書き換えられません。
# .claude/hooks/budget-guard.sh (PreToolUse / matcher: Bash)
#!/usr/bin/env bash
spent=$(jq -s 'map(.cost_usd) | add // 0' .claude/ledger/runs.jsonl 2>/dev/null)
if (( $(echo "$spent > 5.0" | bc -l) )); then
echo "予算上限 \$5.0 を超過。これ以上の実行を停止します。" >&2
exit 2 # exit 2 でツール呼び出しをブロック
fi
exit 0
# .claude/hooks/protect-harness.sh (PreToolUse / matcher: Edit|Write)
#!/usr/bin/env bash
path=$(jq -r '.tool_input.file_path // empty')
case "$path" in
*.claude/agents/*|*.claude/skills/*|*harness/*)
echo "ハーネス(agent/skill/rubric)への直接編集は禁止です。proposals/ に改善案を書いてください。" >&2
exit 2 ;; # 誰も harness を直接書き換えられない
esac
exit 0
ledger — PostToolUse フック
PostToolUse で Agent 呼び出しの tool_response には、subagent の最終テキストと使用量テレメトリが入るので、subagent ごとのコストを記録できます。
# .claude/hooks/ledger.sh (PostToolUse / matcher: Agent)
#!/usr/bin/env bash
input=$(cat)
agent=$(echo "$input" | jq -r '.tool_input.subagent_type // "unknown"')
text=$(echo "$input" | jq -r '.tool_response.text // ""' | tail -c 800)
cost=$(echo "$input" | jq -r '.tool_response.usage.total_cost_usd // 0')
jq -cn --arg a "$agent" --arg t "$text" --argjson c "$cost" \
'{ts:now, agent:$a, cost_usd:$c, note:$t}' >> .claude/ledger/runs.jsonl
フック登録 — settings.json
{
"hooks": {
"PreToolUse": [
{ "matcher": "Bash", "hooks": [{ "type": "command", "command": ".claude/hooks/budget-guard.sh" }] },
{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": ".claude/hooks/protect-harness.sh" }] }
],
"PostToolUse": [
{ "matcher": "Agent", "hooks": [{ "type": "command", "command": ".claude/hooks/ledger.sh" }] }
],
"Stop": [
{ "hooks": [{ "type": "command", "command": ".claude/hooks/stop-gate.sh" }] }
]
}
}
Loop 4 — optimizer と自己改善の閉じ方
optimizer は ledger を分析し、改善案を proposals/ に書くだけです。protect-harness.sh があるので agent / skill / rubric を書き換えられません。反映されるのは人間がマージしたときだけ、という構造的ゲートになります。
<!-- .claude/agents/optimizer.md -->
---
name: optimizer
description: ledgerの失敗傾向を分析し、ハーネス改善案をproposals/に出す。
tools: Read, Write, Bash, Grep
model: opus
---
`.claude/ledger/runs.jsonl` を分析してください。
1. pass率・平均試行・平均コストを集計
2. 失敗理由を頻度順に最大3件
3. 各原因に対し、maker.md / checker.md / rubric SKILL.md のどこを
どう直すか unified diff 形式で提案
4. 出力は `proposals/proposal-<日付>.md` に Write のみ(harnessは触らない)
改善仮説は「pass率がN%上がる」など検証可能な形で書くこと。
採否は before/after で eval を回して決める、というイメージです(/goal の評価モデルをハーネス階層に対し人手で行う感覚に近いです)。
無人運用への配線 — /loop と /goal
<!-- .claude/commands/triage.md -->
orchestrator subagent を起動し、.claude/tasks/ のキューを1周処理してください。
# 30分ごとにキューを処理(最大3日間スケジュール)
/loop 30m /triage
# 1日ごとに自己改善案を生成
/loop 1d /optimize
長時間タスクの監査可能な停止判定には agent の Stop フックを使うのが Anthropic 推奨で、stop-gate.sh がそれにあたります。
3層の隔離モデル
| レイヤー | 隔離するもの | 答える問い |
|---|---|---|
| SubAgent | Context | 探索ノイズをメイン会話から隔離 |
| Agent Teams | Roles | 専門エージェントの協調 |
| Worktree | Files | 並列エージェントの書き込み衝突回避 |
maker / checker で context と役割を分け、isolation: worktree でファイルを分け、Stop フックでループを駆動し、PreToolUse フックでハーネスの自己改変を封じます。
ガードレール(要点)
- 停止条件を第一級に:
maxTurns、周回ごとの予算上限、再試行上限 - 不可逆アクションは人間ゲート: ハーネス変更は自動マージ禁止(デプロイ承認フローと同型)
- 隔離の徹底: 並列は worktree で必ず分ける
- 理解の負債に注意: ledger と proposal は必ず人が読む
Own the Outer Loop との対応
Loop Engineering の続編にあたる Osmani 氏の記事 Own the Outer Loop(2026年7月9日)は、「内側のループ(エージェント)」と「外側のループ(人間)」を切り分け、人間が担うべき境界を Quality / Verdict / Answerability の3語で定義しています。上記の maker / checker / orchestrator / optimizer 設計は、実質的にこの内側/外側の境界をすでに実装しています。
| Own the Outer Loop の概念 | このハーネスでの対応 |
|---|---|
| 内側のループ(investigate→implement→verify→repeat) | Loop 1–3(maker→checker→orchestrator)。すべて無人で回る |
| Quality(出荷前に仕込む証拠) | checker の PASS/FAIL + ledger.sh が記録する trace |
| Verdict(出荷可否の最終決定) | optimizer の proposals/ を人間がマージするか。protect-harness.sh により、ここだけが唯一の harness 変更経路 |
| Answerability(説明できる状態) | ledger/runs.jsonl。「どの agent が・いくらで・何をしたか」を追跡できる |
| 制約/サンプリング/監査/所有権ループ | 制約=budget-guard・protect-harness、監査=ledger、所有権=harness 書き込みを人間のみに残す |
| 3つの隠れたコスト(cognitive surrender / debt / orchestration tax) | 提案を読まず機械的にマージすると顕在化。「ledger と proposal は人が読む」が回避策 |
つまりこのハーネスにおける outer loop は、optimizer が生成する proposals/ と ledger を人間が読み、マージするかどうかを判断する、その一点に集約されます。Loop 4 は自己改善の入力を作るだけで、決定は常に人間側にある——「エージェントはレビューできる以上の量を出荷できる。だからこそ稀少資源は人間の判断そのものになる」という主張と一致します。
参考リンク
- Loop Engineering(Addy Osmani・原典)
- Own the Outer Loop(Addy Osmani)
- Claude Code subagents
- Claude Code hooks
- Claude Code worktrees
- The Art of Loop Engineering(LangChain)
まとめ
Loop Engineering を Claude Code ネイティブ機能だけで組むとき、ポイントは次の4つです。
- Loop 1–3:
maker(worktree 隔離)→checker(独立検証)→orchestrator(キュー処理)で内側のループを無人化する - 駆動: Stop フック(
stop-gate.sh)で「キューが残る間は止めない」を実装する。停止条件はスクリプト側に持つ - Loop 4:
optimizerはproposals/に書くだけ。protect-harness.shで harness 直接編集を封じ、マージ判断は人間に残す - 観測:
ledger.shでコストと結果を残し、Outer Loop(Quality / Verdict / Answerability)の材料にする
プロンプトを上手く打つより、「止め方と隔離と検証」を先に設計する——それが Loop Engineering の実務寄りの入口だと思います。