Skip to content

Port: avoid embedding large adversarial review diffs inline #40

Closed

Description

@JohnnyVicious

Port of openai/codex-plugin-cc#179 (merged)

Problem

Today buildReviewPrompt in plugins/opencode/scripts/lib/prompts.mjs unconditionally embeds the full git diff output into the review prompt:

// prompts.mjs:32-36
diff = await getDiff(cwd, { base: opts.base });
status = await getStatus(cwd);
changedFiles = await getChangedFiles(cwd, { base: opts.base });
// ...
if (diff) {
  sections.push(`<diff>\n${diff}\n</diff>`);
}

For moderate/large changesets (upstream repro: ~4500-line markdown corpus, also 148 files / ~19.5k LOC) this blows out the prompt, triggers HTTP 400 / content-too-large errors on the OpenCode backend, or silently causes the model to skim and return shallow findings.

Fix (from upstream PR)

Classify the review scope before embedding, and fall back to a lightweight "self-collect" context when either the file count or total diff byte size exceeds thresholds:

  • DEFAULT_INLINE_DIFF_MAX_FILES = 2
  • DEFAULT_INLINE_DIFF_MAX_BYTES = 256 * 1024

When the scope is small, include the full diff as today. When it's larger, emit a lightweight context (status + diff stat + changed-files list + untracked-file pointers only) and add a guidance line to the prompt telling the model to inspect the diff itself via read-only git commands:

The repository context below is a lightweight summary. Inspect the target diff yourself with read-only git commands before finalizing findings.

Implementation sketch for opencode

This is cleaner than the upstream Codex version because opencode doesn't have the broker/app-server layering. Concrete changes:

  1. lib/git.mjs: add a measureDiffBytes(cwd, base, maxBytes) helper that uses git diff --shortstat and git diff with maxBuffer to cap the read.

  2. lib/prompts.mjs::buildReviewPrompt: compute { diffBytes, fileCount } before building the context. If fileCount > 2 or diffBytes > 256 KB, set includeDiff = false and switch to the lightweight branch.

  3. lib/prompts.mjs::buildReviewContext: accept an includeDiff flag. When false, emit <diff_stat> and <changed_files> only, skip <diff>, and append the self-collect guidance.

  4. prompts/adversarial-review.md: add a {{REVIEW_COLLECTION_GUIDANCE}} placeholder so the lightweight mode can inject its guidance line.

Reference code from upstream

// lib/git.mjs additions
const DEFAULT_INLINE_DIFF_MAX_FILES = 2;
const DEFAULT_INLINE_DIFF_MAX_BYTES = 256 * 1024;

function measureGitOutputBytes(cwd, args, maxBytes) {
  const result = git(cwd, args, { maxBuffer: maxBytes + 1 });
  if (result.error?.code === "ENOBUFS") return maxBytes + 1;
  if (result.status !== 0) throw new Error(formatCommandFailure(result));
  return Buffer.byteLength(result.stdout, "utf8");
}
// In the review-context builder:
const diffBytes = measureGitOutputBytes(cwd, ["diff", base ? `${base}...HEAD` : "HEAD"], DEFAULT_INLINE_DIFF_MAX_BYTES);
const includeDiff = changedFiles.length <= DEFAULT_INLINE_DIFF_MAX_FILES
                 && diffBytes <= DEFAULT_INLINE_DIFF_MAX_BYTES;

const collectionGuidance = includeDiff
  ? "Use the repository context below as primary evidence."
  : "The repository context below is a lightweight summary. Inspect the target diff yourself with read-only git commands before finalizing findings.";

Test plan

  1. Small diff (1 file, 10 LOC) -> inline mode, <diff> present.
  2. 3-file diff -> self-collect mode, no <diff>, guidance line present, <changed_files> present.
  3. 1-file diff >256 KB -> self-collect mode.
  4. PR review via --pr N -> threshold evaluated against PR diff size (keep the existing gh pr diff path, just measure its output).

Upstream reference

openai/codex-plugin-cc#179 (merged 2026-04-08), closes upstream #11.

Activity

  1. JohnnyVicious commented on Apr 12, 2026

    @JohnnyVicious
    OwnerAuthor

    Consolidated into #32. Avoiding large adversarial inline diffs is part of the broader review-context size cap work.

  2. added 2 commits that reference this issue on Apr 12, 2026
    e196f88
    e6a18bc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions