fix(codex,review,ship): scope Codex review with --base — prompt-only review silently reviews the wrong diff - #2513
Open
fangearhq-boop wants to merge 2 commits into
Conversation
`/codex review`'s default path passed the diff scope as prompt text and no scope flag. That parses cleanly on Codex CLI 0.144.x, but a prompt-only `codex review` falls back to the *uncommitted working-tree* scope: it runs `git status --short; git diff` and reviews that. Prose in the prompt telling the model to "run git diff origin/<base>...HEAD" does not change what the CLI feeds the reviewer, so the skill returned confident, well-formatted reviews of the wrong diff, and reported "no changes" on a clean tree. Step 2A now runs `codex review --base <base>` with no prompt argument. Applied unconditionally with no version branch: `[PROMPT]` has always been optional, so the no-prompt form is valid on every version supporting `--base`. Also: scope the Filesystem Boundary section to the three `codex exec` paths (Review mode's default path no longer has a prompt to prefix), and document both the argv error and the silent-wrong-scope symptom in Error Handling. Two tests pinned the prompt-only shape as desired behavior. Both banned the substring `--base <base> -c '...'`, which the correct bare form also contains, so they could not tell a scoped call from an unscoped one. They now assert the real invariant for codex/ (no positional prompt before a scope flag) and keep review/, ship/, and scripts/resolvers/review.ts pinned, since those call sites still use the prompt workaround and still review the wrong diff. Refs garrytan#1428, garrytan#1479
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
The structured Codex review in scripts/resolvers/review.ts had the same silent-wrong-scope bug just fixed in codex/: it passed the diff range as prompt text with no scope flag, so the CLI fell back to reviewing the uncommitted working tree. That resolver feeds /review and /ship's adversarial pass, so both were reviewing the wrong changes with no error. Now `codex review --base <base>` with no prompt argument, matching codex/. The adversarial pass a few lines above is deliberately left alone: it uses `codex exec`, which is agentic and really does run the git command it's told to, so scoping it in prompt text is correct there. The new prose spells out that distinction so the two don't get "unified" later. Both tests that pinned the prompt-only shape now assert the real invariant across all five files: every `codex review` invocation carries a scope flag, and none puts a positional prompt in front of it. Prose mentions of the command and `codex exec` calls are excluded. Regenerated review/SKILL.md, ship/sections/adversarial.md, and the factory ship golden; the golden's only delta is this change. Refs garrytan#1428, garrytan#1479
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
/codex reviewhas been reviewing the wrong diffOn Codex CLI 0.144.x,
/codex reviewreturns a confident, well-formatted review of changes that are not the ones you asked about. There is no error, no warning, and no way to tell from the output that anything is wrong.The crash was the symptom. The fix for the crash was the bug.
Codex CLI 0.130.0 made the positional
[PROMPT]mutually exclusive with the scope flags, so the old invocation started failing loudly:That's #1428 and #1479. The resolution kept the prompt and dropped
--base, then moved the scope into prompt text:This parses cleanly, which is exactly why it survived. But
codex review --helpshows[PROMPT],--base,--commit, and--uncommittedare all scope selectors, and a prompt-onlycodex reviewfalls back to the uncommitted working-tree scope: it runsgit status --short; git diffand reviews that.Prompt text asking the model to "run git diff origin/main...HEAD" does not change what the CLI feeds the reviewer. The model gets the working-tree diff and reviews it, faithfully. So:
Neither case produces an error, so the failure is invisible. A review tool that silently reviews the wrong thing is worse than one that crashes: the crash told you to look.
The fix
Step 2A's default path now passes the scope flag and no prompt:
Applied unconditionally, with no
codex --versionbranch.[PROMPT]has always been optional, so the no-prompt form is valid on every version that supports--base— there is no version window this breaks.Three supporting changes:
codex execpaths. It claimed to govern Review mode's prompt argument, which no longer exists. Dropping it from the scoped path is acceptable:--basehands the model a pre-computed diff rather than turning it loose on the filesystem, so the rabbit-hole risk it guards against is much lower there.codex exec. They cannot ride along with--base(that's the rejected combination) and cannot be smuggled in by dropping--base(that's the silent wrong scope). The rationale now says so explicitly.--base" warning, since that is the trap — and the silent-wrong-scope symptom. The second entry matters precisely because it has no error message anyone could search for.Tests that pinned the bug
Two tests asserted the broken shape as desired behavior, including one named "codex review commands pass diff scope through prompt, not --base". Both worked by banning the substring:
The correct bare form contains that substring too, so the assertion could not distinguish a scoped call from an unscoped one — it just banned
--baseoutright. They now assert the real invariant forcodex/: no positional prompt may precede a scope flag. That's the same line-level checktest/codex-hardening.test.tsalready uses, and those existing guards pass unchanged.A stale comment in the boundary test instructed future contributors that the call "must be through
codex review "<prompt>"not barecodex review --base" — i.e. it told the next person to reintroduce this. Corrected; the assertion itself was already passing and is untouched./reviewand/shiphad it too (second commit)scripts/resolvers/review.ts:559carried the identical prompt-only invocation, and that resolver feeds both/reviewand/ship's structured Codex pass — so those were reviewing the wrong diff as well, with the same absence of any error. Fixed the same way, withreview/SKILL.md,ship/sections/adversarial.md, and the factory ship golden regenerated. The golden's only delta is this change.The adversarial pass a few lines above is deliberately left as-is. It uses
codex exec, which is agentic and genuinely runs the git command it's given, so scoping that one in prompt text is correct. The distinction is now written down next to both call sites, because the obvious "cleanup" is to unify them and that would reintroduce the bug on the wrong side.With everything on the scoped form, both tests now assert the real invariant across all five files instead of pinning one shape per file: every
codex reviewinvocation carries a scope flag, and none puts a positional prompt in front of it. Prose mentions andcodex execcalls are excluded.Verification
codex review --base HEAD~2 -c 'model_reasoning_effort="high"' --enable web_search_cachedon a real repo returned a genuine review including a[P2]finding — the marker format Step 4's pass/fail gate parses, so the gate still works.codex review --base HEAD~1correctly reported an empty diff.codex exec "<prompt>"still accepts a prompt, so Challenge (2B) and Consult (2C) are unaffected.codex review --helpon 0.144.1 confirms[PROMPT]is optional and the scope flags are what set scope.bun test): failure set is identical before and after — 840 failures on both, all pre-existing on this Windows checkout (gstack-slugexecutable-bit andgtimeoutresolution). Verified by running the suite onmainand diffing the failure lists, not by assertion.context-save-hardening.test.ts→ "50 .md files → only 20 returned"). It passed 3/3 in isolation and did not recur on the next full run; it touches nothing in this diff. Flagging it rather than quietly dropping it from the counts.bun run gen:skill-docsregeneratedcodex/SKILL.mdagainst currentmain; regeneration is idempotent.Tested on codex-cli 0.144.1.
Refs #1428, #1479