Skip to content

Commit 7084e37

Browse files
steveyeggeclaude
andcommitted
fix: refinery PostMerge uses ForceCloseWithReason for source issue (GH #2321)
The refinery PostMerge and HandleMRInfoSuccess methods used plain Close/CloseWithReason to close the source task bead after merge. This failed when the source issue had an attached molecule (wisp) with open steps, since bd close respects dependency checks. Fix: use ForceCloseWithReason (matching how gt done handles closures) to bypass dependency checks, and add a close reason for audit trail. Also handle the already-closed case gracefully since the polecat gt done may have already closed the issue. Additionally fix pre-existing test compilation errors where CreateOptions used the deprecated Label field instead of Labels. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5a5deaa commit 7084e37

3 files changed

Lines changed: 30 additions & 13 deletions

File tree

internal/refinery/engineer.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -939,11 +939,19 @@ func (e *Engineer) HandleMRInfoSuccess(mr *MRInfo, result ProcessResult) {
939939
}
940940
}
941941

942-
// 1. Close source issue with reference to MR
942+
// 1. Close source issue with reference to MR.
943+
// Use ForceCloseWithReason to bypass dependency checks — the source issue
944+
// may have an attached molecule (wisp) whose open steps would block a
945+
// normal close. This matches how gt done handles closures.
943946
if mr.SourceIssue != "" {
944947
closeReason := fmt.Sprintf("Merged in %s", mr.ID)
945-
if err := e.beads.CloseWithReason(closeReason, mr.SourceIssue); err != nil {
946-
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to close source issue %s: %v\n", mr.SourceIssue, err)
948+
if err := e.beads.ForceCloseWithReason(closeReason, mr.SourceIssue); err != nil {
949+
// Check if already closed (by polecat's gt done) — that's fine
950+
if issue, showErr := e.beads.Show(mr.SourceIssue); showErr == nil && beads.IssueStatus(issue.Status).IsTerminal() {
951+
_, _ = fmt.Fprintf(e.output, "[Engineer] Source issue already closed: %s\n", mr.SourceIssue)
952+
} else {
953+
_, _ = fmt.Fprintf(e.output, "[Engineer] Warning: failed to close source issue %s: %v\n", mr.SourceIssue, err)
954+
}
947955
} else {
948956
_, _ = fmt.Fprintf(e.output, "[Engineer] Closed source issue: %s\n", mr.SourceIssue)
949957
}

internal/refinery/manager.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -540,12 +540,21 @@ func (m *Manager) PostMerge(idOrBranch string) (*PostMergeResult, error) {
540540
result.MRClosed = true
541541
}
542542

543-
// Close the source issue
543+
// Close the source issue with reason and --force to bypass dependency checks.
544+
// The source issue may have an attached molecule (wisp) whose open steps
545+
// would block a normal bd close. ForceCloseWithReason bypasses this,
546+
// matching how gt done handles closures for the no-MR path.
544547
if mr.IssueID != "" {
545-
if err := b.Close(mr.IssueID); err != nil {
546-
// Source issue may already be closed or not exist
547-
_, _ = fmt.Fprintf(m.output, " %s source issue close: %v\n", style.Dim.Render("○"), err)
548-
result.SourceIssueNotFound = true
548+
closeReason := fmt.Sprintf("Merged in %s", mr.ID)
549+
if err := b.ForceCloseWithReason(closeReason, mr.IssueID); err != nil {
550+
// Check if already closed (by polecat's gt done) — that's fine
551+
if issue, showErr := b.Show(mr.IssueID); showErr == nil && beads.IssueStatus(issue.Status).IsTerminal() {
552+
_, _ = fmt.Fprintf(m.output, " %s source issue already closed: %s\n", style.Dim.Render("○"), mr.IssueID)
553+
result.SourceIssueClosed = true
554+
} else {
555+
_, _ = fmt.Fprintf(m.output, " %s source issue close: %v\n", style.Dim.Render("○"), err)
556+
result.SourceIssueNotFound = true
557+
}
549558
} else {
550559
result.SourceIssueClosed = true
551560
}

internal/refinery/manager_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,14 @@ func TestManager_Queue_FiltersClosedMergeRequests(t *testing.T) {
111111

112112
openIssue, err := b.Create(beads.CreateOptions{
113113
Title: "Open MR",
114-
Label: "gt:merge-request",
114+
Labels: []string{"gt:merge-request"},
115115
})
116116
if err != nil {
117117
t.Fatalf("create open merge-request issue: %v", err)
118118
}
119119
closedIssue, err := b.Create(beads.CreateOptions{
120120
Title: "Closed MR",
121-
Label: "gt:merge-request",
121+
Labels: []string{"gt:merge-request"},
122122
})
123123
if err != nil {
124124
t.Fatalf("create closed merge-request issue: %v", err)
@@ -227,7 +227,7 @@ func TestManager_PostMerge_ClosesMRAndSourceIssue(t *testing.T) {
227227
// Create a source issue
228228
srcIssue, err := b.Create(beads.CreateOptions{
229229
Title: "Implement feature X",
230-
Label: "gt:task",
230+
Labels: []string{"gt:task"},
231231
})
232232
if err != nil {
233233
t.Fatalf("create source issue: %v", err)
@@ -237,7 +237,7 @@ func TestManager_PostMerge_ClosesMRAndSourceIssue(t *testing.T) {
237237
mrDesc := "branch: polecat/test/gt-xyz\nsource_issue: " + srcIssue.ID + "\nworker: test\ntarget: main"
238238
mrIssue, err := b.Create(beads.CreateOptions{
239239
Title: "MR for feature X",
240-
Label: "gt:merge-request",
240+
Labels: []string{"gt:merge-request"},
241241
Description: mrDesc,
242242
})
243243
if err != nil {
@@ -277,7 +277,7 @@ func TestManager_PostMerge_AlreadyClosedMR(t *testing.T) {
277277
// Create and close an MR bead
278278
mrIssue, err := b.Create(beads.CreateOptions{
279279
Title: "Already merged MR",
280-
Label: "gt:merge-request",
280+
Labels: []string{"gt:merge-request"},
281281
Description: "branch: polecat/old/gt-old\ntarget: main",
282282
})
283283
if err != nil {

0 commit comments

Comments
 (0)