Make gt done MR creation idempotent (gt-svdsy)

Add FindMRForBranch helper to check for existing MR beads before creating.
If an MR already exists for the branch, skip creation and reuse it.
This makes gt done safe to re-run if interrupted mid-execution.

Implements Option C from gt-svdsy: idempotent operations that check
if already done before doing, making it safe to retry.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
gastown/polecats/capable
2025-12-30 22:12:30 -08:00
committed by Steve Yegge
parent 49117c9a48
commit c2a33be4e6
2 changed files with 59 additions and 20 deletions

View File

@@ -153,29 +153,43 @@ func runDone(cmd *cobra.Command, args []string) error {
}
}
// Build MR bead title and description
title := fmt.Sprintf("Merge: %s", issueID)
description := fmt.Sprintf("branch: %s\ntarget: %s\nsource_issue: %s\nrig: %s",
branch, target, issueID, rigName)
if worker != "" {
description += fmt.Sprintf("\nworker: %s", worker)
}
// Create MR bead (ephemeral wisp - will be cleaned up after merge)
mrIssue, err := bd.Create(beads.CreateOptions{
Title: title,
Type: "merge-request",
Priority: priority,
Description: description,
})
// Check if MR bead already exists for this branch (idempotency)
existingMR, err := bd.FindMRForBranch(branch)
if err != nil {
return fmt.Errorf("creating merge request bead: %w", err)
style.PrintWarning("could not check for existing MR: %v", err)
// Continue with creation attempt - Create will fail if duplicate
}
mrID = mrIssue.ID
// Success output
fmt.Printf("%s Work submitted to merge queue\n", style.Bold.Render("✓"))
fmt.Printf(" MR ID: %s\n", style.Bold.Render(mrID))
if existingMR != nil {
// MR already exists - use it instead of creating a new one
mrID = existingMR.ID
fmt.Printf("%s MR already exists (idempotent)\n", style.Bold.Render("✓"))
fmt.Printf(" MR ID: %s\n", style.Bold.Render(mrID))
} else {
// Build MR bead title and description
title := fmt.Sprintf("Merge: %s", issueID)
description := fmt.Sprintf("branch: %s\ntarget: %s\nsource_issue: %s\nrig: %s",
branch, target, issueID, rigName)
if worker != "" {
description += fmt.Sprintf("\nworker: %s", worker)
}
// Create MR bead (ephemeral wisp - will be cleaned up after merge)
mrIssue, err := bd.Create(beads.CreateOptions{
Title: title,
Type: "merge-request",
Priority: priority,
Description: description,
})
if err != nil {
return fmt.Errorf("creating merge request bead: %w", err)
}
mrID = mrIssue.ID
// Success output
fmt.Printf("%s Work submitted to merge queue\n", style.Bold.Render("✓"))
fmt.Printf(" MR ID: %s\n", style.Bold.Render(mrID))
}
fmt.Printf(" Source: %s\n", branch)
fmt.Printf(" Target: %s\n", target)
fmt.Printf(" Issue: %s\n", issueID)