Skip to content

Commit 7a3d8d8

Browse files
committed
feat: auto-fix issues during review phase
Enhanced the review phase to automatically fix issues: - If review finds issues, spawn agent to fix them - Run tests to verify fixes work - Commit and push fixes if tests pass - Re-review the PR after fixes - Only block merge if auto-fix fails or cannot resolve issues This makes evolve self-healing — it doesn't just block on issues, it actively tries to resolve them before giving up.
1 parent ebfeb3d commit 7a3d8d8

1 file changed

Lines changed: 93 additions & 3 deletions

File tree

internal/evolution/git.go

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,99 @@ func (e *Engine) reviewPR(ctx context.Context, p iteragent.Provider, tools []ite
233233
return nil
234234
}
235235

236-
// Reviewer found issues but didn't say LGTM — block the merge.
237-
e.logger.Warn("PR self-review did not pass — blocking merge")
238-
return fmt.Errorf("review blocked merge: reviewer did not say LGTM")
236+
// Reviewer found issues — try to auto-fix them
237+
e.logger.Warn("PR self-review found issues — attempting auto-fix")
238+
239+
fixed, fixErr := e.autoFixIssues(ctx, p, tools, systemPrompt, skills, reviewOutput)
240+
if fixErr != nil {
241+
e.logger.Error("auto-fix failed", "err", fixErr)
242+
e.postReviewComment(ctx, "Auto-fix attempt failed: "+fixErr.Error(), false)
243+
return fmt.Errorf("review blocked merge: %w", fixErr)
244+
}
245+
246+
if !fixed {
247+
e.logger.Warn("auto-fix could not resolve all issues — blocking merge")
248+
return fmt.Errorf("review blocked merge: auto-fix could not resolve all issues")
249+
}
250+
251+
// Re-review after fixes
252+
e.logger.Info("auto-fix applied — re-reviewing PR")
253+
return e.reviewPR(ctx, p, tools, systemPrompt, skills)
254+
}
255+
256+
// autoFixIssues attempts to fix issues identified during review.
257+
// Returns true if fixes were applied and tests pass, false otherwise.
258+
func (e *Engine) autoFixIssues(ctx context.Context, p iteragent.Provider, tools []iteragent.Tool, systemPrompt string, skills *iteragent.SkillSet, reviewOutput string) (bool, error) {
259+
e.logger.Info("starting auto-fix based on review feedback")
260+
261+
// Build fix prompt with review feedback
262+
fixPrompt := fmt.Sprintf(`The code review identified issues:
263+
264+
%s
265+
266+
Fix these issues in the codebase. Run tests to verify the fixes work.
267+
If you cannot fix an issue, explain why. Only commit if tests pass.`, reviewOutput)
268+
269+
a := e.newAgent(p, tools, systemPrompt, skills)
270+
var fixResult string
271+
for ev := range a.Prompt(ctx, fixPrompt) {
272+
if e.eventSink != nil {
273+
select {
274+
case e.eventSink <- ev:
275+
default:
276+
}
277+
}
278+
if ev.Type == string(iteragent.EventMessageEnd) {
279+
fixResult = ev.Content
280+
}
281+
}
282+
a.Finish()
283+
284+
// Check if any changes were made
285+
status, err := e.runTool(ctx, "bash", map[string]interface{}{
286+
"cmd": "git status --porcelain",
287+
})
288+
if err != nil {
289+
return false, fmt.Errorf("failed to check git status: %w", err)
290+
}
291+
292+
if strings.TrimSpace(status) == "" {
293+
e.logger.Info("no changes made during auto-fix")
294+
return false, nil
295+
}
296+
297+
// Run tests to verify fixes
298+
e.logger.Info("running tests to verify auto-fix")
299+
_, testErr := e.runTool(ctx, "bash", map[string]interface{}{
300+
"cmd": "go test ./... 2>&1 | head -50",
301+
})
302+
if testErr != nil {
303+
e.logger.Warn("tests failed after auto-fix, reverting changes")
304+
// Revert failed fixes
305+
e.runTool(ctx, "bash", map[string]interface{}{
306+
"cmd": "git checkout -- .",
307+
})
308+
return false, fmt.Errorf("tests failed after auto-fix")
309+
}
310+
311+
// Commit the fixes
312+
e.logger.Info("committing auto-fix changes")
313+
if _, err := e.runTool(ctx, "bash", map[string]interface{}{
314+
"cmd": "git add -A && git commit -m 'fix: auto-fix issues from review'",
315+
}); err != nil {
316+
return false, fmt.Errorf("failed to commit auto-fix: %w", err)
317+
}
318+
319+
// Push fixes to the PR branch
320+
if _, err := e.runTool(ctx, "bash", map[string]interface{}{
321+
"cmd": fmt.Sprintf("git push origin %s", e.branchName),
322+
}); err != nil {
323+
return false, fmt.Errorf("failed to push auto-fix: %w", err)
324+
}
325+
326+
e.logger.Info("auto-fix applied successfully")
327+
e.postReviewComment(ctx, fmt.Sprintf("Auto-fix applied:\n\n%s\n\nChanges committed and pushed.", fixResult), true)
328+
return true, nil
239329
}
240330

241331
// postReviewComment posts the reviewer's output as a GitHub PR comment.

0 commit comments

Comments
 (0)