feat(refinery): implement handleSuccess for merge queue

Implement success handling for the merge queue Engineer:
- Add handleSuccess method that handles successful merge completion
- Update MR body with merge_commit SHA and close_reason
- Close MR with 'merged' reason
- Close source issue with reference to MR ID
- Delete source branch if delete_merged_branches is configured
- Add DeleteRemoteBranch method to git package
- Add git client to Engineer struct
- Add tests for new functionality

Closes gt-3x1.5

🤖 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:13 -08:00
parent e5ce1467e0
commit 349205eda1
4 changed files with 364 additions and 293 deletions

View File

@@ -347,7 +347,7 @@ func (g *Git) RemoteBranchExists(remote, branch string) (bool, error) {
return out != "", nil
}
// DeleteBranch deletes a branch.
// DeleteBranch deletes a local branch.
func (g *Git) DeleteBranch(name string, force bool) error {
flag := "-d"
if force {
@@ -357,6 +357,12 @@ func (g *Git) DeleteBranch(name string, force bool) error {
return err
}
// DeleteRemoteBranch deletes a branch from the remote.
func (g *Git) DeleteRemoteBranch(remote, branch string) error {
_, err := g.run("push", remote, "--delete", branch)
return err
}
// Rev returns the commit hash for the given ref.
func (g *Git) Rev(ref string) (string, error) {
return g.run("rev-parse", ref)

View File

@@ -10,6 +10,7 @@ import (
"time"
"github.com/steveyegge/gastown/internal/beads"
"github.com/steveyegge/gastown/internal/git"
"github.com/steveyegge/gastown/internal/rig"
)
@@ -67,6 +68,7 @@ func DefaultMergeQueueConfig() *MergeQueueConfig {
type Engineer struct {
rig *rig.Rig
beads *beads.Beads
git *git.Git
config *MergeQueueConfig
workDir string
@@ -79,6 +81,7 @@ func NewEngineer(r *rig.Rig) *Engineer {
return &Engineer{
rig: r,
beads: beads.New(r.Path),
git: git.NewGit(r.Path),
config: DefaultMergeQueueConfig(),
workDir: r.Path,
stopCh: make(chan struct{}),
@@ -256,12 +259,7 @@ func (e *Engineer) processOnce(ctx context.Context) error {
// 6. Handle result
if result.Success {
// Close with merged reason
reason := fmt.Sprintf("merged: %s", result.MergeCommit)
if err := e.beads.CloseWithReason(reason, mr.ID); err != nil {
fmt.Printf("[Engineer] Warning: failed to close MR %s: %v\n", mr.ID, err)
}
fmt.Printf("[Engineer] ✓ Merged: %s\n", mr.ID)
e.handleSuccess(mr, result)
} else {
// Failure handling (detailed implementation in gt-3x1.4)
e.handleFailure(mr, result)
@@ -305,6 +303,56 @@ func (e *Engineer) ProcessMR(ctx context.Context, mr *beads.Issue) ProcessResult
}
}
// handleSuccess handles a successful merge completion.
// Steps:
// 1. Update MR with merge_commit SHA
// 2. Close MR with reason 'merged'
// 3. Close source issue with reference to MR
// 4. Delete source branch if configured
// 5. Log success
func (e *Engineer) handleSuccess(mr *beads.Issue, result ProcessResult) {
// Parse MR fields from description
mrFields := beads.ParseMRFields(mr)
if mrFields == nil {
mrFields = &beads.MRFields{}
}
// 1. Update MR with merge_commit SHA
mrFields.MergeCommit = result.MergeCommit
mrFields.CloseReason = "merged"
newDesc := beads.SetMRFields(mr, mrFields)
if err := e.beads.Update(mr.ID, beads.UpdateOptions{Description: &newDesc}); err != nil {
fmt.Printf("[Engineer] Warning: failed to update MR %s with merge commit: %v\n", mr.ID, err)
}
// 2. Close MR with reason 'merged'
if err := e.beads.CloseWithReason("merged", mr.ID); err != nil {
fmt.Printf("[Engineer] Warning: failed to close MR %s: %v\n", mr.ID, err)
}
// 3. Close source issue with reference to MR
if mrFields.SourceIssue != "" {
closeReason := fmt.Sprintf("Merged in %s", mr.ID)
if err := e.beads.CloseWithReason(closeReason, mrFields.SourceIssue); err != nil {
fmt.Printf("[Engineer] Warning: failed to close source issue %s: %v\n", mrFields.SourceIssue, err)
} else {
fmt.Printf("[Engineer] Closed source issue: %s\n", mrFields.SourceIssue)
}
}
// 4. Delete source branch if configured
if e.config.DeleteMergedBranches && mrFields.Branch != "" {
if err := e.git.DeleteRemoteBranch("origin", mrFields.Branch); err != nil {
fmt.Printf("[Engineer] Warning: failed to delete branch %s: %v\n", mrFields.Branch, err)
} else {
fmt.Printf("[Engineer] Deleted branch: %s\n", mrFields.Branch)
}
}
// 5. Log success
fmt.Printf("[Engineer] ✓ Merged: %s (commit: %s)\n", mr.ID, result.MergeCommit)
}
// handleFailure handles a failed merge request.
// This is a placeholder that will be fully implemented in gt-3x1.4.
func (e *Engineer) handleFailure(mr *beads.Issue, result ProcessResult) {

View File

@@ -200,6 +200,9 @@ func TestNewEngineer(t *testing.T) {
if e.beads == nil {
t.Error("expected beads client to be initialized")
}
if e.git == nil {
t.Error("expected git client to be initialized")
}
if e.config == nil {
t.Error("expected config to be initialized with defaults")
}
@@ -207,3 +210,11 @@ func TestNewEngineer(t *testing.T) {
t.Error("expected stopCh to be initialized")
}
}
func TestEngineer_DeleteMergedBranchesConfig(t *testing.T) {
// Test that DeleteMergedBranches is true by default
cfg := DefaultMergeQueueConfig()
if !cfg.DeleteMergedBranches {
t.Error("expected DeleteMergedBranches to be true by default")
}
}