Refinery startup: auto-bond mol-refinery-patrol on start

Add outputRefineryPatrolContext to gt prime that automatically spawns
the refinery patrol molecule when no active patrol is found. This ensures
the merge queue is always monitored when Refinery starts up.

Key changes:
- Add RoleRefinery to outputMoleculeContext
- Implement outputRefineryPatrolContext with auto-spawn logic
- Check for existing in-progress or open patrol molecules
- Spawn mol-refinery-patrol wisp if none found

(gt-j6s8)

🤖 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 00:10:39 -08:00
parent 1832ee321c
commit ef1f5ac2f3

View File

@@ -550,8 +550,8 @@ func outputAttachmentStatus(ctx RoleContext) {
// outputMoleculeContext checks if the agent is working on a molecule step and shows progress.
func outputMoleculeContext(ctx RoleContext) {
// Applies to polecats, crew workers, and deacon
if ctx.Role != RolePolecat && ctx.Role != RoleCrew && ctx.Role != RoleDeacon {
// Applies to polecats, crew workers, deacon, and refinery
if ctx.Role != RolePolecat && ctx.Role != RoleCrew && ctx.Role != RoleDeacon && ctx.Role != RoleRefinery {
return
}
@@ -561,6 +561,12 @@ func outputMoleculeContext(ctx RoleContext) {
return
}
// For Refinery, use special patrol molecule handling (auto-bonds on startup)
if ctx.Role == RoleRefinery {
outputRefineryPatrolContext(ctx)
return
}
// Check for in-progress issues
b := beads.New(ctx.WorkDir)
issues, err := b.List(beads.ListOptions{
@@ -750,6 +756,171 @@ func outputDeaconPatrolContext(ctx RoleContext) {
fmt.Println(" - Loop back to spawn new wisp, or exit if context high")
}
// outputRefineryPatrolContext shows patrol molecule status for the Refinery.
// Unlike other patrol roles, Refinery AUTO-BONDS its patrol molecule on startup
// if one isn't already running. This ensures the merge queue is always monitored.
func outputRefineryPatrolContext(ctx RoleContext) {
fmt.Println()
fmt.Printf("%s\n\n", style.Bold.Render("## 🔧 Refinery Patrol Status"))
// Refinery works from its own rig clone: <rig>/refinery/rig/
// Beads are in the current WorkDir
refineryBeadsDir := ctx.WorkDir
// Find mol-refinery-patrol molecules (exclude template)
// Look for in-progress patrol first (resumable)
cmdList := exec.Command("bd", "--no-daemon", "list", "--status=in_progress", "--type=epic")
cmdList.Dir = refineryBeadsDir
var stdoutList bytes.Buffer
cmdList.Stdout = &stdoutList
cmdList.Stderr = nil
errList := cmdList.Run()
hasPatrol := false
var patrolID string
var patrolLine string
if errList == nil {
lines := strings.Split(stdoutList.String(), "\n")
for _, line := range lines {
if strings.Contains(line, "mol-refinery-patrol") && !strings.Contains(line, "[template]") {
parts := strings.Fields(line)
if len(parts) > 0 {
patrolID = parts[0]
patrolLine = line
hasPatrol = true
break
}
}
}
}
// Also check for open patrols with open children (active wisp)
if !hasPatrol {
cmdOpen := exec.Command("bd", "--no-daemon", "list", "--status=open", "--type=epic")
cmdOpen.Dir = refineryBeadsDir
var stdoutOpen bytes.Buffer
cmdOpen.Stdout = &stdoutOpen
cmdOpen.Stderr = nil
if cmdOpen.Run() == nil {
lines := strings.Split(stdoutOpen.String(), "\n")
for _, line := range lines {
if strings.Contains(line, "mol-refinery-patrol") && !strings.Contains(line, "[template]") {
parts := strings.Fields(line)
if len(parts) > 0 {
molID := parts[0]
// Check if this molecule has open children
cmdShow := exec.Command("bd", "--no-daemon", "show", molID)
cmdShow.Dir = refineryBeadsDir
var stdoutShow bytes.Buffer
cmdShow.Stdout = &stdoutShow
cmdShow.Stderr = nil
if cmdShow.Run() == nil {
showOutput := stdoutShow.String()
if strings.Contains(showOutput, "- open]") || strings.Contains(showOutput, "- in_progress]") {
hasPatrol = true
patrolID = molID
patrolLine = line
break
}
}
}
}
}
}
}
if !hasPatrol {
// No active patrol - AUTO-SPAWN one
fmt.Println("Status: **No active patrol** - spawning mol-refinery-patrol...")
fmt.Println()
// Find the proto ID for mol-refinery-patrol
cmdCatalog := exec.Command("bd", "--no-daemon", "mol", "catalog")
cmdCatalog.Dir = refineryBeadsDir
var stdoutCatalog bytes.Buffer
cmdCatalog.Stdout = &stdoutCatalog
cmdCatalog.Stderr = nil
if cmdCatalog.Run() != nil {
fmt.Println(style.Dim.Render("Failed to list molecule catalog. Run `bd mol catalog` to troubleshoot."))
return
}
// Find mol-refinery-patrol in catalog
var protoID string
catalogLines := strings.Split(stdoutCatalog.String(), "\n")
for _, line := range catalogLines {
if strings.Contains(line, "mol-refinery-patrol") {
parts := strings.Fields(line)
if len(parts) > 0 {
protoID = parts[0]
break
}
}
}
if protoID == "" {
fmt.Println(style.Dim.Render("Proto mol-refinery-patrol not found in catalog. Run `bd mol register` first."))
return
}
// Spawn the wisp (default spawn creates wisp)
cmdSpawn := exec.Command("bd", "--no-daemon", "mol", "spawn", protoID, "--assignee", ctx.Rig+"/refinery")
cmdSpawn.Dir = refineryBeadsDir
var stdoutSpawn, stderrSpawn bytes.Buffer
cmdSpawn.Stdout = &stdoutSpawn
cmdSpawn.Stderr = &stderrSpawn
if err := cmdSpawn.Run(); err != nil {
fmt.Printf("Failed to spawn patrol: %s\n", stderrSpawn.String())
fmt.Println(style.Dim.Render("Run manually: bd --no-daemon mol spawn " + protoID))
return
}
// Parse the spawned molecule ID from output
spawnOutput := stdoutSpawn.String()
fmt.Printf("✓ Spawned patrol molecule\n")
// Extract molecule ID from spawn output (format: "Created molecule: gt-xxxx")
for _, line := range strings.Split(spawnOutput, "\n") {
if strings.Contains(line, "molecule:") || strings.Contains(line, "Created") {
parts := strings.Fields(line)
for _, p := range parts {
if strings.HasPrefix(p, "gt-") {
patrolID = p
break
}
}
}
}
if patrolID != "" {
fmt.Printf("Patrol ID: %s\n\n", patrolID)
}
} else {
// Has active patrol - show status
fmt.Println("Status: **Patrol Active**")
fmt.Printf("Patrol: %s\n\n", strings.TrimSpace(patrolLine))
}
// Show patrol work loop instructions
fmt.Println("**Refinery Patrol Work Loop:**")
fmt.Println("1. Check inbox: `gt mail inbox`")
fmt.Println("2. Check next step: `bd ready`")
fmt.Println("3. Execute the step (queue scan, process branch, tests, merge)")
fmt.Println("4. Close step: `bd close <step-id>`")
fmt.Println("5. Check next: `bd ready`")
fmt.Println("6. At cycle end (burn-or-loop step):")
fmt.Println(" - Generate summary of patrol cycle")
fmt.Println(" - Squash: `bd --no-daemon mol squash <mol-id> --summary \"<summary>\"`")
fmt.Println(" - Loop back to spawn new wisp, or exit if context high")
if patrolID != "" {
fmt.Println()
fmt.Printf("Current patrol ID: %s\n", patrolID)
}
}
// acquireIdentityLock checks and acquires the identity lock for worker roles.
// This prevents multiple agents from claiming the same worker identity.
// Returns an error if another agent already owns this identity.