You asked Codex to “refactor the billing module.” It returns a PR touching 50 files and 1,500 lines. Every change is individually defensible, but nobody on your team will review 1,500 lines of agent-written code. The PR sits open for a week, conflicts with main, and gets closed with “let’s break this up.”
Fastest fix: before any non-trivial task, put Codex in Plan Mode (/plan, or Shift+Tab to cycle modes in the CLI and IDE extension as of June 2026). Plan Mode runs read-only, proposes a numbered plan, and waits — so you see the 50-file scope before a single line is written and can say “ship step 1 only.” For a PR that already exists, ask Codex to /review and split it, then cherry-pick into a chain of small PRs (Step 4).
Oversize PRs aren’t a model defect. They’re a scope problem: the task description authorized everything Codex touched. The durable fix bounds diff size at three layers — the prompt (one concern per task), AGENTS.md (a written per-PR budget), and CI (a gate that fails over-large diffs).
Which bucket are you in?
| Symptom in the diff | Likely cause | Go to |
|---|---|---|
| Sprawling edits, no single core change | Open-ended task (“refactor X”) | Cause 1, Steps 1-2 |
| Small real change + large “while I was here” halo | Opportunistic adjacent cleanup | Cause 2, Step 3 |
| 4+ unrelated bullets in the PR description | Mixed independent concerns | Cause 4, Steps 1, 4 |
| Small change + many identical call-site edits | A signature change that cascaded | Cause 6, Step 6 |
| It happens on every task, not just this one | No AGENTS.md size cap / no CI gate | Causes 3, 5, Steps 2, 5 |
Common causes
Ordered by hit rate, highest first.
1. The task was open-ended (“refactor X”)
“Refactor the billing module” / “improve performance” / “clean up the auth layer” have no natural stopping point. Codex keeps editing until it runs out of plausible improvements, not when it reaches a meaningful chunk.
How to spot it: Re-read your prompt. If it contains “refactor”, “improve”, “clean up”, or “modernize” without a scope boundary, you authorized infinity.
2. Codex did opportunistic adjacent cleanup
Original task: fix a typo in billing.ts. Codex finished in 4 lines, looked around, noticed inconsistent imports in the same directory, and “helpfully” normalized them across 30 files.
How to spot it: A small core change wrapped in a large halo of “while I was here” edits. Halo files don’t touch the original bug — they were changed for consistency.
3. AGENTS.md doesn’t cap diff size
Without an explicit “max diff per PR” rule, Codex treats every plausible edit as in-scope. Most teams have an implicit norm; Codex doesn’t read implicit. AGENTS.md is loaded automatically into context at session start (repo root, plus ~/.codex global and per-directory files, most specific winning), so a rule written there applies to every task.
How to spot it: grep -i "diff\|pr size\|scope" AGENTS.md returns nothing — no cap exists.
4. The refactor mixed independent concerns
The PR renames a function, adds tests, fixes 3 unrelated bugs found along the way, and updates docs. Each piece is small; together they’re a 1,500-line review burden.
How to spot it: The PR description (or commit list) has 4+ independent bullets. Each could be its own PR.
5. Codex didn’t propose a plan first
Asked for a big change directly in normal mode, Codex jumps straight to code. No plan means no chance for you to say “do steps 1-3, hold 4-7.”
How to spot it: Session history shows no plan or breakdown before the code — just “task” then “1,500 lines.”
6. A one-line fix cascaded by accident
A change to formatDate legitimately required updating 14 callers. Codex bundled it all into one PR instead of: PR1 = new formatDate signature, PR2..N = caller updates in batches.
How to spot it: A small “real” change plus many near-identical call-site updates that could have shipped as a chain.
Shortest path to fix
Ordered by ROI. Steps 1 and 2 prevent oversize PRs before they happen.
Step 1: Use Plan Mode to get a plan before code
Plan Mode is the built-in version of “show me the scope first.” Toggle it with /plan (or Shift+Tab to cycle Read Only → Plan → Auto). Codex explores read-only, asks clarifying questions, and returns a numbered plan you approve or edit before it writes anything. For a sprawling task, also paste explicit framing so the plan comes back as shippable units:
Before writing any code, propose a plan:
1. Numbered list of discrete steps, each shippable as its own PR.
2. For each step: target files (count + paths), estimated diff size, dependencies.
3. Identify the smallest step that delivers visible value — start there.
Wait for my approval before generating code.
You’ll see the 50-file plan up front and can reply “ship step 1 only.”
Step 2: Cap diff size in the prompt and AGENTS.md
The official prompt frame is Goal / Context / Constraints / Done when. Put the size limits under Constraints:
Constraints:
- Touch at most 5 files.
- Diff under 200 lines (excluding tests and generated code).
- Do not edit files outside the task scope, even for consistency.
- If the task can't fit, STOP and propose a split.
Done when: the named change is complete and the existing tests pass.
In AGENTS.md (a durable rule that survives across sessions and model upgrades):
## PR sizing
- Default cap: 200 lines added/removed per PR (excluding tests/generated).
- Hard cap: 500 lines; anything larger requires explicit approval.
- Adjacent "helpful cleanups" ship as separate PRs, never bundled.
- Refactors: propose a plan and ship as a chain of small PRs.
Keep AGENTS.md well under its project_doc_max_bytes limit (32 KiB as of June 2026); past that, Codex truncates the file and may silently drop your rules.
Step 3: Disable opportunistic edits
In the prompt:
Do NOT make any changes outside the immediate task:
- No "while I was here" import normalization.
- No formatting changes unless they're in the lines you're already editing.
- No renaming for consistency.
- If you see other issues, list them at the end as TODOs — don't fix them.
Step 4: Split an oversize PR with Codex’s help
If the PR already exists and you don’t want to discard the work, run /review first so Codex (and you) see the diff structure, then ask for a split:
This PR has 50 files. Split it into 4 logical PRs:
1. Read the diff: `gh pr diff <number>`.
2. Propose a 4-PR split based on independent concerns.
3. For each PR, list files + line count + commit message.
I'll cherry-pick into separate branches after.
Then in git:
# One branch per PR
git checkout main
git checkout -b pr1-rename-helpers
git cherry-pick <commits-for-pr1>
git checkout main
git checkout -b pr2-add-tests
git cherry-pick <commits-for-pr2>
# ...repeat per split
If the work lives in one squashed commit instead of clean per-concern commits, use git checkout main -- <paths> per branch to stage only the files for that PR, rather than cherry-picking.
Step 5: Add a CI check that fails over-large PRs
This gate uses git diff --numstat, which prints machine-readable added removed path rows — more robust than parsing the --shortstat summary:
# .github/workflows/pr-size.yml
name: PR size
on: pull_request
jobs:
size:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Check PR size
run: |
ADDED=$(git diff --numstat "origin/${{ github.base_ref }}...HEAD" -- \
':!**/*.snap' ':!**/__generated__/**' \
':!**/package-lock.json' ':!**/pnpm-lock.yaml' ':!**/yarn.lock' \
| awk '{ s += $1 } END { print s + 0 }')
echo "Reviewable lines added: $ADDED"
if [ "$ADDED" -gt 500 ]; then
echo "::error::PR adds ${ADDED} lines (max 500). Split it."
exit 1
fi
The pathspec excludes lockfiles, snapshots, and generated code, so only the actually-reviewable lines are gated. Bump 500 to match the hard cap in your AGENTS.md.
Step 6: For genuine large refactors, ship behind a feature flag
If the change really must be atomic (for example, migrating an API), stage it:
Step 1: Add the new code path alongside the old (small PR, flag off).
Step 2: Migrate callers in batches (small PRs, flag still off).
Step 3: Flip the flag (one-line PR).
Step 4: Remove the old code (small PR).
This turns one 1,500-line PR into four ~300-line PRs, each independently reviewable.
How to confirm it’s fixed
- The next non-trivial task returns a plan first, not a diff — Plan Mode (or your prompt) is taking hold.
git diff --numstat origin/main...HEAD | awk '{s+=$1} END {print s}'on the new branch is under your cap.- The
PR sizecheck shows green (or fails loudly on an oversize PR — both are correct). grep -A6 "PR sizing" AGENTS.mdshows your written cap, so the rule is actually in context.- A reviewer can read the whole diff in one sitting without “I’ll come back to this.”
Prevention
- Plan-first by default: turn on Plan Mode for any non-trivial task; approve the plan before code.
- Hard PR-size cap in
AGENTS.md(e.g., 200 default, 500 hard) — Codex follows what’s written. - Disable opportunistic adjacent edits in every prompt: “fix only the named issue.”
- CI gate on PR size so oversize PRs can’t reach review.
- For refactors, ship behind a feature flag as a chain of small PRs, not one atomic PR.
- When Codex proposes a 50-file plan, push back (“ship step 1 only”) instead of accepting the whole thing.
FAQ
Does Codex have a real “plan before code” mode, or do I have to prompt for it?
Both. Plan Mode is built in — toggle it with /plan or by cycling modes with Shift+Tab. It runs read-only and returns a plan you approve before any edits. The prompt in Step 1 is a supplement that forces the plan to come back as independently shippable PRs.
What’s a sane line cap for an agent PR?
Most teams land around a 200-line default and a 500-line hard cap (excluding tests, lockfiles, and generated code). The exact number matters less than writing it down in AGENTS.md so Codex and CI enforce the same limit.
Codex ignores my size limits even though they’re in AGENTS.md. Why?
Check three things: the file is at a path Codex loads (repo root, ~/.codex, or the working subdirectory), it’s under the 32 KiB project_doc_max_bytes limit so it isn’t truncated, and the limit lives under a clear Constraints/PR sizing heading rather than buried in prose. A CI gate (Step 5) enforces the cap even when the model drifts.
How do I split a PR when all the work is in one squashed commit?
Don’t cherry-pick — there’s nothing granular to pick. Branch off main per concern and stage only that concern’s files with git checkout main -- <paths> (or git restore --source=<branch> --staged --worktree <paths>), commit, and open each PR separately.
Should I just raise the cap so the big PR passes CI? No. The cap exists because review quality collapses past a few hundred lines. Split the work or, for a genuinely atomic change, stage it behind a feature flag (Step 6) so each PR stays reviewable.