Skip to content

fix(#1559): preserve prior successful review on agent failure - #1560

Closed
fullsend-ai-coder[bot] wants to merge 2 commits into
mainfrom
agent/1559-preserve-review-on-failure
Closed

fix(#1559): preserve prior successful review on agent failure#1560
fullsend-ai-coder[bot] wants to merge 2 commits into
mainfrom
agent/1559-preserve-review-on-failure

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

When the review agent encounters a tool-failure after previously posting a successful review, the sticky comment was overwritten with a blanket "NOT reviewed" notice, collapsing the valid review findings into a hidden

Details block. This created a misleading signal for anyone checking the PR's review status.

postFailureNotice() now checks for an existing sticky comment with a prior successful review before posting. When one exists, the failure is shown as a warning banner while the previous review findings remain prominently visible. Prior failures and stale-head notices are not preserved (replaced as before).

Added ExtractCurrentContent() to the sticky package to extract the top-level content from a sticky comment body, stripping the marker, footer, and collapsed history blocks.


Closes #1559

Post-script verification

  • Branch is not main/master (agent/1559-preserve-review-on-failure)
  • Secret scan passed (gitleaks — 6a20b831b8d7655aa0d550d342676b404dfd8214..HEAD)
  • Pre-commit hooks passed (authoritative run on runner)
  • Tests ran inside sandbox

When the review agent encounters a tool-failure after previously
posting a successful review, the sticky comment was overwritten
with a blanket "NOT reviewed" notice, collapsing the valid review
findings into a hidden <details> block. This created a misleading
signal for anyone checking the PR's review status.

postFailureNotice() now checks for an existing sticky comment
with a prior successful review before posting. When one exists,
the failure is shown as a warning banner while the previous
review findings remain prominently visible. Prior failures and
stale-head notices are not preserved (replaced as before).

Added ExtractCurrentContent() to the sticky package to extract
the top-level content from a sticky comment body, stripping the
marker, footer, and collapsed history blocks.

Closes #1559

Signed-off-by: fullsend-code <fullsend-code@users.noreply.github.com>
@github-actions

github-actions Bot commented May 26, 2026

Copy link
Copy Markdown

Site preview

Preview: https://6ed4aec4-site.fullsend-ai.workers.dev

Commit: d67e1609b3afe639d020b0ec34c54d7936a9f4b0

@fullsend-ai-review

fullsend-ai-review Bot commented May 26, 2026

Copy link
Copy Markdown

Review

Findings

Low

  • [redundant-api-calls] internal/cli/postreview.go:253postFailureNotice now calls GetAuthenticatedUser and ListIssueComments to check for prior successful reviews, then unconditionally calls sticky.Post() which makes the same two API calls internally (sticky.go lines 43–53). This doubles the API round-trips on every failure path.
    Remediation: Consider extracting the comment lookup into a shared helper or passing the found comment/bot-user to sticky.Post to avoid redundant calls.

  • [test-gap] internal/cli/postreview_test.go — No test covers the interaction when parsed.Body is non-empty AND a prior successful review exists. In that case, the custom failure body is silently overridden by the preservation logic (line 260 replaces body). While this is likely the intended behavior, a test would document that expectation and prevent regressions.
    Remediation: Add a test that seeds both parsed.Body (custom failure message) and a prior successful review comment, then asserts the preserved review is shown and the custom body is not.

Previous run

Review

Findings

High

  • [correctness] internal/cli/postreview.go:255isFailureOrStaleContent does not match the format string produced by postFailureNotice. The format string wraps the emoji inside bold markers (**⚠️ Latest review run failed**) but the detection function searches for the emoji outside bold markers (⚠️ **Latest review run failed**). Because strings.Contains performs exact substring matching, the guard against re-nesting preserved reviews never triggers. Consecutive failures will produce doubly-nested warning banners instead of replacing the prior preserved-failure content.
    Remediation: Align the search string in isFailureOrStaleContent with the format string — either change the format string to > ⚠️ **Latest review run failed** (%s) or change the search to **⚠️ Latest review run failed**. Then update the isFailureOrStaleContent test's "prior preserved failure" input to match the actual code output.

Medium

  • [correctness] internal/cli/postreview_test.go:303 — The "prior preserved failure" test case in TestIsFailureOrStaleContent uses ⚠️ **Latest review run failed** (emoji outside bold) which matches the search pattern but does not match the actual format string produced by postFailureNotice (**⚠️ Latest review run failed**, emoji inside bold). This means the test passes but does not exercise the real code path — it validates the wrong input format.
    Remediation: Change the test input to match the format string: "## Review\n\n> **⚠️ Latest review run failed** (x).". The test should then fail, confirming the bug in isFailureOrStaleContent, and both should be fixed together.

Low

  • [style] internal/cli/postreview.go:245GetAuthenticatedUser error is silently discarded (botUser, _ := ...) without logging a warning. In sticky.Post (sticky.go:43-45), the same call logs "Could not determine bot user, marker spoofing protection degraded" when it fails. The inconsistency means that if auth fails here, FindMarkedComment will match any user's comment (since botUser is empty), with no indication in the logs.
    Remediation: Log a warning via printer.StepInfo when the error is non-nil, consistent with sticky.Post.

@fullsend-ai-review fullsend-ai-review 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.

See the review comment for full details.

priorContent := sticky.ExtractCurrentContent(existing.Body, cfg)
if priorContent != "" && !isFailureOrStaleContent(priorContent) {
body = fmt.Sprintf("## Review\n\n> **⚠️ Latest review run failed** (%s). Showing results from previous successful review.\n\n%s", reason, priorContent)
printer.StepInfo("Preserving prior successful review")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[high] correctness

isFailureOrStaleContent does not match the format string produced by postFailureNotice. The format string wraps the emoji inside bold markers (⚠️ Latest review run failed) but the detection function searches for the emoji outside bold markers (⚠️ Latest review run failed). The guard against re-nesting preserved reviews never triggers, so consecutive failures produce doubly-nested warning banners.

Suggested fix: Align the search string with the format string — either change the format to place the emoji outside bold, or change the search to ⚠️ Latest review run failed. Update the corresponding test input to match.

{"empty", "", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[medium] correctness

The "prior preserved failure" test case uses input with the emoji outside bold markers, which matches the search pattern but not the actual format string from postFailureNotice. The test passes but validates the wrong input format.

Suggested fix: Change the test input to match the actual format string. The test should then fail, confirming the bug, and both should be fixed together.

This PR was NOT reviewed. Do not count this as an approval.`, reason)
}

// When a prior successful review exists, preserve it prominently

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] style

GetAuthenticatedUser error silently discarded without logging, inconsistent with sticky.Post which logs a warning for the same failure.

Suggested fix: Log a warning via printer.StepInfo when the error is non-nil.

@rh-hemartin

Copy link
Copy Markdown
Member

/fs-fix

…Notice

- Fix isFailureOrStaleContent to match the actual format string produced
  by postFailureNotice (emoji inside bold markers, not outside)
- Update test case to use the correct format string
- Log warning when GetAuthenticatedUser fails, consistent with sticky.Post

Addresses review feedback on #1560

Signed-off-by: fullsend-fix <fullsend-code@users.noreply.github.com>
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (bot-triggered)

Addressed all 3 review findings: fixed the string mismatch between isFailureOrStaleContent and the format string in postFailureNotice (high), corrected the test case to use the actual format (medium), and added error logging for GetAuthenticatedUser (low).

Fixed (3):

  1. isFailureOrStaleContent string mismatch with format string (internal/cli/postreview.go): Aligned the search string in isFailureOrStaleContent to match the actual format string produced by postFailureNotice: changed from '⚠️ Latest review run failed' (emoji outside bold) to '⚠️ Latest review run failed' (emoji inside bold). This fixes the bug where consecutive failures would produce doubly-nested warning banners.
  2. Test case uses wrong format for prior preserved failure (internal/cli/postreview_test.go): Updated the 'prior preserved failure' test input in TestIsFailureOrStaleContent to match the actual format string produced by postFailureNotice, ensuring the test exercises the real code path.
  3. GetAuthenticatedUser error silently discarded (internal/cli/postreview.go): Added warning log via printer.StepInfo when GetAuthenticatedUser returns an error, consistent with the pattern used in sticky.Post.

Tests: passed

Updated by fullsend fix agent

printer.StepInfo("Could not determine bot user, marker spoofing protection degraded")
}
comments, listErr := client.ListIssueComments(ctx, owner, repo, pr)
if listErr == 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] redundant-api-calls

postFailureNotice now calls GetAuthenticatedUser and ListIssueComments to check for prior successful reviews, then unconditionally calls sticky.Post() which makes the same two API calls internally (sticky.go lines 43-53). This doubles the API round-trips on every failure path.

Suggested fix: Consider extracting the comment lookup into a shared helper or passing the found comment/bot-user to sticky.Post to avoid redundant calls.

@fullsend-ai-review fullsend-ai-review Bot added the ready-for-merge All reviewers approved — ready to merge label May 27, 2026
@rh-hemartin

Copy link
Copy Markdown
Member

A lot of changes to the review agent in the last week, closing to see if we reproduce this again.

@rh-hemartin rh-hemartin closed this Jun 8, 2026
@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #1560 — fix(#1559): preserve prior successful review on agent failure

Outcome: Closed without merging after 12 days. Human determined recent review agent changes may have already addressed the underlying problem.

Timeline

  1. Retro agent filed #1559 after observing review overwrite behavior on PR feat(e2e): parallel test runs via org pool #1215
  2. Code agent created PR #1560 (~15 min triage-to-PR)
  3. Review agent found real bugs: string mismatch in failure detection, wrong test format, swallowed error — requested changes
  4. Fix agent auto-trigger failed — eligibility gate misclassified bot-authored PR as human-authored (known bug #1569)
  5. 8-hour delay until human manually triggered /fs-fix
  6. Fix dispatch sent wrong PR payload — dispatched fix for PR docs(#1544): add ADR 0040 for /fs- slash command prefix #1549 instead of fix(#1559): preserve prior successful review on agent failure #1560 (known bug #1326)
  7. Fix commit landed correctly on PR fix(#1559): preserve prior successful review on agent failure #1560 via the direct /fs-fix path; secondary dispatch runs were misdirected
  8. Review agent approved on second pass, ready-for-merge label applied
  9. PR sat approved for 12 days with no merge action, then human closed it

What went well

  • Review quality was strong. The review agent caught a genuine correctness bug (emoji placement mismatch between detection string and format string) that would have caused the feature to silently fail on consecutive failures. It also caught a test that validated the wrong format.
  • Code agent produced a reasonable initial implementation with proper test coverage (204 lines added across 4 files).
  • Fix agent addressed all review findings once it successfully ran.

What went wrong

Proposals

No new proposals — all identified improvements are already tracked in existing open issues. Resolving #1569 and #1326 would have eliminated the 8-hour human intervention delay and the misdirected fix runs on this PR.

@github-actions
github-actions Bot deleted the agent/1559-preserve-review-on-failure branch June 28, 2026 04:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-merge All reviewers approved — ready to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Review agent tool-failure overwrites valid prior reviews with 'NOT reviewed'

1 participant