Letting Claude Code and Codex Run Overnight in tmux (Practice)
Recap
In the setup article, I described the idea and basic scripts for linking Claude Code and Codex.
Claude Code (implement)
↓ code output
Codex (review)
↓ review result
Claude Code (fix)
↓ repeat...
This time I actually ran it.
Scripts Used
Four scripts in total.
start-tmux.sh (Launch Script)
Starts a tmux session split left/right with workers in each pane.
#!/bin/bash
# tmux session launch script
# Left: Claude Code (implement) / Right: Codex (review)
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
SESSION_NAME="my-dev" # change to whatever you want
MAX_ROUNDS=${1:-10}
# Check for existing session
if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
echo "Session '$SESSION_NAME' already exists"
echo "Attach: tmux attach -t $SESSION_NAME"
echo "Delete: tmux kill-session -t $SESSION_NAME"
exit 1
fi
# Create log directory / clear signal files
mkdir -p "$PROJECT_DIR/logs"
rm -f "$PROJECT_DIR/logs/.commit_done" "$PROJECT_DIR/logs/.review_done"
# Grant execute permission
chmod +x "$PROJECT_DIR/scripts/claude-worker.sh"
chmod +x "$PROJECT_DIR/scripts/codex-worker.sh"
# Create tmux session
tmux new-session -d -s "$SESSION_NAME" -c "$PROJECT_DIR"
# Left pane: Claude Code worker
tmux send-keys -t "$SESSION_NAME" "./scripts/claude-worker.sh $MAX_ROUNDS" C-m
# Right pane: Codex worker
tmux split-window -h -t "$SESSION_NAME" -c "$PROJECT_DIR"
tmux send-keys -t "$SESSION_NAME" "./scripts/codex-worker.sh" C-m
# Attach to session
echo "Starting tmux session '$SESSION_NAME'..."
echo "Left: Claude Code (implement) / Right: Codex (review)"
tmux attach -t "$SESSION_NAME"
claude-worker.sh (Left Pane)
Claude Code handles implementation. One task per round; waits for Codex after each commit.
#!/bin/bash
# Claude Code worker (left pane)
# implement → commit → wait for review → fix loop
set -e
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
LOG_DIR="$PROJECT_DIR/logs"
CLAUDE_LOG="$LOG_DIR/claude.log"
REVIEW_FILE="$LOG_DIR/review.md"
COMMIT_SIGNAL="$LOG_DIR/.commit_done"
REVIEW_SIGNAL="$LOG_DIR/.review_done"
MAX_ROUNDS=${1:-10}
mkdir -p "$LOG_DIR"
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
cd "$PROJECT_DIR"
# Initialize signal files
rm -f "$COMMIT_SIGNAL" "$REVIEW_SIGNAL"
echo -e "${GREEN}=== Claude Code worker started ===${NC}"
for ((round=1; round<=MAX_ROUNDS; round++)); do
echo -e "${YELLOW}=== Round $round/$MAX_ROUNDS ===${NC}"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Round $round started" >> "$CLAUDE_LOG"
# Step 1: Implement
echo -e "${CYAN}[Implementing...]${NC}"
claude --dangerously-skip-permissions -p "
You are developing ○○. (describe what you're building here)
## Instructions
1. Check docs/todo.md for incomplete tasks
2. Pick the next incomplete task and implement it
3. After implementation, record in docs/progress.md
4. git commit the changes (concise message)
5. git push
6. Mark the task as complete in docs/todo.md
## Constraints
- Make decisions without asking questions
- Fix errors on your own
- Focus on one task per round
Start implementing.
" 2>&1 | tee -a "$CLAUDE_LOG"
# Commit complete signal
touch "$COMMIT_SIGNAL"
echo -e "${GREEN}[Commit done - waiting for Codex review...]${NC}"
# Wait for review to complete
while [ ! -f "$REVIEW_SIGNAL" ]; do
sleep 2
done
rm -f "$REVIEW_SIGNAL"
# Step 2: Check review result / fix if needed
if [ -f "$REVIEW_FILE" ] && ! grep -qi "LGTM" "$REVIEW_FILE"; then
echo -e "${CYAN}[Review feedback received - fixing...]${NC}"
claude --dangerously-skip-permissions -p "
Received the following review from Codex.
Fix the issues and commit and push again.
$(cat "$REVIEW_FILE")
" 2>&1 | tee -a "$CLAUDE_LOG"
else
echo -e "${GREEN}[LGTM - no fixes needed]${NC}"
fi
# Step 3: Code Simplifier
echo -e "${CYAN}[Running Code Simplifier...]${NC}"
claude --dangerously-skip-permissions -p "
/plugin run code-simplifier
Clean up and simplify recently changed code.
Improve readability and maintainability without changing functionality.
Commit and push if there are changes.
" 2>&1 | tee -a "$CLAUDE_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Round $round complete" >> "$CLAUDE_LOG"
# Check if all tasks are done
if ! grep -q "\- \[ \]" "$PROJECT_DIR/docs/todo.md" 2>/dev/null; then
echo -e "${GREEN}All tasks complete!${NC}"
break
fi
sleep 2
done
echo -e "${GREEN}=== Claude Code worker finished ===${NC}"
codex-worker.sh (Right Pane)
Detects commits and runs Codex reviews.
#!/bin/bash
# Codex worker (right pane)
# detect commit → run review loop
set -e
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
LOG_DIR="$PROJECT_DIR/logs"
CODEX_LOG="$LOG_DIR/codex.log"
REVIEW_FILE="$LOG_DIR/review.md"
COMMIT_SIGNAL="$LOG_DIR/.commit_done"
REVIEW_SIGNAL="$LOG_DIR/.review_done"
mkdir -p "$LOG_DIR"
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
cd "$PROJECT_DIR"
echo -e "${GREEN}=== Codex worker started ===${NC}"
echo "Waiting for commits..."
while true; do
# Wait for commit signal
if [ -f "$COMMIT_SIGNAL" ]; then
rm -f "$COMMIT_SIGNAL"
echo -e "${YELLOW}=== New commit detected ===${NC}"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Review started" >> "$CODEX_LOG"
echo -e "${CYAN}[Reviewing...]${NC}"
codex review --commit HEAD 2>&1 | tee "$REVIEW_FILE" | tee -a "$CODEX_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Review complete" >> "$CODEX_LOG"
# Review complete signal
touch "$REVIEW_SIGNAL"
echo -e "${GREEN}[Review done]${NC}"
echo ""
echo "Waiting for commits..."
fi
sleep 2
done
ai-loop.sh (Simple Version)
A simpler version without tmux that runs sequentially.
#!/bin/bash
# AI auto-dev loop script
# Claude Code implement → Codex review → Claude Code fix
set -e
# Config
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
LOG_DIR="$PROJECT_DIR/logs"
CLAUDE_LOG="$LOG_DIR/claude.log"
CODEX_LOG="$LOG_DIR/codex.log"
REVIEW_FILE="$LOG_DIR/review.md"
MAX_ROUNDS=${1:-10}
# Create log directory
mkdir -p "$LOG_DIR"
# Colored output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
echo -e "${GREEN}=== AI auto-dev loop started ===${NC}"
echo "Project: $PROJECT_DIR"
echo "Max rounds: $MAX_ROUNDS"
echo ""
cd "$PROJECT_DIR"
for ((round=1; round<=MAX_ROUNDS; round++)); do
echo -e "${YELLOW}=== Round $round/$MAX_ROUNDS ===${NC}"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Round $round started" >> "$CLAUDE_LOG"
# Step 1: Claude Code implements
echo -e "${CYAN}[Step 1] Claude Code implementing...${NC}"
claude --dangerously-skip-permissions -p "
You are developing ○○. (describe what you're building here)
## Instructions
1. Check docs/todo.md for incomplete tasks
2. Pick the next incomplete task and implement it
3. After implementation, record in docs/progress.md
4. git commit the changes (concise message)
5. git push
6. Mark the task as complete in docs/todo.md
## Constraints
- Make decisions without asking questions
- Fix errors on your own
- Focus on one task per round
Start implementing.
" 2>&1 | tee -a "$CLAUDE_LOG"
# Step 2: Codex review
echo -e "${CYAN}[Step 2] Codex reviewing...${NC}"
codex review --commit HEAD 2>&1 | tee "$REVIEW_FILE" | tee -a "$CODEX_LOG"
# Step 3: Check review and fix if needed
if ! grep -qi "LGTM" "$REVIEW_FILE"; then
echo -e "${CYAN}[Step 3] Claude Code fixing...${NC}"
claude --dangerously-skip-permissions -p "
Received the following review from Codex.
Fix the issues and commit and push again.
$(cat "$REVIEW_FILE")
" 2>&1 | tee -a "$CLAUDE_LOG"
else
echo -e "${GREEN}[Step 3] Review OK - no fixes needed${NC}"
fi
# Step 4: Code Simplifier cleanup
echo -e "${CYAN}[Step 4] Running Code Simplifier...${NC}"
claude --dangerously-skip-permissions -p "
/plugin run code-simplifier
Clean up and simplify recently changed code.
Improve readability and maintainability without changing functionality.
Commit and push if there are changes.
" 2>&1 | tee -a "$CLAUDE_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Round $round complete" >> "$CLAUDE_LOG"
echo ""
# Check if all tasks are done
if ! grep -q "\- \[ \]" "$PROJECT_DIR/docs/todo.md" 2>/dev/null; then
echo -e "${GREEN}All tasks complete!${NC}"
break
fi
# Interval
sleep 3
done
echo -e "${GREEN}=== Loop finished ===${NC}"
If you copy-paste this, you’ll need:
claude plugin install code-simplifier
first. It works without it, though.
In Action
The tmux session running left/right split.

Claude implements and commits in the left pane; Codex reviews and sends feedback in the right pane.

When Codex outputs “LGTM,” Claude skips fixes and moves to the next task. When there are issues, Claude fixes first, then advances.
Results
Input Spec
Fed in the soba-eating game spec. A spec covering a two-gauge system, seating system, enemy system, etc.
Generated Code
- 1,134 lines of TypeScript
- Vite project that runs
- Playable in the browser
Completed Tasks
A portion of what was logged in docs/progress.md:
### Basic HTML Structure
- [x] Game screen layout implemented
- [x] Two-gauge UI (fullness + soba satisfaction)
- [x] Timer display
- [x] NEXT display area
- [x] Seat assignment display area
### Player Movement
- [x] Player state interface defined
- [x] Keyboard input handling (arrow keys + WASD)
- [x] 8-direction movement
### Food System
- [x] Food parameters configured (soba regular/large/extra, udon, ramen, napolitan)
- [x] Stage-based spawn rates
- [x] Counter placement and pickup
### Seating System
- [x] Stage-based table selection rules
- [x] Table assignment system
- [x] Eating system
### Enemy System
- [x] Stage-based enemy parameters
- [x] Player-enemy collision detection
- [x] Food dropped when carrying and hit
What Worked Well
File-Based History
Setting up CLAUDE.md to say “record progress in docs/progress.md” paid off. Even when Claude’s context gets compacted, it can re-read the file to catch up on what happened.
Work Unit Control
The prompt says “focus on one task per round,” but sometimes Claude groups multiple tasks into one phase. It’s unpredictable — just how it feels, I guess.
Bug Detection by Codex
Codex caught a bug where “the seat isn’t released when food is dropped,” and Claude fixed it. It was also recorded in progress.md:
### Bug Fixes
- [x] Fixed missing seat release when food is dropped
- Reset assigned seat state to 'available' in dropCarriedFood()
- Response to Codex review issue (P1)
Tips
Anything not in the spec gets filled in with whatever seems reasonable. To avoid that, you need a very detailed spec. Have Claude use AskUserQuestionTool in a separate window to ask everything exhaustively until there are no open questions.
The script caps at 10 rounds — adjust at your own discretion.
Pushing after every commit was intentional:
- Visibility: you can see what’s happening even from elsewhere
- External intervention:
--dangerously-skip-permissionsgives full access, so the agent could theoretically make unexpected external connections. If that happens, you can modify CLAUDE.md or TODO.md and push to GitHub from wherever you are. When the agent tries to push and encounters a conflict, it has to pull — and pulling loads the updated instructions. Since each round starts a new Claude, the updated instructions take effect on the next round.
This actually stopped an SFTP external connection attempt.
Claude Code + Codex auto-loop works. With a solid spec and task list, it runs itself. I tried leaving it overnight and it finished in under an hour.