Skip to content

fix(post-review): post file-level comments in a separate review - #6001

Open
rh-hemartin wants to merge 1 commit into
mainfrom
fix/post-review-file-level-isolation
Open

fix(post-review): post file-level comments in a separate review#6001
rh-hemartin wants to merge 1 commit into
mainfrom
fix/post-review-file-level-isolation

Conversation

@rh-hemartin

Copy link
Copy Markdown
Member

Summary

  • File-level comments (findings whose line falls outside any diff hunk) were mixed into the main review batch. A single invalid file-level comment would 422 the entire review submission, dropping all inline comments with it.
  • findingsToReviewComments now returns two separate slices (inline and fileLevel) instead of one mixed slice.
  • File-level comments are posted in a preceding COMMENT review. If that call fails, a warning is logged and the main review proceeds unaffected.

Relates-to: fullsend-ai/agents#430
Relates-to: fullsend-ai/agents#193

Test plan

  • TestFindingsToReviewComments -- verifies inline/fileLevel separation with nil diffHunks
  • TestFindingsToReviewComments_FiltersByDiffHunks -- out-of-hunk finding lands in fileLevel slice
  • TestFindingsToReviewComments_EmptyPatchSkipsLineFiltering -- binary/empty patches skip line filtering
  • TestFindingsToReviewComments_AllSeveritiesPassThrough -- all severities remain inline with nil diffHunks
  • TestFindingsToReviewComments_AllSeveritiesFallbackToFileLevel -- all severities fall back to file-level when out of hunk
  • TestSubmitFormalReview_FiltersByPRFileDiffs -- integration: two reviews created (COMMENT + REQUEST_CHANGES)
  • TestSubmitFormalReview_FileLevelCommentPostedSeparately -- file-level COMMENT review posted before main review
  • TestSubmitFormalReview_FileLevelFailureDoesNotBlockMainReview -- file-level 422 does not block main review

🤖 Generated with Claude Code

File-level comments (findings outside diff hunks) were mixed into
the same review batch as inline comments. A single invalid file-level
comment would 422 the entire review submission, dropping all inline
comments with it.

Split findingsToReviewComments into two return slices (inline and
fileLevel) and post file-level comments in a preceding COMMENT review.
If the file-level review fails, warn and continue; the main review
and sticky comment are unaffected.

Relates-to: fullsend-ai/agents#430
Relates-to: fullsend-ai/agents#193

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Hector Martinez <hemartin@redhat.com>
@rh-hemartin
rh-hemartin requested a review from a team as a code owner August 7, 2026 10:22
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Isolate file-level PR review comments into a separate COMMENT review

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Split findings into inline vs file-level review comments to prevent batch 422 failures.
• Post file-level comments in a separate COMMENT review before the main verdict review.
• Add test coverage for diff-hunk filtering, file-level fallback, and failure isolation.
Diagram

graph TD
  A(("Review findings")) --> B["findingsToReviewComments()"] --> C["Inline comments"] --> D["Main formal review\n(APPROVE/REQUEST_CHANGES)"] --> F{{"Forge API\nCreatePullRequestReview"}}
  B --> E["File-level comments"] --> G["Separate COMMENT review"] --> F
  G -. "best-effort; warn on failure" .-> D

  subgraph Legend
    direction LR
    _data(("Data")) ~~~ _proc["Process"] ~~~ _ext{{"External API"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Pre-validate and drop invalid file-level comments from the main batch
  • ➕ Single review submission (fewer API calls, simpler timeline)
  • ➕ Avoids creating an extra COMMENT review event on the PR
  • ➖ Hard to reliably detect all validation failures without duplicating forge-side rules
  • ➖ Still risks batch failure if validation misses an edge case
2. Post file-level findings as regular PR comments (issue comments) instead of a review
  • ➕ Decoupled from review validation rules entirely
  • ➕ Doesn’t create extra review events
  • ➖ Loses association with the review/comment UX on code
  • ➖ Harder to keep parity with existing review-comment formatting and workflows

Recommendation: The chosen approach (separate best-effort COMMENT review for file-level comments, then a main verdict review with only inline comments) is the safest/most robust way to prevent a single invalid file-level comment from discarding the entire inline review. It minimizes behavioral change, preserves inline comments reliably, and degrades gracefully by warning while still keeping findings visible via the sticky comment.

Files changed (2) +124 / -84

Bug fix (1) +22 / -18
postreview.goSplit inline vs file-level review comments and submit file-level separately +22/-18

Split inline vs file-level review comments and submit file-level separately

• Updates the review submission flow to partition findings into inline and file-level comment slices. File-level comments (out-of-hunk) are posted in a preceding COMMENT review; failures are logged as warnings and do not block the main review submission.

internal/cli/postreview.go

Tests (1) +102 / -66
postreview_test.goAdd tests for inline/file-level partitioning and failure isolation +102/-66

Add tests for inline/file-level partitioning and failure isolation

• Refactors existing tests to assert separate inline vs file-level slices from findingsToReviewComments. Adds integration-style coverage ensuring two reviews are created (COMMENT then REQUEST_CHANGES) and that a 422 on the file-level review does not prevent the main review.

internal/cli/postreview_test.go

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 7, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:23 AM UTC · Completed 10:36 AM UTC

Commit: 0f8a91e · View workflow run →

@rh-hemartin

Copy link
Copy Markdown
Member Author

Design note: per-comment isolation

An alternative considered was posting each file-level comment as its own review call, so a single invalid comment cannot take down the others. The tradeoff is API call volume: 10 file-level comments would cost 10 calls instead of 1. For repos that hit GitHub rate limits during busy periods, that multiplier adds up. The current two-batch approach (one COMMENT review for file-level, one for inline) gets most of the isolation benefit at 2 calls instead of N+1.

If file-level batch rejections turn out to be common in practice, we can revisit with per-comment posting for file-level comments while keeping inline comments batched.

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. File-level failure under-logged 🐞 Bug ◔ Observability
Description
When the separate file-level COMMENT review fails, submitFormalReview only logs the formatted error
string and omits structured GitHub API error details and rejected-comment context, making validation
failures difficult to diagnose. This is introduced by the new isolated file-level posting path which
does not reuse the existing error-detail logging helpers used for the main review submission.
Code

internal/cli/postreview.go[R355-358]

+		if err := client.CreatePullRequestReview(ctx, owner, repo, pr, "COMMENT", "", commitSHA, fileLevelComments); err != nil {
+			printer.StepWarn(fmt.Sprintf("File-level comments failed (%v), findings remain in sticky comment", err))
+		} else {
+			printer.StepDone(fmt.Sprintf("Posted %d file-level comment(s)", len(fileLevelComments)))
Relevance

●●● Strong

Repo recently accepted improving GitHub API error detail logging in postreview; consistent
diagnosability pattern likely welcomed.

PR-#5567
PR-#2444
PR-#2415

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new file-level submission branch logs only a generic warning on error, while the main review
submission path explicitly logs structured API error details and rejected comment locations via
helper functions. Reusing those helpers for file-level failures would restore comparable diagnostics
without changing the fail-open behavior.

internal/cli/postreview.go[351-360]
internal/cli/postreview.go[383-400]
internal/cli/postreview.go[476-505]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The new isolated file-level review submission logs only `File-level comments failed (%v)` and does not emit the structured GitHub error details (`gh.APIError.Errors[]`) or which specific comments were rejected. Since this call is intentionally fail-open, better diagnostics are important to troubleshoot why file-level comments didn’t appear.

### Issue Context
`logAPIErrorDetails` and `logRejectedComments` already exist and are used on the main review submission path (especially for 422 validation failures). The file-level submission path should use the same helpers (at least on 422), without blocking the main review.

### Fix Focus Areas
- internal/cli/postreview.go[351-359]
- internal/cli/postreview.go[383-400]
- internal/cli/postreview.go[476-505]

### Suggested change
- In the `CreatePullRequestReview(... fileLevelComments)` error branch:
 - Call `logAPIErrorDetails(err, printer)` so field-level validation errors are visible.
 - Optionally, when `is422Error(err)` is true, also call `logRejectedComments(fileLevelComments, err, printer)` to print which file-level comment(s) were rejected.
 - Keep the current behavior of not returning an error (main review proceeds).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
✅ Compliance rules (platform): 54 rules

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines +355 to +358
if err := client.CreatePullRequestReview(ctx, owner, repo, pr, "COMMENT", "", commitSHA, fileLevelComments); err != nil {
printer.StepWarn(fmt.Sprintf("File-level comments failed (%v), findings remain in sticky comment", err))
} else {
printer.StepDone(fmt.Sprintf("Posted %d file-level comment(s)", len(fileLevelComments)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. File-level failure under-logged 🐞 Bug ◔ Observability

When the separate file-level COMMENT review fails, submitFormalReview only logs the formatted error
string and omits structured GitHub API error details and rejected-comment context, making validation
failures difficult to diagnose. This is introduced by the new isolated file-level posting path which
does not reuse the existing error-detail logging helpers used for the main review submission.
Agent Prompt
### Issue description
The new isolated file-level review submission logs only `File-level comments failed (%v)` and does not emit the structured GitHub error details (`gh.APIError.Errors[]`) or which specific comments were rejected. Since this call is intentionally fail-open, better diagnostics are important to troubleshoot why file-level comments didn’t appear.

### Issue Context
`logAPIErrorDetails` and `logRejectedComments` already exist and are used on the main review submission path (especially for 422 validation failures). The file-level submission path should use the same helpers (at least on 422), without blocking the main review.

### Fix Focus Areas
- internal/cli/postreview.go[351-359]
- internal/cli/postreview.go[383-400]
- internal/cli/postreview.go[476-505]

### Suggested change
- In the `CreatePullRequestReview(... fileLevelComments)` error branch:
  - Call `logAPIErrorDetails(err, printer)` so field-level validation errors are visible.
  - Optionally, when `is422Error(err)` is true, also call `logRejectedComments(fileLevelComments, err, printer)` to print which file-level comment(s) were rejected.
  - Keep the current behavior of not returning an error (main review proceeds).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@rh-hemartin rh-hemartin self-assigned this Aug 7, 2026
@fullsend-ai-review

Copy link
Copy Markdown

Review

Findings

Low

  • [edge-case] internal/cli/postreview.go:355 — When the review event is COMMENT and all findings fall outside diff hunks (producing only file-level comments, zero inline comments), the file-level comments are posted in a separate COMMENT review, then the main review is skipped at the early-return. This path has no dedicated integration test covering the COMMENT verdict + only-file-level-findings scenario.
    Remediation: Add a test case with a COMMENT verdict where all findings are outside diff hunks. Verify that exactly one COMMENT review is created (the file-level one) and the main review is skipped.

  • [error-handling] internal/cli/postreview.go:353 — The file-level COMMENT review is posted with an empty body string. While GitHub's API accepts empty-body reviews when comments are present, this is an implicit contract that could break if the API changes its validation.


Labels: Bug fix in Go CLI post-review command (review agent pipeline)

// don't poison the main review batch. A single invalid file-level
// comment would otherwise 422 the entire submission.
if len(fileLevelComments) > 0 {
if err := client.CreatePullRequestReview(ctx, owner, repo, pr, "COMMENT", "", commitSHA, fileLevelComments); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] edge-case

When the review event is COMMENT and all findings fall outside diff hunks (producing only file-level comments, zero inline comments), the file-level comments are posted in a separate COMMENT review, then the main review is skipped at the early-return. This path has no dedicated integration test covering the COMMENT verdict + only-file-level-findings scenario.

Suggested fix: Add a test case with a COMMENT verdict where all findings are outside diff hunks. Verify that exactly one COMMENT review is created (the file-level one) and the main review is skipped.


// Post file-level comments in a separate COMMENT review so they
// don't poison the main review batch. A single invalid file-level
// comment would otherwise 422 the entire submission.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] error-handling

The file-level COMMENT review is posted with an empty body string. While GitHub's API accepts empty-body reviews when comments are present, this is an implicit contract that could break if the API changes its validation.

@fullsend-ai-review fullsend-ai-review Bot added ready-for-merge All reviewers approved — ready to merge bug go Pull requests that update go code labels Aug 7, 2026

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review findings (2 posted inline; 1 additional finding below on a line outside the diff hunk that GitHub won't let me anchor inline):

[MEDIUM] internal/cli/postreview.go:367 — Misleading skip message and doc comment after a file-level review has already been posted

When event == "COMMENT" and every eligible finding falls outside a diff hunk, fileLevelComments is non-empty and inlineComments is empty. The code first posts a COMMENT review with the file-level comments (lines 354-360), then hits if event == "COMMENT" && len(inlineComments) == 0 (line 367) and logs "Skipping formal COMMENT review (sticky comment already updated)" before returning nil. Both this log line and the function's doc comment (lines 296-299: "COMMENT: skipped when no inline-eligible findings exist ... sticky comment already covers it") imply no review was submitted in this run, when in fact a separate COMMENT review carrying the file-level comments was just posted moments earlier.

Suggestion: rephrase the log message and doc comment to acknowledge that a file-level review may already have been posted in this branch, e.g. only claim "sticky comment already covers it" when fileLevelComments is also empty, or say "Skipping formal COMMENT review with inline comments (file-level findings posted separately, if any)".

printer.StepInfo(fmt.Sprintf("%d finding(s) posted as file-level comment(s) (line outside diff hunk)", len(fileLevelComments)))
}

// Post file-level comments in a separate COMMENT review so they

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[MEDIUM] PR's stated rationale for isolating file-level comments is not supported by the linked issues

The comment here and the PR description assert that "a single invalid file-level comment would 422 the entire review submission, dropping all inline comments with it." Both linked issues (fullsend-ai/agents#430 and fullsend-ai/agents#193) describe a different, already-fixed failure mode: out-of-hunk inline comments (not Line=0 file-level comments) causing a 422 because they weren't excluded from the inline array before submission. Neither issue documents an observed case where a file-level comment itself (Line=0, no position) was invalid and 422'd a batch that also contained valid inline comments. The new isolation mechanism reads as speculative hardening for an unconfirmed failure mode rather than a fix for the cited root cause.

Suggestion: either confirm a concrete scenario where a file-level comment itself is rejected by GitHub, or soften the code comment/PR description to describe this as defense-in-depth rather than a proven root cause.

// don't poison the main review batch. A single invalid file-level
// comment would otherwise 422 the entire submission.
if len(fileLevelComments) > 0 {
if err := client.CreatePullRequestReview(ctx, owner, repo, pr, "COMMENT", "", commitSHA, fileLevelComments); err != nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[MEDIUM] No 422 retry/fallback for the file-level review, unlike the main review

The main review submission below (around lines 383-400) has an explicit 422 fallback: it retries without inline comments and folds them into the review body via buildFallbackReviewBody, preserving visibility of the findings. This new file-level COMMENT review has no equivalent fallback — on any failure the file-level comments are simply dropped with a printer.StepWarn, even though they could similarly be appended to a fallback body so the findings remain visible on the review itself rather than only in the sticky comment.

Suggestion: reuse buildFallbackReviewBody-style logic for the file-level comments so a failed file-level review still surfaces the findings' content in a body, consistent with the main review's 422 fallback behavior.

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

Labels

bug go Pull requests that update go code ready-for-merge All reviewers approved — ready to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants