tmuxでClaude CodeとCodexを連携させて一晩放置でゲームを作らせる(改善編)
前回までのおさらい
準備編でClaude CodeとCodexを連携させるアイデアを書いて、実践編で実際に動かした。結果、1134行のゲームコードが生成された。
上限問題が発生
実践編のスクリプトで別のプロジェクトを回したら、速攻でClaude Code MAXプラン(x20、一番上)の上限に到達。じゃあCodexをメインに、と切り替えたら今度はCodex Proプラン(これも一番上)が上限。
原因を考えた。毎回tmuxでコマンドを叩き合うオーバーヘッドが大きすぎる。
そもそも:
- Codex(5.2)は十分賢くてコンテキスト管理も自分でできる
- Claude Codeはコンテキストがすぐ溢れるけど、直前の記憶が完全に飛ぶわけじゃない
だったら:
- 1タスク中は同一のClaudeに作業させる
- push完了したらClaudeを閉じる → 次は新しいClaudeが指示書から動く
- Codexは毎回新しいセッションにしない → 毎度ファイルフルスキャンしなくて済む
これで両方の上限問題を緩和できるのでは、という仮説で改善した。
役割分担の再設計
既存(実践編)
対等なループ構造。ClaudeとCodexが交互に動く。
Claude(実装)→ Codex(レビュー)→ Claude(修正)→ ...
改善版
Codexが上位層としてClaudeを管理する二層構造。
Codex(メタ層): 指示・レビュー・進行管理
↓ ファイル経由
Claude(作業層): 実装・commit・push
通信はすべてファイル経由:
| ファイル | 用途 |
|---|---|
work/task.md | Codex → Claude へのタスク指示 |
work/review.md | Codex → Claude へのレビュー指摘 |
logs/.claude_done | Claude → Codex への完了シグナル |
API呼び出し削減: ブロッキング待機
既存の問題
while [ ! -f "$REVIEW_SIGNAL" ]; do
sleep 2
done
sleep 2秒ごとにファイル存在チェック。ループ自体はAPIを呼ばないが、Codexがエージェントモードで動いていると、この待機中にも定期的にAPIを叩く可能性がある。
改善: ブロッキング待機
timeout 3600 bash -c "while [ ! -f logs/.claude_done ]; do sleep 10; done"
AGENTS.mdに「このコマンドが終了するまで、追加のAPIリクエストは行わない」と明記しておく。Codexはこの指示に従い、待機中は何もしない。
### 6. ブロッキング待機
**重要**: このコマンドが終了するまで、追加のAPIリクエストは行わない
\`\`\`bash
timeout 3600 bash -c "while [ ! -f logs/.claude_done ]; do sleep 10; done"
\`\`\`
タイムアウトは1時間。無限ループ防止。
コンテキスト軽量化
1. 読んではいけないファイルの明示
AGENTS.md(Codex向け):
## 禁止事項
- `logs/`内のログファイルを読まない
- `docs/progress-archive/`を読まない
- 過去のコミット履歴を大量に読まない
- Claudeに余計なファイル(ログ、履歴)を読ませない
CLAUDE.md(Claude向け):
### 読んではいけないファイル
コンテキスト節約のため、以下は読まない:
- `logs/`内のファイル(シグナル以外)
- `docs/progress-archive/`
- `work/`内の過去のファイル(現在のタスク以外)
禁止リストを明示しておくと、LLMは素直に従う。
2. 一時ファイルの強制削除
タスク完了後に必ずクリーンアップ:
# シグナル・一時ファイル削除(コンテキスト軽量化のため必須)
rm -f logs/.claude_done
rm -f work/task.md work/review.md work/progress.md
削除理由をコメントで書いておくと、LLMが勝手に省略しない。
3. セッション分離
タスクごとにClaudeを再起動する:
# 環境リセット
rm -f logs/.claude_done
tmux kill-window -t codex-dev:claude-worker 2>/dev/null || true
# Claude起動(新しいウィンドウ)
tmux new-window -t codex-dev -n claude-worker -c $(pwd)
tmux send-keys -t codex-dev:claude-worker "claude --dangerously-skip-permissions" C-m
これで前のタスクのコンテキストが引き継がれない。新しいClaudeは新鮮な状態で次のタスクを開始する。
タスク実行フロー
9ステップの流れ:
1. タスク選択(todo.mdから)
2. 環境リセット(シグナル削除、Claudeウィンドウ破棄)
3. Claude起動(新規tmuxウィンドウ)
4. タスク指示書作成(work/task.md)
5. 指示送信
6. ブロッキング待機(APIを呼ばない)
7. レビュー(最大3回の修正サイクル)
8. 完了処理(一時ファイル削除、todo.md更新)
9. 次のタスクへ
ポイントはステップ2と6と8。
- ステップ2: 前のタスクの残骸を消す
- ステップ6: 待機中はAPIを呼ばない
- ステップ8: 次のタスクに余計なコンテキストを持ち込まない
比較: 既存 vs 改善版
| 観点 | 既存(実践編) | 改善版 |
|---|---|---|
| 待機方法 | sleep 2 ポーリング | シグナルファイル + ブロッキング |
| コンテキスト | 蓄積される | タスクごとにリセット |
| 役割分担 | 対等なループ | Codex主導の二層構造 |
| 禁止事項 | なし | ログ・アーカイブ読み禁止を明示 |
起動スクリプト
改善版の起動は1行:
codex --sandbox danger-full-access --ask-for-approval never
Codexがエージェントモードで起動して、AGENTS.mdを自動読み込み。
ただし、AGENTS.mdを読んだだけでは止まる。初回だけ「AGENTS.mdを読んで実行しろ」と明示的に指示する必要がある。1回やれば以降は勝手に回る。
tmuxセッションの作成はスクリプト側で処理:
#!/bin/bash
# codex-main.sh
set -e
cd "$(dirname "$0")/.."
SESSION_NAME="codex-dev"
PROJECT_DIR="$(pwd)"
mkdir -p logs work
if ! tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
tmux new-session -d -s "$SESSION_NAME" -n "main" -c "$PROJECT_DIR"
fi
codex --sandbox danger-full-access --ask-for-approval never
LLMへの指示の書き方
実践で気づいたこと。
「やるな」は効く、「やれ」は省略される
LLMは禁止事項にはわりと従う。でも「やれ」は省略されることがある。
対策:
- 重要な指示は強めの言葉で書く(「必須」「絶対」「毎回」など)
- 指示ファイルは短くして記憶から消えないようにする
- 同じ指示を複数箇所に書く(反復の有効性は研究でも示されている)
# NG: 省略されがち
作業完了後、シグナルファイルを作成する
# OK: 強調 + 理由付き
**作業完了後、必ず `touch logs/.claude_done` を実行**(Codexに完了を通知するため必須)
指示ファイルは短く
AGENTS.mdやCLAUDE.mdが長くなると、後半の指示が記憶から消える。
- 必要最低限の指示に絞る
- 詳細は別ファイルに分離して「必要なときだけ読め」と書く
- 最重要の指示はファイルの先頭に置く
まとめ
省コストのポイント:
- ブロッキング待機でAPI呼び出し削減: 待機中は何もしないと明記
- 読んではいけないファイルの明示: ログ・アーカイブ読み禁止
- タスクごとのセッションリセット: コンテキストの蓄積を防止
- 指示の書き方: 「やれ」は強めに、指示ファイルは短く
LLMに「やるな」と言えば素直に従う。「やれ」は省略されがちなので強調して書く。