技術 約5分で読めます

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.mdCodex → Claude へのタスク指示
work/review.mdCodex → Claude へのレビュー指摘
logs/.claude_doneClaude → 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が長くなると、後半の指示が記憶から消える。

  • 必要最低限の指示に絞る
  • 詳細は別ファイルに分離して「必要なときだけ読め」と書く
  • 最重要の指示はファイルの先頭に置く

まとめ

省コストのポイント:

  1. ブロッキング待機でAPI呼び出し削減: 待機中は何もしないと明記
  2. 読んではいけないファイルの明示: ログ・アーカイブ読み禁止
  3. タスクごとのセッションリセット: コンテキストの蓄積を防止
  4. 指示の書き方: 「やれ」は強めに、指示ファイルは短く

LLMに「やるな」と言えば素直に従う。「やれ」は省略されがちなので強調して書く。