feat(refinery): implement merge failure handling with labels and notifications

Add comprehensive failure handling for merge queue:
- Add FailureType enum with conflict, tests_fail, build_fail, flaky_test, push_fail
- Add handleFailure function that updates beads issue with labels and assignee
- Add notifyWorkerFailure for type-specific failure notifications
- Add retry logic for flaky tests (configurable via retry_flaky_tests)
- Add pushWithRetry with exponential backoff for transient push failures
- Add label support to beads UpdateOptions (AddLabels, RemoveLabels, SetLabels)

Failure actions by type:
- conflict: needs-rebase label, assign to worker
- tests_fail/build_fail: needs-fix label, assign to worker
- flaky_test: retry once, then treat as tests_fail
- push_fail: retry with backoff, needs-retry label

Closes gt-3x1.4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-19 14:48:30 -08:00
parent 8696413e09
commit 7a6f87ebb7
5 changed files with 466 additions and 78 deletions

View File

@@ -239,6 +239,59 @@ func (mr *MergeRequest) IsClosed() bool {
return mr.Status == MRClosed
}
// FailureType categorizes merge failures for appropriate handling.
type FailureType string
const (
// FailureNone indicates no failure (success).
FailureNone FailureType = ""
// FailureConflict indicates merge conflicts with target branch.
FailureConflict FailureType = "conflict"
// FailureTestsFail indicates tests failed after merge.
FailureTestsFail FailureType = "tests_fail"
// FailureBuildFail indicates build failed after merge.
FailureBuildFail FailureType = "build_fail"
// FailureFlakyTest indicates a potentially flaky test failure (may retry).
FailureFlakyTest FailureType = "flaky_test"
// FailurePushFail indicates push to remote failed.
FailurePushFail FailureType = "push_fail"
// FailureFetch indicates fetch of source branch failed.
FailureFetch FailureType = "fetch_fail"
// FailureCheckout indicates checkout of target branch failed.
FailureCheckout FailureType = "checkout_fail"
)
// FailureLabel returns the beads label for this failure type.
func (f FailureType) FailureLabel() string {
switch f {
case FailureConflict:
return "needs-rebase"
case FailureTestsFail, FailureBuildFail, FailureFlakyTest:
return "needs-fix"
case FailurePushFail:
return "needs-retry"
default:
return ""
}
}
// ShouldAssignToWorker returns true if this failure should be assigned back to the worker.
func (f FailureType) ShouldAssignToWorker() bool {
switch f {
case FailureConflict, FailureTestsFail, FailureBuildFail, FailureFlakyTest:
return true
default:
return false
}
}
// IsOpen returns true if the MR is in an open state (waiting for processing).
func (mr *MergeRequest) IsOpen() bool {
return mr.Status == MROpen