Skip to content

fix(review): give the reviewer the diff instead of expecting it to find one - #375

Merged
unohee merged 1 commit into
mainfrom
fix/reviewer-gets-the-diff
Aug 1, 2026
Merged

fix(review): give the reviewer the diff instead of expecting it to find one#375
unohee merged 1 commit into
mainfrom
fix/reviewer-gets-the-diff

Conversation

@unohee

@unohee unohee commented Aug 1, 2026

Copy link
Copy Markdown
Owner

The gate's own output caught this. A CI run returned revise whose feedback opened with:

I cannot determine the actual diff between the current state and commit 47b9837… because the .git directory is not accessible from the working tree

…and then delivered a verdict anyway, based on files read in isolation.

Why

buildReviewerPrompt gave direct mode only a list of changed filenames. Its own comment says direct mode "reviews a Git diff supplied by a user/CI checkout" — but no diff was ever supplied.

In working-tree mode an agent can recover the rest with bash. Under --read-only it has no bash, and in --base mode there is nothing in the working tree to recover: reading a file shows the result of the change, never the change.

--read-only --base is exactly what the GitHub Action passes. The flagship CI path was reviewing blind and still emitting confident verdicts.

What

The diff travels in the prompt now. Bounded at 200KB and says so when it truncates — a silently partial diff invites a verdict on a change the reviewer only partly saw. A git failure degrades to the old file-list behaviour rather than ending the review, and omits the section rather than rendering an empty one, since a heading with nothing under it reads as "nothing changed".

A correction I made mid-change

The diff is appended as plain text, because the template already wraps the whole worker report in its untrusted-data block.

My first version added a second fence, with a comment claiming it stopped a malicious diff from escaping. Then the test I wrote for it passed with that escaping removed — the outer block had been doing the work all along. The redundant fence is gone, the comment says what is actually true, and the test now bisects against the escaper that really protects it (removing DATA_BLOCK_CLOSE escaping in the template turns it red).

  • npm run lint · npx tsc --noEmit — clean
  • npm run test:coverage — 266 files, 3574 pass, statements 90.14%

…nd one

The gate's own output caught this. A CI run returned `revise` whose feedback
began: "I cannot determine the actual diff between the current state and commit
47b9837 because the .git directory is not accessible" — and then delivered a
verdict anyway, on files read in isolation.

`buildReviewerPrompt` gave direct mode only a list of changed filenames. In
working-tree mode an agent can recover the rest with bash. Under `--read-only`
it cannot, and in `--base` mode there is nothing in the working tree to recover:
reading a file shows the result of the change, never the change. Those two flags
together are exactly what the GitHub Action passes, so the flagship CI path was
reviewing blind.

The diff now travels in the prompt. It is bounded at 200KB and says so when it
truncates, because a silently partial diff invites a verdict on a change the
reviewer only partly saw. A git failure degrades to the old file-list behaviour
rather than ending the review, and omits the section rather than showing an
empty one, since a heading with nothing under it reads as "nothing changed".

The diff is appended as plain text: the template already wraps the whole worker
report in its untrusted-data block. My first version added a second fence with a
comment claiming it stopped the diff from escaping — and the test I wrote for it
passed with that escaping removed, because the outer block was doing the work.
Removed, and the test now bisects against the escaper that actually protects it.
@unohee
unohee merged commit eb0fdfa into main Aug 1, 2026
10 checks passed
@unohee
unohee deleted the fix/reviewer-gets-the-diff branch August 1, 2026 11:20
@unohee unohee mentioned this pull request Aug 1, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e4fe9d8500

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agents/reviewer.ts
Comment on lines +162 to +168
const report = options.diff
? `- **Files changed (${files.length}):** ${filesSummary}\n- **Diff under review:**\n${options.diff}`
: `- **Files changed (${files.length}):** ${filesSummary}`;
return getPrompts().buildReviewerPrompt({
taskTitle: options.taskTitle,
taskDescription: options.taskDescription,
workerReport: `- **Files changed (${files.length}):** ${filesSummary}`,
workerReport: report,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the supplied diff past the generic data cap

When a diff exceeds roughly 20,000 characters, passing it as workerReport causes promptDataBlock() to truncate it at MAX_PROMPT_DATA_CHARS in src/locale/prompts/en.ts:9-17 (and the Korean equivalent), long before getDiffText()'s advertised 200,000-character limit. In read-only CI, a large early patch can therefore hide every later patch; when more than 20 files changed, those later filenames are also absent from filesSummary, so the reviewer cannot even determine which files to inspect without Git access. Give direct-review diffs a compatible bound or otherwise preserve the remaining changed-file identities and truncation guidance.

Useful? React with 👍 / 👎.

Comment thread src/cli/reviewCommand.ts
mode: 'direct',
priorReviewContext: history.context,
readOnly: opts.readOnly,
diff: await deps.getDiff?.(c, opts.base) ?? await defaultGetDiff(c, opts.base),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude filtered OpenSwarm state from the supplied diff

When a repository does not ignore .openswarm/ (the case the explicit filter at lines 287-290 supports) and it has both a real source change and generated review-history/audit changes, changed excludes the OpenSwarm state but this call obtains an unfiltered repository-wide Git diff. The prompt consequently includes files that the command deliberately removed from its review scope, allowing self-generated reports to consume prompt space and influence the next verdict. Generate the supplied diff from the filtered paths or apply the same .openswarm exclusion to it.

Useful? React with 👍 / 👎.

Comment thread src/support/gitTracker.ts
Comment on lines +59 to +61
const diff = await runGitCommand(projectPath, args);
if (diff.length <= maxBytes) return diff;
return `${diff.slice(0, maxBytes)}\n\n[diff truncated at ${maxBytes} bytes of ${diff.length}; read the files directly for the rest]`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound Git output while it is being collected

For a very large text diff, such as an attacker-authored CI change containing generated files, runGitCommand() concatenates the complete Git output before this code checks maxBytes and slices it. The advertised 200 KB bound therefore does not bound memory or collection time, and a sufficiently large diff can exhaust the Node process before the reviewer starts. Stream and count the output while retaining only the allowed prefix instead of materializing the entire patch.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant