Letting Claude Code and Codex Run Overnight in tmux (Optimization)
Recap
In the setup article I described the idea of linking Claude Code and Codex, and in the practice article I actually ran it. The result was 1,134 lines of game code.
Hitting Rate Limits
Running the practice script on a different project immediately hit the Claude Code MAX plan (x20, the highest tier) cap. Switching to Codex as the primary then hit the Codex Pro plan (also the highest tier) cap.
Thinking about why: the overhead from tmux command-chaining every round was too high.
The underlying issue:
- Codex (5.2) is smart enough and capable of managing its own context
- Claude Code’s context overflows fast, but it doesn’t completely forget the immediate past
So the approach became:
- Keep the same Claude session for a single task
- Close Claude after push completes → next task starts fresh from the instruction file
- Don’t restart Codex every time → avoid full file scans each round
This should reduce both limits. That hypothesis drove the improvements.
Redesigning the Roles
Previous (Practice Article)
Equal loop structure. Claude and Codex take turns.
Claude (implement) → Codex (review) → Claude (fix) → ...
Improved Version
Two-tier structure where Codex manages Claude.
Codex (meta layer): instructions, reviews, progress management
↓ via files
Claude (worker layer): implement, commit, push
All communication goes through files:
| File | Purpose |
|---|---|
work/task.md | Codex → Claude task instructions |
work/review.md | Codex → Claude review feedback |
logs/.claude_done | Claude → Codex completion signal |
Reducing API Calls: Blocking Wait
The Original Problem
while [ ! -f "$REVIEW_SIGNAL" ]; do
sleep 2
done
Checks for a file every 2 seconds. The loop itself doesn’t call the API, but when Codex runs in agent mode, it may still make periodic API calls during the wait.
Improvement: Blocking Wait
timeout 3600 bash -c "while [ ! -f logs/.claude_done ]; do sleep 10; done"
Add this to AGENTS.md: “Do not make any additional API requests until this command completes.” Codex follows the instruction and does nothing while waiting.
### 6. Blocking Wait
**Important**: Do not make any additional API requests until this command completes.
\`\`\`bash
timeout 3600 bash -c "while [ ! -f logs/.claude_done ]; do sleep 10; done"
\`\`\`
Timeout is 1 hour. Prevents infinite loops.
Reducing Context Size
1. Explicitly List Files That Must Not Be Read
AGENTS.md (for Codex):
## Prohibited Actions
- Do not read log files inside `logs/`
- Do not read `docs/progress-archive/`
- Do not read large amounts of past commit history
- Do not have Claude read unnecessary files (logs, history)
CLAUDE.md (for Claude):
### Files That Must Not Be Read
To conserve context, do not read:
- Files inside `logs/` (except signals)
- `docs/progress-archive/`
- Past files in `work/` (other than the current task)
LLMs are fairly obedient when told “don’t do this.”
2. Force-Delete Temp Files
Always clean up after a task:
# Remove signals and temp files (required for context efficiency)
rm -f logs/.claude_done
rm -f work/task.md work/review.md work/progress.md
Adding a comment explaining why prevents LLMs from skipping it.
3. Session Isolation
Restart Claude for each task:
# Reset environment
rm -f logs/.claude_done
tmux kill-window -t codex-dev:claude-worker 2>/dev/null || true
# Launch Claude (new window)
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
This prevents context from carrying over from the previous task. Each new Claude starts fresh.
Task Execution Flow
9 steps:
1. Select task (from todo.md)
2. Reset environment (delete signals, kill Claude window)
3. Launch Claude (new tmux window)
4. Write task instructions (work/task.md)
5. Send instructions
6. Blocking wait (no API calls)
7. Review (up to 3 fix cycles)
8. Completion processing (delete temp files, update todo.md)
9. Move to next task
The key steps are 2, 6, and 8.
- Step 2: Clear leftovers from the previous task
- Step 6: No API calls during the wait
- Step 8: Don’t carry unnecessary context into the next task
Comparison: Before vs. After
| Aspect | Previous (Practice) | Improved |
|---|---|---|
| Wait method | sleep 2 polling | Signal file + blocking wait |
| Context | Accumulates | Reset per task |
| Role structure | Equal loop | Codex-led two-tier |
| Prohibited actions | None | Explicitly list logs/archive |
Launch Script
The improved version launches with one line:
codex --sandbox danger-full-access --ask-for-approval never
Codex starts in agent mode and auto-loads AGENTS.md.
That said, just reading AGENTS.md isn’t enough — it will pause. The first time, explicitly tell it “read AGENTS.md and run it.” After that, it runs on its own.
tmux session creation is handled by a script:
#!/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
Writing Instructions for LLMs
Lessons from practice.
”Don’t do X” works; “do X” gets skipped
LLMs are fairly obedient with prohibitions. But “do this” instructions can get skipped.
Countermeasures:
- Use strong language for important instructions (“required,” “always,” “every time”)
- Keep instruction files short so nothing falls off the end of context
- Repeat the same instruction in multiple places (research confirms repetition improves LLM accuracy)
# Bad: likely to be skipped
After finishing, create a signal file.
# Good: emphasized + reason given
**After finishing, always run `touch logs/.claude_done`** (required to notify Codex of completion)
Keep instruction files short
When AGENTS.md or CLAUDE.md gets long, instructions near the end drop out of context.
- Limit to the bare minimum
- Move details to separate files and write “read only when needed”
- Put the most critical instructions at the top of the file
Key points for keeping costs down:
- Blocking wait reduces API calls: explicitly say “do nothing while waiting”
- List files that must not be read: ban logs and archives
- Reset session per task: prevent context accumulation
- Instruction phrasing: use strong language for “do”; keep files short