並列AIエージェント開発を支える2つの規律 — テスト先行スキル「試練」とworktree掃除スキル「陣払い」
毎度おなじみ、Claude Code のサブエージェントを戦国の軍勢に見立てた「大名システム」で開発しています。
足軽(worktree隔離エージェント)を並列で走らせていると、放っておくと2種類の「見えない負債」が溜まっていきます。
今回は 品質負債(実装漏れ) と 衛生負債(worktree残骸)。この2つを解消するために ── /試練 と /陣払い ── をスキルとして導入したお話。
今回も部下としてのAI(牛尾剛)からインスピレーション(ま、丸パクリではない・・・|д゚))をいただき、作成したスキルです。
要約
- 複数のAIエージェントを並列に走らせる開発は速いが、2つの負債が静かに積もる。
- 品質負債: 実装が「done に見えて実は漏れている」。AIは特にこれをやる。
-
衛生負債: 並列のために切った
git worktreeの残骸が溜まり、ディスク・インデクサ・ブランチ一覧を圧迫する。
-
/試練(攻めの規律): 実装より前に、受け入れ条件から 失敗するテスト(red) を書き切る。実装漏れが「red のまま残る」ことで構造的に可視化される。モックは外部境界だけ、DB・認可・自分の Bean は実物(Testcontainers)で検査するのがキモ。 -
/陣払い(守りの規律): 古い足軽 worktree を安全則つきで一括掃除する。年齢閾値・feature/*ブランチ温存・dirty 確認・locked の unlock を機械化。 - 攻めと守り、両方あって初めて並列エージェント開発が継続可能になる。
0. 前提 — 大名システムと「並列の副作用」
足軽は isolation:"worktree" で起動され、.claude/worktrees/agent-xxxxx/ という別ディレクトリで作業します。これにより複数セッションを並行させても HEAD が衝突しません。
速い。しかし、この並列運用そのものが2つの負債を生みます。
1. 攻めの規律 /試練 — 実装漏れをテスト先行で根治する
1.1 なぜ実装漏れが起きるのか(3類型)
「設計書 → 実装」で進めると実装漏れが多発しました。原因を分解すると3種類あります。
実装後にテストを書くと、テストは「コードが何をするか」をなぞるだけで、仕様の独立した検査になりません。つまり①②を素通りします。(´・ω`・)エッ?
1.2 対応策 — 受け入れ条件から red テストを先に書く
長らく頭を悩ませていたのですが、牛尾さんの本に「TDDを取り入れるといいよ!」と記載がありました。なので、開発順序をこう変えました。テスト作成を /試練(しれん) という独立フェーズに切り出し、実装(/出陣)の前段に置きます。
実装前に書いたテストは red(失敗)が正常。実装は「red を green に変える戦い」になり、未消化の受け入れ条件が red のまま残るので、実装漏れが構造的に見えるようになります。これで①②は潰れます。
牛尾さんもこうすべきと書かれていました。
1.3 ③を潰す本丸 — 受け入れ条件のトレーサビリティ
ただし、テスト先行だけでは③(テスト自体の漏れ)は消えません。テストを書く側も実装する側も、同じ仕様を見落とすからです。
そこで 軍議で「受け入れ条件(acceptance criteria)」を検証可能な粒度で列挙し、試練で条件1個=テスト1本を対応づけ、検分で「全条件に green テストがあるか」を照合します。
| test-first だけ | + 受け入れ条件トレーサビリティ | |
|---|---|---|
| ① 実装サボり | ✅ 効く | ✅ |
| ② 分解漏れ | ✅ 効く | ✅ |
| ③ テスト自体の漏れ | ❌ 効かない | ✅ これで潰す |
受け入れ条件の例(検証可能な粒度で書くのがコツ):
AC-1: 未認証で GET /api/v1/foo を叩くと 401
AC-2: 他組織の foo を取得しようとすると 403(テナント分離)
AC-3: name 未指定で POST すると 400 とフィールドエラー
AC-4: 正常作成で 201 と Location ヘッダ
1.4 試練の最重要ポリシー — モックは外部境界だけ
ここが一番ハマったところです。試練をモック中心にすると、試練が逆に実装漏れの隠蔽装置になります。
私のプロジェクトは「純 Mockito のユニットテストでは緑なのに、実 DB / 本番で壊れる」バグを何度も踏んできました(Bean 名衝突で起動不能、トランザクション設定ミス、エンティティの初期値欠落での NULL 挿入、更新時のデータ破壊…)。これらは全部モックなら通ってしまう。
なので試練では、受け入れ条件の性質ごとにできるだけ実物に近いレベルで書きます。
| 受け入れ条件の種類 | テストの書き方 | モック |
|---|---|---|
| 認可境界(401/403・テナント分離) | 実 Security フィルタ + MockMvc、DB 絡みは Testcontainers | ❌ 実認可を通す |
| API契約(ステータス・DTO・バリデーション) | MockMvc(実 Controller/Bean 解決) | ❌ |
| 永続化・更新 | Testcontainers で永続 → 再読込まで | ❌ |
| 純粋ドメインロジック | 素のユニットテスト | △ 協力者があれば最小限 |
| 外部境界(決済・ストレージ・メール) | スタブ/モック | ✅ ここだけ |
原則: DB・認可・自分の Bean はモックしない。 モックを許すのは自分で制御できない外部サービス境界だけ。
テストはポートも固定しません(RANDOM_PORT + Testcontainers 自動採番)。固定すると並列実行で衝突します。
2. 守りの規律 /陣払い — worktree 残骸を安全に掃除する
2.1 なぜ溜まるのか
足軽を起動するたびに .claude/worktrees/agent-xxxxx/ が増えます。成果を本リポに統合した後は不要ですが、放っておくと:
- ディスク圧迫(
node_modules/.gradleキャッシュ込みで1個数百MB) - IDE のインデクサが大量ファイルを舐めて重くなる
-
git worktree listが肥大化して状況把握が困難に - 孤立した
worktree-agent-*ブランチが積もる
しかも Windows ではファイルロック(Gradle daemon、IDE インデクサ)で消せないことがあり、手で消すのは地味に危険です。間違って稼働中の足軽や feature/* ブランチを消すと作業が飛びます。
2.2 解決策 — 安全則を機械化した一括掃除
/陣払い [日数](既定7日)で、HEAD コミットが指定日数以上前の agent worktree だけを安全に撤去します。
掃除を支える安全則がポイントです。
| 安全則 | 理由 |
|---|---|
対象は agent-* のみ。dev-main/e2e-run 等は触らない |
非 agent worktree は別用途で常駐 |
| 閾値以内(例: 直近7日)は残す | 他セッションで稼働中の足軽かもしれない |
feature/*・feat/* ブランチは温存
|
他 PR・他セッションが参照しうる。worktree を消してもコミット済み成果は git に残る |
| dirty な worktree は中身確認してから |
--force は未コミット変更を失う。実体的なソース変更が残っていたら撤去保留 |
| locked は unlock してから削除 | 死んだ agent の stale lock を解除 |
「年齢で足切り」+「feature ブランチ温存」+「dirty は保留」の3点で、速く・かつ事故らない掃除になります。
3. 攻めと守り、両方いる
並列エージェント開発は速さが魅力ですが、その速さは品質負債と衛生負債を同時に増やす性質を持っています。
-
/試練は、AIが得意な「done に見えて漏れている」を、red テストと受け入れ条件トレーサビリティで構造的に潰す ── 攻めの規律。 -
/陣払いは、並列の副産物である worktree 残骸を、安全則つきで機械的に掃除する ── 守りの規律。
派手な仕組みではありません。でも、この2つの地味な規律があるかないかで、並列エージェント開発が「速いけど荒れる」か「速くて続く」かが分かれます。