feat(handoff): Auto-submit MR to merge queue when polecat shuts down
When a polecat runs `gt handoff` (default: shutdown action), the current branch is now automatically submitted to the merge queue if it follows the polecat/<name>/<issue> naming convention. This streamlines the polecat workflow: - Work on assigned issue - Commit changes - Run `gt handoff` (automatically submits MR + retires) The Refinery will process the merge request. If MR submission fails, a warning is printed but handoff continues. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,14 +31,21 @@ var handoffCmd = &cobra.Command{
|
||||
|
||||
This command initiates graceful retirement:
|
||||
1. Verifies git state is clean
|
||||
2. Sends handoff mail to yourself (for cycle)
|
||||
3. Sends lifecycle request to your manager
|
||||
4. Sets requesting state and waits for retirement
|
||||
2. For polecats (shutdown): auto-submits MR to merge queue
|
||||
3. Sends handoff mail to yourself (for cycle)
|
||||
4. Sends lifecycle request to your manager
|
||||
5. Sets requesting state and waits for retirement
|
||||
|
||||
Your manager (daemon for Mayor/Witness, witness for polecats) will
|
||||
verify the request and terminate your session. For cycle/restart,
|
||||
a new session starts and reads your handoff mail to continue work.
|
||||
|
||||
Polecat auto-MR:
|
||||
When a polecat runs 'gt handoff' (default: shutdown), the current branch
|
||||
is automatically submitted to the merge queue if it follows the
|
||||
polecat/<name>/<issue> naming convention. The Refinery will process
|
||||
the merge request.
|
||||
|
||||
Flags:
|
||||
--cycle Restart with handoff mail (default for Mayor/Witness)
|
||||
--restart Fresh restart, no handoff context
|
||||
@@ -96,6 +103,17 @@ func runHandoff(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// For polecats shutting down with work complete, auto-submit MR to merge queue
|
||||
if role == RolePolecat && action == HandoffShutdown {
|
||||
if err := submitMRForPolecat(); err != nil {
|
||||
// Non-fatal: warn but continue with handoff
|
||||
fmt.Printf("%s Could not auto-submit MR: %v\n", style.Warning.Render("Warning:"), err)
|
||||
fmt.Println(style.Dim.Render(" You may need to run 'gt mq submit' manually"))
|
||||
} else {
|
||||
fmt.Printf("%s Auto-submitted work to merge queue\n", style.Bold.Render("✓"))
|
||||
}
|
||||
}
|
||||
|
||||
// For cycle, update handoff bead for successor
|
||||
if action == HandoffCycle {
|
||||
if err := sendHandoffMail(role, townRoot); err != nil {
|
||||
@@ -334,6 +352,47 @@ Please verify state and execute lifecycle action.
|
||||
return nil
|
||||
}
|
||||
|
||||
// submitMRForPolecat submits the current branch to the merge queue.
|
||||
// This is called automatically when a polecat shuts down with completed work.
|
||||
func submitMRForPolecat() error {
|
||||
// Check if we're on a polecat branch with work to submit
|
||||
cmd := exec.Command("git", "branch", "--show-current")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting current branch: %w", err)
|
||||
}
|
||||
branch := strings.TrimSpace(string(out))
|
||||
|
||||
// Skip if on main/master (no work to submit)
|
||||
if branch == "main" || branch == "master" || branch == "" {
|
||||
return nil // Nothing to submit, that's OK
|
||||
}
|
||||
|
||||
// Check if branch follows polecat/<name>/<issue> pattern
|
||||
parts := strings.Split(branch, "/")
|
||||
if len(parts) < 3 || parts[0] != "polecat" {
|
||||
// Not a polecat work branch, skip
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run gt mq submit
|
||||
submitCmd := exec.Command("gt", "mq", "submit")
|
||||
submitOutput, err := submitCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s", strings.TrimSpace(string(submitOutput)))
|
||||
}
|
||||
|
||||
// Print the submit output (trimmed)
|
||||
output := strings.TrimSpace(string(submitOutput))
|
||||
if output != "" {
|
||||
for _, line := range strings.Split(output, "\n") {
|
||||
fmt.Printf(" %s\n", line)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setRequestingState updates state.json to indicate we're requesting lifecycle action.
|
||||
func setRequestingState(role Role, action HandoffAction, townRoot string) error {
|
||||
// Determine state file location based on role
|
||||
|
||||
Reference in New Issue
Block a user