mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-14 08:59:01 +02:00
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:
co-authored by
Claude Fable 5
parent
7a8e39d2cb
commit
8c5bb4545b
@@ -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([]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user