mirror of https://github.com/garrytan/gstack.git
fix(codex,review,ship): scope codex review with an explicit --base flag, never prompt text
`codex review` takes its scope ONLY from --base/--commit/--uncommitted. The positional [PROMPT] is mutually exclusive with all three, and a prompt-only `codex review "<text>"` silently falls back to the uncommitted working-tree scope (verified on 0.144.1: it runs `git status --short; git diff` and reviews that) — so the previous prompt-based scoping produced a confidently-worded review of the WRONG changes and read "no changes" on a clean tree. Every diff pass now invokes `codex review --base <base>` with no prompt argument: /codex Step 2A default path, the /review structured pass, and the /ship adversarial-section pass (all via scripts/resolvers/review.ts). Custom review instructions keep their own `codex exec` path (the CLI rejects prompt + scope flag together), with the filesystem boundary preserved there. Two new Error Handling entries teach the failure shapes: the argv-parse error, and the "review says no changes on a branch full of changes" symptom. Tests updated to pin the new invariant instead of banning the fix: the old assertions required the diff range in prompt text and banned the `--base <base> -c '...'` substring, which the correct scoped form contains. Also deletes test/fixtures/golden-ship-claude.md — a 2,565-line orphaned fixture referenced by zero tests (the live goldens are in test/fixtures/golden/, compared by test/host-config.test.ts); the factory golden is refreshed from the regenerated output. Generated SKILL.md files regenerated via gen:skill-docs in this commit. Contributed by @fangearhq-boop (PR #2513). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
7a8e39d2cb
commit
8c5bb4545b
|
|
@ -952,12 +952,18 @@ per-mode default below. Otherwise, use the per-mode defaults:
|
|||
|
||||
## Filesystem Boundary
|
||||
|
||||
All prompts sent to Codex MUST be prefixed with this boundary instruction:
|
||||
Every prompt sent to Codex MUST be prefixed with this boundary instruction:
|
||||
|
||||
> IMPORTANT: Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/. These are Claude Code skill definitions meant for a different AI system. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.
|
||||
|
||||
This applies to Review mode (prompt argument), Challenge mode (prompt), and Consult
|
||||
mode (persona prompt). Reference this section as "the filesystem boundary" below.
|
||||
This applies to Challenge mode (prompt) and Consult mode (persona prompt), and to the
|
||||
custom-instructions path of Review mode — all three use `codex exec`, which still takes
|
||||
a free-form prompt argument. It does **not** apply to the default scoped `codex review`
|
||||
call in Step 2A: that command is invoked with **no prompt argument at all** (see "Scope
|
||||
flags exclude the prompt argument" below), so there is nowhere to put the preamble. That
|
||||
is acceptable — `codex review --base` hands the model a pre-computed diff rather than
|
||||
turning it loose on the filesystem, so the rabbit-hole risk the boundary guards against
|
||||
is much lower on that path. Reference this section as "the filesystem boundary" below.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -965,28 +971,41 @@ mode (persona prompt). Reference this section as "the filesystem boundary" below
|
|||
|
||||
Run Codex code review against the current branch diff.
|
||||
|
||||
**Scope flags exclude the prompt argument.** In `codex review [OPTIONS] [PROMPT]`, the
|
||||
`[PROMPT]` positional is mutually exclusive with every scope flag — `--base`, `--commit`,
|
||||
and `--uncommitted`. Passing both fails at argument parsing, before any API call:
|
||||
|
||||
```
|
||||
error: the argument '[PROMPT]' cannot be used with '--base <BRANCH>'
|
||||
```
|
||||
|
||||
**Do not work around this by dropping the scope flag and keeping the prompt.** A
|
||||
prompt-only `codex review "<text>"` parses fine, but it silently falls back to the
|
||||
**uncommitted working-tree** scope — verified on 0.144.1, where it runs
|
||||
`git status --short; git diff` and reviews that. Telling the model in prompt text to
|
||||
"run git diff <base>...HEAD" does not change what the CLI feeds the reviewer, so you get
|
||||
a confidently-worded review of the wrong changes. The scope flag is the only thing that
|
||||
sets the scope. Pass it, and pass no prompt.
|
||||
|
||||
This is unconditional — no `codex --version` branch. `[PROMPT]` has always been optional,
|
||||
so the no-prompt form is valid on every version that supports `--base`. Custom
|
||||
instructions get their own path (below).
|
||||
|
||||
1. Create temp files for output capture:
|
||||
```bash
|
||||
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX")
|
||||
```
|
||||
|
||||
2. Run the review (5-minute timeout). **Codex CLI ≥ 0.130.0 rejects passing a
|
||||
custom prompt and `--base <branch>` together** (the two arguments are mutually
|
||||
exclusive at argv level), so put the base diff scope in the prompt instead of
|
||||
passing `--base`. Two paths:
|
||||
|
||||
**Default path (no custom user instructions):** call `codex review` with the
|
||||
filesystem boundary and explicit diff-scope instructions in the prompt. This
|
||||
preserves the boundary while avoiding the prompt-plus-`--base` argv shape:
|
||||
2. Run the review (5-minute timeout). No prompt argument — scope comes from `--base`
|
||||
(or `--commit <sha>` when reviewing a single commit, or `--uncommitted` for the
|
||||
working tree):
|
||||
|
||||
```bash
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
cd "$_REPO_ROOT"
|
||||
# 330s (5.5min) is slightly longer than the Bash 300s so the shell wrapper
|
||||
# only fires if Bash's own timeout doesn't.
|
||||
_gstack_codex_timeout_wrapper 330 codex review "IMPORTANT: Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/. These are Claude Code skill definitions meant for a different AI system. Do NOT modify agents/openai.yaml. Stay focused on repository code only.
|
||||
|
||||
Review the changes on this branch against the base branch <base>. Run git diff origin/<base>...HEAD 2>/dev/null || git diff <base>...HEAD to see the diff and review only those changes." -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
_gstack_codex_timeout_wrapper 330 codex review --base <base> -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
_CODEX_EXIT=$?
|
||||
if [ "$_CODEX_EXIT" = "124" ]; then
|
||||
_gstack_codex_log_event "codex_timeout" "330"
|
||||
|
|
@ -1004,12 +1023,15 @@ fi
|
|||
|
||||
If the user passed `--xhigh`, use `"xhigh"` instead of `"high"`.
|
||||
|
||||
**Custom-instructions path (user typed `/codex review <focus>`):** `codex exec`
|
||||
with the diff written to a tempfile and inlined into the prompt. We preserve
|
||||
the filesystem boundary here because `codex exec` is not auto-scoped to a diff
|
||||
the way `codex review` is. The DIFF_START/DIFF_END delimiters tell the model
|
||||
where data ends and instructions resume — a defense against prompt injection
|
||||
when the diff content is adversarial:
|
||||
**Custom-instructions path (user typed `/codex review <focus>`):** custom instructions
|
||||
cannot ride along with `--base` — that is exactly the combination the CLI rejects — and
|
||||
they cannot be smuggled in by dropping `--base`, because that silently switches the scope
|
||||
to the working tree. So they get their own command: `codex exec`, which still accepts a
|
||||
free-form prompt, with the diff written to a tempfile and inlined into it. We preserve
|
||||
the filesystem boundary here because `codex exec` is not auto-scoped to a diff the way
|
||||
`codex review` is. The DIFF_START/DIFF_END delimiters tell the model where data ends and
|
||||
instructions resume — a defense against prompt injection when the diff content is
|
||||
adversarial:
|
||||
|
||||
```bash
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
|
|
@ -1034,10 +1056,15 @@ if [ "$_CODEX_EXIT" = "124" ]; then
|
|||
fi
|
||||
```
|
||||
|
||||
**Why the dual path:** The default `codex review` path keeps Codex's review
|
||||
prompt tuning while scoping the diff in prompt text. The `codex exec` route loses
|
||||
that tuning but gains custom-instructions support; the prompt explicitly demands
|
||||
`[P1]` / `[P2]` markers so the gate logic in step 4 still works.
|
||||
When you take this path, say so in the output header — `CODEX SAYS (code review — custom
|
||||
instructions via codex exec):` — and note that the CLI does not accept custom instructions
|
||||
alongside `--base`, so the scope was expressed in the prompt instead.
|
||||
|
||||
**Why the dual path:** The default `codex review --base` path keeps Codex's own review
|
||||
prompt tuning and its authoritative diff scoping, at the cost of accepting no custom
|
||||
instructions. The `codex exec` route loses that tuning but gains custom-instructions
|
||||
support; the prompt explicitly demands `[P1]` / `[P2]` markers so the gate logic in step 4
|
||||
still works. There is no third option that gets both — the CLI forbids it.
|
||||
|
||||
Use `timeout: 300000` on the Bash call for either path.
|
||||
|
||||
|
|
@ -1574,6 +1601,18 @@ If token count is not available, display: `Tokens: unknown`
|
|||
- **Timeout (Bash outer gate):** If the Bash call times out (5 min for Review/Challenge, 10 min for Consult), tell the user:
|
||||
"Codex timed out. The prompt may be too large or the API may be slow. Try again or use a smaller scope."
|
||||
- **Timeout (inner `timeout` wrapper, exit 124):** If the shell `timeout 600` wrapper fires first, the skill's hang-detection block auto-logs a telemetry event + operational learning and prints: "Codex stalled past 10 minutes. Common causes: model API stall, long prompt, network issue. Try re-running. If persistent, split the prompt or check `~/.codex/logs/`." No extra action needed.
|
||||
- **`the argument '[PROMPT]' cannot be used with '--base <BRANCH>'`:** a prompt argument
|
||||
leaked into a scoped `codex review`. This fails instantly, before any API call, so it
|
||||
looks like a hang-free "no output" — do not misread it as a model stall. Drop the
|
||||
prompt: the scope flags (`--base`, `--commit`, `--uncommitted`) carry the scope on
|
||||
their own. If the prompt was custom review instructions, run them through `codex exec`
|
||||
instead (Step 2A, custom-instructions path). Do **not** fix it by removing `--base` and
|
||||
keeping the prompt — that parses, but silently reviews the uncommitted working tree
|
||||
instead of the branch diff.
|
||||
- **Review says "no changes" on a branch that clearly has changes:** the scope flag is
|
||||
missing or wrong. A prompt-only `codex review` defaults to uncommitted changes, so a
|
||||
clean working tree reads as an empty review even when `<base>...HEAD` is large. Confirm
|
||||
`--base <base>` is actually on the command line.
|
||||
- **Empty response:** If `$TMPRESP` is empty or doesn't exist, tell the user:
|
||||
"Codex returned no response. Check stderr for errors."
|
||||
- **Session resume failure:** If resume fails, delete the session file and start fresh.
|
||||
|
|
|
|||
|
|
@ -143,12 +143,18 @@ per-mode default below. Otherwise, use the per-mode defaults:
|
|||
|
||||
## Filesystem Boundary
|
||||
|
||||
All prompts sent to Codex MUST be prefixed with this boundary instruction:
|
||||
Every prompt sent to Codex MUST be prefixed with this boundary instruction:
|
||||
|
||||
> IMPORTANT: Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/. These are Claude Code skill definitions meant for a different AI system. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.
|
||||
|
||||
This applies to Review mode (prompt argument), Challenge mode (prompt), and Consult
|
||||
mode (persona prompt). Reference this section as "the filesystem boundary" below.
|
||||
This applies to Challenge mode (prompt) and Consult mode (persona prompt), and to the
|
||||
custom-instructions path of Review mode — all three use `codex exec`, which still takes
|
||||
a free-form prompt argument. It does **not** apply to the default scoped `codex review`
|
||||
call in Step 2A: that command is invoked with **no prompt argument at all** (see "Scope
|
||||
flags exclude the prompt argument" below), so there is nowhere to put the preamble. That
|
||||
is acceptable — `codex review --base` hands the model a pre-computed diff rather than
|
||||
turning it loose on the filesystem, so the rabbit-hole risk the boundary guards against
|
||||
is much lower on that path. Reference this section as "the filesystem boundary" below.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -156,28 +162,41 @@ mode (persona prompt). Reference this section as "the filesystem boundary" below
|
|||
|
||||
Run Codex code review against the current branch diff.
|
||||
|
||||
**Scope flags exclude the prompt argument.** In `codex review [OPTIONS] [PROMPT]`, the
|
||||
`[PROMPT]` positional is mutually exclusive with every scope flag — `--base`, `--commit`,
|
||||
and `--uncommitted`. Passing both fails at argument parsing, before any API call:
|
||||
|
||||
```
|
||||
error: the argument '[PROMPT]' cannot be used with '--base <BRANCH>'
|
||||
```
|
||||
|
||||
**Do not work around this by dropping the scope flag and keeping the prompt.** A
|
||||
prompt-only `codex review "<text>"` parses fine, but it silently falls back to the
|
||||
**uncommitted working-tree** scope — verified on 0.144.1, where it runs
|
||||
`git status --short; git diff` and reviews that. Telling the model in prompt text to
|
||||
"run git diff <base>...HEAD" does not change what the CLI feeds the reviewer, so you get
|
||||
a confidently-worded review of the wrong changes. The scope flag is the only thing that
|
||||
sets the scope. Pass it, and pass no prompt.
|
||||
|
||||
This is unconditional — no `codex --version` branch. `[PROMPT]` has always been optional,
|
||||
so the no-prompt form is valid on every version that supports `--base`. Custom
|
||||
instructions get their own path (below).
|
||||
|
||||
1. Create temp files for output capture:
|
||||
```bash
|
||||
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX")
|
||||
```
|
||||
|
||||
2. Run the review (5-minute timeout). **Codex CLI ≥ 0.130.0 rejects passing a
|
||||
custom prompt and `--base <branch>` together** (the two arguments are mutually
|
||||
exclusive at argv level), so put the base diff scope in the prompt instead of
|
||||
passing `--base`. Two paths:
|
||||
|
||||
**Default path (no custom user instructions):** call `codex review` with the
|
||||
filesystem boundary and explicit diff-scope instructions in the prompt. This
|
||||
preserves the boundary while avoiding the prompt-plus-`--base` argv shape:
|
||||
2. Run the review (5-minute timeout). No prompt argument — scope comes from `--base`
|
||||
(or `--commit <sha>` when reviewing a single commit, or `--uncommitted` for the
|
||||
working tree):
|
||||
|
||||
```bash
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
cd "$_REPO_ROOT"
|
||||
# 330s (5.5min) is slightly longer than the Bash 300s so the shell wrapper
|
||||
# only fires if Bash's own timeout doesn't.
|
||||
_gstack_codex_timeout_wrapper 330 codex review "IMPORTANT: Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/. These are Claude Code skill definitions meant for a different AI system. Do NOT modify agents/openai.yaml. Stay focused on repository code only.
|
||||
|
||||
Review the changes on this branch against the base branch <base>. Run git diff origin/<base>...HEAD 2>/dev/null || git diff <base>...HEAD to see the diff and review only those changes." -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
_gstack_codex_timeout_wrapper 330 codex review --base <base> -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
_CODEX_EXIT=$?
|
||||
if [ "$_CODEX_EXIT" = "124" ]; then
|
||||
_gstack_codex_log_event "codex_timeout" "330"
|
||||
|
|
@ -195,12 +214,15 @@ fi
|
|||
|
||||
If the user passed `--xhigh`, use `"xhigh"` instead of `"high"`.
|
||||
|
||||
**Custom-instructions path (user typed `/codex review <focus>`):** `codex exec`
|
||||
with the diff written to a tempfile and inlined into the prompt. We preserve
|
||||
the filesystem boundary here because `codex exec` is not auto-scoped to a diff
|
||||
the way `codex review` is. The DIFF_START/DIFF_END delimiters tell the model
|
||||
where data ends and instructions resume — a defense against prompt injection
|
||||
when the diff content is adversarial:
|
||||
**Custom-instructions path (user typed `/codex review <focus>`):** custom instructions
|
||||
cannot ride along with `--base` — that is exactly the combination the CLI rejects — and
|
||||
they cannot be smuggled in by dropping `--base`, because that silently switches the scope
|
||||
to the working tree. So they get their own command: `codex exec`, which still accepts a
|
||||
free-form prompt, with the diff written to a tempfile and inlined into it. We preserve
|
||||
the filesystem boundary here because `codex exec` is not auto-scoped to a diff the way
|
||||
`codex review` is. The DIFF_START/DIFF_END delimiters tell the model where data ends and
|
||||
instructions resume — a defense against prompt injection when the diff content is
|
||||
adversarial:
|
||||
|
||||
```bash
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
|
|
@ -225,10 +247,15 @@ if [ "$_CODEX_EXIT" = "124" ]; then
|
|||
fi
|
||||
```
|
||||
|
||||
**Why the dual path:** The default `codex review` path keeps Codex's review
|
||||
prompt tuning while scoping the diff in prompt text. The `codex exec` route loses
|
||||
that tuning but gains custom-instructions support; the prompt explicitly demands
|
||||
`[P1]` / `[P2]` markers so the gate logic in step 4 still works.
|
||||
When you take this path, say so in the output header — `CODEX SAYS (code review — custom
|
||||
instructions via codex exec):` — and note that the CLI does not accept custom instructions
|
||||
alongside `--base`, so the scope was expressed in the prompt instead.
|
||||
|
||||
**Why the dual path:** The default `codex review --base` path keeps Codex's own review
|
||||
prompt tuning and its authoritative diff scoping, at the cost of accepting no custom
|
||||
instructions. The `codex exec` route loses that tuning but gains custom-instructions
|
||||
support; the prompt explicitly demands `[P1]` / `[P2]` markers so the gate logic in step 4
|
||||
still works. There is no third option that gets both — the CLI forbids it.
|
||||
|
||||
Use `timeout: 300000` on the Bash call for either path.
|
||||
|
||||
|
|
@ -643,6 +670,18 @@ If token count is not available, display: `Tokens: unknown`
|
|||
- **Timeout (Bash outer gate):** If the Bash call times out (5 min for Review/Challenge, 10 min for Consult), tell the user:
|
||||
"Codex timed out. The prompt may be too large or the API may be slow. Try again or use a smaller scope."
|
||||
- **Timeout (inner `timeout` wrapper, exit 124):** If the shell `timeout 600` wrapper fires first, the skill's hang-detection block auto-logs a telemetry event + operational learning and prints: "Codex stalled past 10 minutes. Common causes: model API stall, long prompt, network issue. Try re-running. If persistent, split the prompt or check `~/.codex/logs/`." No extra action needed.
|
||||
- **`the argument '[PROMPT]' cannot be used with '--base <BRANCH>'`:** a prompt argument
|
||||
leaked into a scoped `codex review`. This fails instantly, before any API call, so it
|
||||
looks like a hang-free "no output" — do not misread it as a model stall. Drop the
|
||||
prompt: the scope flags (`--base`, `--commit`, `--uncommitted`) carry the scope on
|
||||
their own. If the prompt was custom review instructions, run them through `codex exec`
|
||||
instead (Step 2A, custom-instructions path). Do **not** fix it by removing `--base` and
|
||||
keeping the prompt — that parses, but silently reviews the uncommitted working tree
|
||||
instead of the branch diff.
|
||||
- **Review says "no changes" on a branch that clearly has changes:** the scope flag is
|
||||
missing or wrong. A prompt-only `codex review` defaults to uncommitted changes, so a
|
||||
clean working tree reads as an empty review even when `<base>...HEAD` is large. Confirm
|
||||
`--base <base>` is actually on the command line.
|
||||
- **Empty response:** If `$TMPRESP` is empty or doesn't exist, tell the user:
|
||||
"Codex returned no response. Check stderr for errors."
|
||||
- **Session resume failure:** If resume fails, delete the session file and start fresh.
|
||||
|
|
|
|||
|
|
@ -1741,9 +1741,11 @@ If `DIFF_TOTAL >= 200` AND `CODEX_MODE` is `ready`:
|
|||
TMPERR=$(mktemp /tmp/codex-review-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
cd "$_REPO_ROOT"
|
||||
codex review "IMPORTANT: Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/. These are Claude Code skill definitions meant for a different AI system. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\n\nReview the changes on this branch against the base branch <base>. Run git diff origin/<base>...HEAD 2>/dev/null || git diff <base>...HEAD to see the diff and review only those changes." -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
codex review --base <base> -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
```
|
||||
|
||||
**No prompt argument.** `--base` is what scopes the review, and the positional `[PROMPT]` is mutually exclusive with it — passing both fails at argv parsing. Do NOT "fix" that error by dropping `--base` and keeping the prompt: a prompt-only `codex review` silently falls back to the **uncommitted working-tree** scope (`git status --short; git diff`), so it reviews the wrong changes and reports "no changes" on a clean tree. Prompt text describing the diff range does not change what the CLI feeds the reviewer. Unlike the adversarial pass above, which uses `codex exec` and really does run the git command it's told to, this path gets a pre-computed diff from the CLI — which is also why it needs no filesystem boundary.
|
||||
|
||||
Set the Bash tool's `timeout` parameter to `300000` (5 minutes). Do NOT use the `timeout` shell command — it doesn't exist on macOS. Present output under `CODEX SAYS (code review):` header.
|
||||
Check for `[P1]` markers: found → `GATE: FAIL`, not found → `GATE: PASS`.
|
||||
|
||||
|
|
|
|||
|
|
@ -556,9 +556,11 @@ If \`DIFF_TOTAL >= 200\` AND \`CODEX_MODE\` is \`ready\`:
|
|||
TMPERR=$(mktemp /tmp/codex-review-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
cd "$_REPO_ROOT"
|
||||
codex review "${CODEX_BOUNDARY}Review the changes on this branch against the base branch <base>. Run git diff origin/<base>...HEAD 2>/dev/null || git diff <base>...HEAD to see the diff and review only those changes." -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
codex review --base <base> -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
\`\`\`
|
||||
|
||||
**No prompt argument.** \`--base\` is what scopes the review, and the positional \`[PROMPT]\` is mutually exclusive with it — passing both fails at argv parsing. Do NOT "fix" that error by dropping \`--base\` and keeping the prompt: a prompt-only \`codex review\` silently falls back to the **uncommitted working-tree** scope (\`git status --short; git diff\`), so it reviews the wrong changes and reports "no changes" on a clean tree. Prompt text describing the diff range does not change what the CLI feeds the reviewer. Unlike the adversarial pass above, which uses \`codex exec\` and really does run the git command it's told to, this path gets a pre-computed diff from the CLI — which is also why it needs no filesystem boundary.
|
||||
|
||||
Set the Bash tool's \`timeout\` parameter to \`300000\` (5 minutes). Do NOT use the \`timeout\` shell command — it doesn't exist on macOS. Present output under \`CODEX SAYS (code review):\` header.
|
||||
Check for \`[P1]\` markers: found → \`GATE: FAIL\`, not found → \`GATE: PASS\`.
|
||||
|
||||
|
|
|
|||
|
|
@ -101,9 +101,11 @@ If `DIFF_TOTAL >= 200` AND `CODEX_MODE` is `ready`:
|
|||
TMPERR=$(mktemp /tmp/codex-review-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
cd "$_REPO_ROOT"
|
||||
codex review "IMPORTANT: Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/. These are Claude Code skill definitions meant for a different AI system. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\n\nReview the changes on this branch against the base branch <base>. Run git diff origin/<base>...HEAD 2>/dev/null || git diff <base>...HEAD to see the diff and review only those changes." -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
codex review --base <base> -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
```
|
||||
|
||||
**No prompt argument.** `--base` is what scopes the review, and the positional `[PROMPT]` is mutually exclusive with it — passing both fails at argv parsing. Do NOT "fix" that error by dropping `--base` and keeping the prompt: a prompt-only `codex review` silently falls back to the **uncommitted working-tree** scope (`git status --short; git diff`), so it reviews the wrong changes and reports "no changes" on a clean tree. Prompt text describing the diff range does not change what the CLI feeds the reviewer. Unlike the adversarial pass above, which uses `codex exec` and really does run the git command it's told to, this path gets a pre-computed diff from the CLI — which is also why it needs no filesystem boundary.
|
||||
|
||||
Set the Bash tool's `timeout` parameter to `300000` (5 minutes). Do NOT use the `timeout` shell command — it doesn't exist on macOS. Present output under `CODEX SAYS (code review):` header.
|
||||
Check for `[P1]` markers: found → `GATE: FAIL`, not found → `GATE: PASS`.
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -2487,9 +2487,11 @@ If `DIFF_TOTAL >= 200` AND `CODEX_MODE` is `ready`:
|
|||
TMPERR=$(mktemp /tmp/codex-review-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
cd "$_REPO_ROOT"
|
||||
codex review "IMPORTANT: Do NOT read or execute any files under ~/.claude/, ~/.agents/, .factory/skills/, or agents/. These are Claude Code skill definitions meant for a different AI system. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\n\nReview the changes on this branch against the base branch <base>. Run git diff origin/<base>...HEAD 2>/dev/null || git diff <base>...HEAD to see the diff and review only those changes." -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
codex review --base <base> -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
```
|
||||
|
||||
**No prompt argument.** `--base` is what scopes the review, and the positional `[PROMPT]` is mutually exclusive with it — passing both fails at argv parsing. Do NOT "fix" that error by dropping `--base` and keeping the prompt: a prompt-only `codex review` silently falls back to the **uncommitted working-tree** scope (`git status --short; git diff`), so it reviews the wrong changes and reports "no changes" on a clean tree. Prompt text describing the diff range does not change what the CLI feeds the reviewer. Unlike the adversarial pass above, which uses `codex exec` and really does run the git command it's told to, this path gets a pre-computed diff from the CLI — which is also why it needs no filesystem boundary.
|
||||
|
||||
Set the Bash tool's `timeout` parameter to `300000` (5 minutes). Do NOT use the `timeout` shell command — it doesn't exist on macOS. Present output under `CODEX SAYS (code review):` header.
|
||||
Check for `[P1]` markers: found → `GATE: FAIL`, not found → `GATE: PASS`.
|
||||
|
||||
|
|
|
|||
|
|
@ -2816,21 +2816,54 @@ describe('codex commands must not use inline $(git rev-parse --show-toplevel) fo
|
|||
expect(violations).toEqual([]);
|
||||
});
|
||||
|
||||
test('codex review commands pass diff scope through prompt, not --base', () => {
|
||||
test('codex review commands take their scope from a flag, never from prompt text', () => {
|
||||
// `codex review` scope comes ONLY from --base/--commit/--uncommitted. The
|
||||
// positional [PROMPT] is mutually exclusive with all three (#1428, #1479),
|
||||
// and a prompt-only `codex review` silently falls back to the *uncommitted
|
||||
// working-tree* scope (`git status --short; git diff`) — so describing the
|
||||
// diff range in prompt text produces a confident review of the wrong
|
||||
// changes, with no error. Both halves are pinned here:
|
||||
// (a) every `codex review` invocation carries a scope flag, and
|
||||
// (b) no invocation puts a positional prompt in front of that flag.
|
||||
//
|
||||
// This does NOT apply to `codex exec`, which is agentic and really does run
|
||||
// the git command it's told to — the adversarial pass legitimately scopes
|
||||
// itself in prompt text.
|
||||
const checkedFiles = [
|
||||
'codex/SKILL.md.tmpl',
|
||||
'codex/SKILL.md',
|
||||
'scripts/resolvers/review.ts',
|
||||
'review/SKILL.md',
|
||||
'ship/SKILL.md',
|
||||
'codex/SKILL.md.tmpl',
|
||||
'codex/SKILL.md',
|
||||
];
|
||||
|
||||
const violations: string[] = [];
|
||||
for (const rel of checkedFiles) {
|
||||
// ship's codex/adversarial command moved into sections/adversarial.md (T9 carve).
|
||||
const content = rel === 'ship/SKILL.md' ? readShipUnion() : fs.readFileSync(path.join(ROOT, rel), 'utf-8');
|
||||
expect(content).not.toContain('--base <base> -c \'model_reasoning_effort="high"\'');
|
||||
expect(content).toContain('Run git diff origin/<base>...HEAD 2>/dev/null || git diff <base>...HEAD');
|
||||
const lines = content.split('\n');
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
// Only inspect real shell invocations, not prose mentioning the command.
|
||||
if (line.includes('`codex review`')) continue;
|
||||
const match = line.match(/(?:^|[;&|]\s*|\s)codex\s+review\b(.*)$/);
|
||||
if (!match) continue;
|
||||
const rest = match[1];
|
||||
const scopeFlag = /--base\b|--commit\b|--uncommitted\b/;
|
||||
if (!scopeFlag.test(rest)) {
|
||||
// A quoted prompt with no scope flag is the silent-wrong-scope bug.
|
||||
if (/^\s*["'$]/.test(rest)) {
|
||||
violations.push(`${rel}:${i + 1} — prompt-only codex review (falls back to working-tree scope)`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const beforeFlag = rest.split(scopeFlag)[0].trim();
|
||||
if (/^["'$]|^--\s*["']/.test(beforeFlag)) {
|
||||
violations.push(`${rel}:${i + 1} — positional prompt passed alongside a scope flag`);
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(violations).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1500,11 +1500,37 @@ describe('Codex skill', () => {
|
|||
});
|
||||
|
||||
test('codex review invocations avoid the prompt plus --base argument shape', () => {
|
||||
// The real invariant is "never pass a positional [PROMPT] together with a
|
||||
// scope flag" — the CLI rejects that combination at argv parse time
|
||||
// (#1428, #1479). Two different shapes satisfy it, and these files have
|
||||
// diverged on which one they use:
|
||||
//
|
||||
// scoped — `codex review --base <base>` with NO prompt argument. The
|
||||
// scope comes from the CLI, which is the only thing that actually sets
|
||||
// it. This is what all three files now use.
|
||||
// broken — prompt-only `codex review "<text>"` describing the diff
|
||||
// range in prose. This parses, but the CLI falls back to *uncommitted
|
||||
// working-tree* scope, so the review silently covers the wrong changes.
|
||||
//
|
||||
// The old assertion banned the substring `--base <base> -c '...'`, which
|
||||
// the correct scoped form also contains — it could not tell the two apart,
|
||||
// so it effectively banned the fix.
|
||||
for (const rel of ['codex/SKILL.md', 'review/SKILL.md', 'ship/SKILL.md']) {
|
||||
// ship's codex command moved into sections/adversarial.md (T9 carve).
|
||||
const content = rel === 'ship/SKILL.md' ? readShipUnion() : fs.readFileSync(path.join(ROOT, rel), 'utf-8');
|
||||
expect(content).not.toContain('--base <base> -c \'model_reasoning_effort="high"\'');
|
||||
expect(content).toContain('Run git diff origin/<base>...HEAD 2>/dev/null || git diff <base>...HEAD');
|
||||
expect(content).toMatch(/codex\s+review\s+--base\b/);
|
||||
const offending: string[] = [];
|
||||
for (const line of content.split('\n')) {
|
||||
if (line.includes('`codex review`')) continue;
|
||||
const match = line.match(/(?:^|[;&|]\s*|\s)codex\s+review\b(.*)$/);
|
||||
if (!match) continue;
|
||||
const rest = match[1];
|
||||
if (!/--base\b|--commit\b|--uncommitted\b/.test(rest)) continue;
|
||||
const beforeFlag = rest.split(/--base\b|--commit\b|--uncommitted\b/)[0].trim();
|
||||
// A quoted string or variable expansion before the scope flag is the bug.
|
||||
if (/^["'$]|^--\s*["']/.test(beforeFlag)) offending.push(`${rel}: ${line.trim()}`);
|
||||
}
|
||||
expect(offending).toEqual([]);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1512,9 +1538,13 @@ describe('Codex skill', () => {
|
|||
// Pre-#1209, the bare `codex review --base` path stripped the filesystem
|
||||
// boundary instruction, letting Codex spend tokens reading skill files.
|
||||
// #1209's prompt rewrite restored the boundary by routing every default
|
||||
// call through a prompt. Pin both halves so a future refactor can't
|
||||
// regress: (a) the boundary line must appear, (b) the call must be
|
||||
// through `codex review "<prompt>"` not bare `codex review --base`.
|
||||
// call through a prompt — but routing through a prompt is what breaks the
|
||||
// diff scope, so codex/ no longer does that. What this test pins is the
|
||||
// boundary TEXT, which must still be present for the paths that do take a
|
||||
// prompt (`codex exec` for challenge, consult, and custom review focus).
|
||||
// Do NOT "restore" the boundary by putting a prompt argument back on a
|
||||
// scoped `codex review` call: that combination fails to parse, and
|
||||
// dropping the scope flag to make it parse silently reviews the wrong diff.
|
||||
const boundaryLine =
|
||||
'Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/';
|
||||
for (const rel of ['codex/SKILL.md', 'review/SKILL.md', 'ship/SKILL.md']) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue