Refinery as worktree: local MR integration (gt-4u5z)

Architecture changes:
- Refinery created as worktree of mayor clone (shares .git)
- Polecat branches stay local (never pushed to origin)
- MRs stored as wisps in .beads-wisp/mq/ (ephemeral)
- Only main gets pushed to origin after merge

New mrqueue package for wisp-based MR storage.
Updated spawn, done, mq_submit, refinery, molecule templates.

🤖 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-23 21:24:42 -08:00
parent e895e51ee4
commit b685879b63
11 changed files with 378 additions and 142 deletions

View File

@@ -3,12 +3,14 @@ package cmd
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/beads"
"github.com/steveyegge/gastown/internal/git"
"github.com/steveyegge/gastown/internal/mail"
"github.com/steveyegge/gastown/internal/mrqueue"
"github.com/steveyegge/gastown/internal/style"
"github.com/steveyegge/gastown/internal/workspace"
)
@@ -144,41 +146,31 @@ func runDone(cmd *cobra.Command, args []string) error {
// Build title
title := fmt.Sprintf("Merge: %s", issueID)
// CRITICAL: Push branch to origin BEFORE creating MR
// Without this, the worktree can be deleted and the branch lost forever
fmt.Printf("Pushing branch to origin...\n")
if err := g.Push("origin", branch, false); err != nil {
return fmt.Errorf("pushing branch to origin: %w", err)
}
fmt.Printf("%s Branch pushed to origin/%s\n", style.Bold.Render("✓"), branch)
// Note: Branch stays local. Refinery sees it via shared .git (worktree).
// Only main gets pushed to origin after merge.
// Build description with MR fields
mrFields := &beads.MRFields{
// Submit to MR queue (wisp storage - ephemeral, not synced)
rigPath := filepath.Join(townRoot, rigName)
queue := mrqueue.New(rigPath)
mr := &mrqueue.MR{
Branch: branch,
Target: target,
SourceIssue: issueID,
Worker: worker,
Rig: rigName,
}
description := beads.FormatMRFields(mrFields)
// Create the merge-request issue
createOpts := beads.CreateOptions{
Title: title,
Type: "merge-request",
Priority: priority,
Description: description,
}
issue, err := bd.Create(createOpts)
if err != nil {
return fmt.Errorf("creating merge request: %w", err)
if err := queue.Submit(mr); err != nil {
return fmt.Errorf("submitting to merge queue: %w", err)
}
mrID = issue.ID
mrID = mr.ID
// Success output
fmt.Printf("%s Work submitted to merge queue\n", style.Bold.Render("✓"))
fmt.Printf(" MR ID: %s\n", style.Bold.Render(issue.ID))
fmt.Printf(" MR ID: %s\n", style.Bold.Render(mr.ID))
fmt.Printf(" Source: %s\n", branch)
fmt.Printf(" Target: %s\n", target)
fmt.Printf(" Issue: %s\n", issueID)

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"
@@ -11,6 +12,7 @@ import (
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/beads"
"github.com/steveyegge/gastown/internal/git"
"github.com/steveyegge/gastown/internal/mrqueue"
"github.com/steveyegge/gastown/internal/style"
"github.com/steveyegge/gastown/internal/workspace"
)
@@ -133,40 +135,30 @@ func runMqSubmit(cmd *cobra.Command, args []string) error {
// Build title
title := fmt.Sprintf("Merge: %s", issueID)
// CRITICAL: Push branch to origin BEFORE creating MR
// Without this, the worktree can be deleted and the branch lost forever
fmt.Printf("Pushing branch to origin...\n")
if err := g.Push("origin", branch, false); err != nil {
return fmt.Errorf("pushing branch to origin: %w", err)
}
fmt.Printf("%s Branch pushed to origin/%s\n", style.Bold.Render("✓"), branch)
// Note: Branch stays local. Refinery sees it via shared .git (worktree).
// Only main gets pushed to origin after merge.
// Build description with MR fields
mrFields := &beads.MRFields{
// Submit to MR queue (wisp storage - ephemeral, not synced)
rigPath := filepath.Join(townRoot, rigName)
queue := mrqueue.New(rigPath)
mr := &mrqueue.MR{
Branch: branch,
Target: target,
SourceIssue: issueID,
Worker: worker,
Rig: rigName,
}
description := beads.FormatMRFields(mrFields)
// Create the merge-request issue
createOpts := beads.CreateOptions{
Title: title,
Type: "merge-request",
Priority: priority,
Description: description,
}
issue, err := bd.Create(createOpts)
if err != nil {
return fmt.Errorf("creating merge request: %w", err)
if err := queue.Submit(mr); err != nil {
return fmt.Errorf("submitting to merge queue: %w", err)
}
// Success output
fmt.Printf("%s Created merge request\n", style.Bold.Render("✓"))
fmt.Printf(" MR ID: %s\n", style.Bold.Render(issue.ID))
fmt.Printf("%s Submitted to merge queue\n", style.Bold.Render("✓"))
fmt.Printf(" MR ID: %s\n", style.Bold.Render(mr.ID))
fmt.Printf(" Source: %s\n", branch)
fmt.Printf(" Target: %s\n", target)
fmt.Printf(" Issue: %s\n", issueID)

View File

@@ -608,8 +608,7 @@ func buildSpawnContext(issue *BeadsIssue, message string) string {
sb.WriteString("2. Work on your task, commit changes regularly\n")
sb.WriteString("3. Run `bd close <issue-id>` when done\n")
sb.WriteString("4. Run `bd sync` to push beads changes\n")
sb.WriteString("5. Push code: `git push origin HEAD`\n")
sb.WriteString("6. Run `gt done` to signal completion\n")
sb.WriteString("5. Run `gt done` to signal completion (branch stays local)\n")
return sb.String()
}
@@ -673,18 +672,16 @@ func buildWorkAssignmentMail(issue *BeadsIssue, message, polecatAddress string,
if moleculeCtx != nil {
body.WriteString("4. Check `bd ready --parent " + moleculeCtx.RootIssueID + "` for more steps\n")
body.WriteString("5. Repeat steps 2-4 for each ready step\n")
body.WriteString("6. When all steps done: run `bd sync`, push code, run `gt done`\n")
body.WriteString("6. When all steps done: run `bd sync`, then `gt done`\n")
} else {
body.WriteString("4. Run `bd sync` to push beads changes\n")
body.WriteString("5. Push code: `git push origin HEAD`\n")
body.WriteString("6. Run `gt done` to signal completion\n")
body.WriteString("5. Run `gt done` to signal completion (branch stays local)\n")
}
body.WriteString("\n## Handoff Protocol\n")
body.WriteString("Before signaling done, ensure:\n")
body.WriteString("- Git status is clean (no uncommitted changes)\n")
body.WriteString("- Issue is closed with `bd close`\n")
body.WriteString("- Beads are synced with `bd sync`\n")
body.WriteString("- Code is pushed to origin\n")
body.WriteString("\nThe `gt done` command verifies these and signals the Witness.\n")
return &mail.Message{