fix: prevent feedback loop from silent reply failures and stale session data - #44
Conversation
…on data The bot ran 14 feedback sessions ($34) on a single PR because: 1. Stale comment-responses.json from prior sessions caused false "success" on the AI-responses path, resetting the retry counter. 2. The final-attempt path returned success regardless of whether addressed-reply comments actually posted, enabling infinite loops. 3. handleNoChanges lacked final-attempt handling, leaving review comments dangling without acknowledgement. Changes: - Clean AI output files between feedback sessions (comment-responses, session-output, cli-output, pr.md) to prevent stale data from prior sessions being read as current. session-context.md is preserved. - Reply functions (replyToCommentsOnRepo, replyToComments, replyUnableToAddress) now return the count of successfully posted replies. Critical paths (final-attempt, AI-responses) return an error when zero replies land, preventing false retry-counter resets. Commit-success paths remain best-effort. - handleNoChanges now handles the final-attempt scenario by posting "unable to address" replies instead of returning a bare error. - Cost labels use round-based numbering with retry tracking and outcome suffixes: "Feedback (2) retry 1 (no changes)" instead of sequential "Feedback #3". Infrastructure errors get "(error)" label distinct from "(no changes)". - Cost labels use parenthesized numbers instead of # to prevent GitHub autolinking to unrelated issues/PRs. - Missing cost comment on single-repo final-attempt ErrNoChanges path is now recorded. Assisted-by: Claude claude-opus-4-6 (1M) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Enterprise Run ID: 📒 Files selected for processing (3)
WalkthroughCost-comment labels now use parenthesized feedback rounds and attempt-aware retries. Feedback execution clears stale AI output files, counts posted replies, and fails when AI responses produce no successful posts. Call sites and tests were updated for the new behavior. ChangesFeedback Labeling and Reply Tracking
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 13✅ Passed checks (13 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
executor/costcomment.go (1)
144-162: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMinor duplication in suffix/label derivation.
The
strings.HasPrefix(label, "Feedback")→TrimPrefix→feedbackLabel(...)sequence is repeated verbatim in both branches (existing comment vs. first comment), differing only in whetherentriesornilis passed. Consider extracting a tiny helper (e.g.deriveFeedbackLabel(entries []costEntry, attemptNum int, label string) string) to avoid the duplicate 3-line block.♻️ Proposed helper extraction
+func deriveFeedbackLabel(entries []costEntry, attemptNum int, label string) string { + if !strings.HasPrefix(label, "Feedback") { + return label + } + suffix := strings.TrimPrefix(label, "Feedback") + return feedbackLabel(entries, attemptNum, suffix) +} + existing := findCostComment(comments) if existing != nil { entries := parseCostComment(existing.Body) - if strings.HasPrefix(label, "Feedback") { - suffix := strings.TrimPrefix(label, "Feedback") - label = feedbackLabel(entries, attemptNum, suffix) - } + label = deriveFeedbackLabel(entries, attemptNum, label) entries = append(entries, costEntry{Label: label, Cost: cost}) body := formatCostComment(entries) ... } - if strings.HasPrefix(label, "Feedback") { - suffix := strings.TrimPrefix(label, "Feedback") - label = feedbackLabel(nil, attemptNum, suffix) - } + label = deriveFeedbackLabel(nil, attemptNum, label)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@executor/costcomment.go` around lines 144 - 162, The Feedback label derivation logic is duplicated in both branches of cost comment handling. Extract the repeated strings.HasPrefix/TrimPrefix/feedbackLabel sequence into a small helper near cost comment generation (for example, in the code path around formatCostComment and UpdateIssueComment) and use it in both the existing-comment and first-comment cases, passing either entries or nil as needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@executor/costcomment_test.go`:
- Around line 238-317: `TestFeedbackLabel` is missing coverage for the
`attemptNum == 0` boundary that falls into `feedbackLabel`’s `attemptNum <= 1`
branch. Add a dedicated test case in `TestFeedbackLabel` using the existing
`feedbackLabel` helper and `costEntry` fixtures to assert the expected label
when `attemptNum` is zero, so the zero-value/edge-case behavior is explicitly
verified alongside the current `attemptNum` 1 and retry cases.
In `@executor/feedback_test.go`:
- Around line 285-291: The test in feedback_test.go is discarding the error from
p.Execute, which makes the preservation assertion unreliable. Update the test
around d.pipeline(t), p.Execute, and the ctxPath os.Stat check to either assert
that Execute succeeds before checking session-context.md or add a brief
justification comment if the error is intentionally ignored. Keep the assertion
meaningful by ensuring the test only passes when Execute reaches the cleanup
path in feedback.go.
In `@executor/feedback.go`:
- Around line 1136-1153: The no-op reply paths in feedback.go currently treat
posted == 0 as an error even when there are no comments to answer. Update the
reply handling around p.replyToComments and p.replyUnableToAddress in the
feedback flow so the zero-post failure is only checked when newComments actually
contains comments to reply to, allowing CI-only cases to exit successfully
without an error.
---
Outside diff comments:
In `@executor/costcomment.go`:
- Around line 144-162: The Feedback label derivation logic is duplicated in both
branches of cost comment handling. Extract the repeated
strings.HasPrefix/TrimPrefix/feedbackLabel sequence into a small helper near
cost comment generation (for example, in the code path around formatCostComment
and UpdateIssueComment) and use it in both the existing-comment and
first-comment cases, passing either entries or nil as needed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: c34ca4c3-8acd-4c15-a176-d4d3cdc6a514
📒 Files selected for processing (7)
executor/costcomment.goexecutor/costcomment_test.goexecutor/feedback.goexecutor/feedback_test.goexecutor/merge.goexecutor/pipeline.goexecutor/pipeline_test.go
- Guard posted==0 checks with len(newComments)>0 so CI-only feedback sessions (no review comments, only CI failures) don't incorrectly fail on the final attempt when there are zero comments to reply to - Add attemptNum=0 edge case to TestFeedbackLabel - Log intentionally-ignored Execute error in session-context preservation test - Extract handleErrNoChanges to keep executeFeedback under the cyclomatic complexity threshold Assisted-by: Claude claude-opus-4-6 (1M) <noreply@anthropic.com>
|
@coderabbitai Re: the suggestion to extract |
|
Tip For best results, initiate chat on the files or code changes.
(=ↀωↀ=) ✏️ Learnings added
|
|
/lgtm |
## Summary - Fix cost label oscillation observed in production on [osac-operator PR #338](osac-project/osac-operator#338 (comment)): labels cycled between "Feedback (15)" and "Feedback (14) retry 1" because `countFeedbackRounds` excluded error entries from the count - Remove `!strings.Contains(e.Label, "error")` filter — error entries with round numbers (e.g., "Feedback (15) (error)") should count as rounds since `feedbackLabel` always includes the round number - Follow-up to #44 which introduced the error exclusion based on a code review concern about bare "Feedback (error)" labels, which can't occur in practice ## Test plan - [x] Updated `TestCountFeedbackRounds` — "error entries count as rounds" replaces "error entries do not count as rounds" - [x] All executor tests pass with `-race` - [x] Lint clean Assisted-by: Claude <noreply@anthropic.com>
Summary
comment-responses.jsonfrom prior sessions causing false successhandleNoChangesnow handles final-attempt scenario with "unable to address" repliesTest plan
TestExecuteFeedback_StaleCommentResponsesCleaned— stale file from prior session doesn't cause false successTestExecuteFeedback_NoChanges_WithCommentResponses— AI writing responses during session (not pre-written) works correctlyTestExecuteFeedback_CleanupPreservesSessionContext— session-context.md survives cleanupTestFeedbackCostLabel— all four branches (no-changes, infrastructure error, final attempt, success)TestCountFeedbackRounds— error and retry entries excluded from round countTestFeedbackLabel— round-based labeling with retries and suffixes-raceAssisted-by: Claude noreply@anthropic.com
Affects
executor/only.comment-responses.jsonfrom being reused) while preservingsession-context.md, addressing the repeated AI-session feedback loop.Feedback (2) retry 1 (no changes)/Feedback (unable)), avoiding GitHub autolink behavior.