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)

  1. Loop 1 エージェントループ — モデルが完了までツールをループ内で呼ぶ
  2. Loop 2 検証ループ — grader が rubric に照らし、不合格ならフィードバックを差し戻す
  3. Loop 3 発見・ディスパッチ — 仕事を発見し並列エージェントに割り当てる
  4. 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層の隔離モデル

レイヤー隔離するもの答える問い
SubAgentContext探索ノイズをメイン会話から隔離
Agent TeamsRoles専門エージェントの協調
WorktreeFiles並列エージェントの書き込み衝突回避

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(makercheckerorchestrator)。すべて無人で回る
Quality(出荷前に仕込む証拠)checker の PASS/FAIL + ledger.sh が記録する trace
Verdict(出荷可否の最終決定)optimizerproposals/ を人間がマージするか。protect-harness.sh により、ここだけが唯一の harness 変更経路
Answerability(説明できる状態)ledger/runs.jsonl。「どの agent が・いくらで・何をしたか」を追跡できる
制約/サンプリング/監査/所有権ループ制約=budget-guardprotect-harness、監査=ledger、所有権=harness 書き込みを人間のみに残す
3つの隠れたコスト(cognitive surrender / debt / orchestration tax)提案を読まず機械的にマージすると顕在化。「ledger と proposal は人が読む」が回避策

つまりこのハーネスにおける outer loop は、optimizer が生成する proposals/ledger を人間が読み、マージするかどうかを判断する、その一点に集約されます。Loop 4 は自己改善の入力を作るだけで、決定は常に人間側にある——「エージェントはレビューできる以上の量を出荷できる。だからこそ稀少資源は人間の判断そのものになる」という主張と一致します。


参考リンク


まとめ

Loop Engineering を Claude Code ネイティブ機能だけで組むとき、ポイントは次の4つです。

  1. Loop 1–3: maker(worktree 隔離)→ checker(独立検証)→ orchestrator(キュー処理)で内側のループを無人化する
  2. 駆動: Stop フック(stop-gate.sh)で「キューが残る間は止めない」を実装する。停止条件はスクリプト側に持つ
  3. Loop 4: optimizerproposals/ に書くだけ。protect-harness.sh で harness 直接編集を封じ、マージ判断は人間に残す
  4. 観測: ledger.sh でコストと結果を残し、Outer Loop(Quality / Verdict / Answerability)の材料にする

プロンプトを上手く打つより、「止め方と隔離と検証」を先に設計する——それが Loop Engineering の実務寄りの入口だと思います。