Merge pull request #461 from sauerdaniel/pr/sling-fixes
fix(sling): auto-attach work molecule and handle dead polecats
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/steveyegge/gastown/internal/beads"
|
"github.com/steveyegge/gastown/internal/beads"
|
||||||
@@ -253,7 +254,37 @@ func runSling(cmd *cobra.Command, args []string) error {
|
|||||||
var targetWorkDir string
|
var targetWorkDir string
|
||||||
targetAgent, targetPane, targetWorkDir, err = resolveTargetAgent(target)
|
targetAgent, targetPane, targetWorkDir, err = resolveTargetAgent(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("resolving target: %w", err)
|
// Check if this is a dead polecat (no active session)
|
||||||
|
// If so, spawn a fresh polecat instead of failing
|
||||||
|
if isPolecatTarget(target) {
|
||||||
|
// Extract rig name from polecat target (format: rig/polecats/name)
|
||||||
|
parts := strings.Split(target, "/")
|
||||||
|
if len(parts) >= 3 && parts[1] == "polecats" {
|
||||||
|
rigName := parts[0]
|
||||||
|
fmt.Printf("Target polecat has no active session, spawning fresh polecat in rig '%s'...\n", rigName)
|
||||||
|
spawnOpts := SlingSpawnOptions{
|
||||||
|
Force: slingForce,
|
||||||
|
Account: slingAccount,
|
||||||
|
Create: slingCreate,
|
||||||
|
HookBead: beadID,
|
||||||
|
Agent: slingAgent,
|
||||||
|
}
|
||||||
|
spawnInfo, spawnErr := SpawnPolecatForSling(rigName, spawnOpts)
|
||||||
|
if spawnErr != nil {
|
||||||
|
return fmt.Errorf("spawning polecat to replace dead polecat: %w", spawnErr)
|
||||||
|
}
|
||||||
|
targetAgent = spawnInfo.AgentID()
|
||||||
|
targetPane = spawnInfo.Pane
|
||||||
|
hookWorkDir = spawnInfo.ClonePath
|
||||||
|
|
||||||
|
// Wake witness and refinery to monitor the new polecat
|
||||||
|
wakeRigAgents(rigName)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("resolving target: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("resolving target: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Use target's working directory for bd commands (needed for redirect-based routing)
|
// Use target's working directory for bd commands (needed for redirect-based routing)
|
||||||
if targetWorkDir != "" {
|
if targetWorkDir != "" {
|
||||||
@@ -422,6 +453,15 @@ func runSling(cmd *cobra.Command, args []string) error {
|
|||||||
// Update agent bead's hook_bead field (ZFC: agents track their current work)
|
// Update agent bead's hook_bead field (ZFC: agents track their current work)
|
||||||
updateAgentHookBead(targetAgent, beadID, hookWorkDir, townBeadsDir)
|
updateAgentHookBead(targetAgent, beadID, hookWorkDir, townBeadsDir)
|
||||||
|
|
||||||
|
// Auto-attach mol-polecat-work to polecat agent beads
|
||||||
|
// This ensures polecats have the standard work molecule attached for guidance
|
||||||
|
if strings.Contains(targetAgent, "/polecats/") {
|
||||||
|
if err := attachPolecatWorkMolecule(targetAgent, hookWorkDir, townRoot); err != nil {
|
||||||
|
// Warn but don't fail - polecat will still work without molecule
|
||||||
|
fmt.Printf("%s Could not attach work molecule: %v\n", style.Dim.Render("Warning:"), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Store dispatcher in bead description (enables completion notification to dispatcher)
|
// Store dispatcher in bead description (enables completion notification to dispatcher)
|
||||||
if err := storeDispatcherInBead(beadID, actor); err != nil {
|
if err := storeDispatcherInBead(beadID, actor); err != nil {
|
||||||
// Warn but don't fail - polecat will still complete work
|
// Warn but don't fail - polecat will still complete work
|
||||||
|
|||||||
@@ -111,6 +111,11 @@ func runBatchSling(beadIDs []string, rigName string, townBeadsDir string) error
|
|||||||
// Update agent bead state
|
// Update agent bead state
|
||||||
updateAgentHookBead(targetAgent, beadID, hookWorkDir, townBeadsDir)
|
updateAgentHookBead(targetAgent, beadID, hookWorkDir, townBeadsDir)
|
||||||
|
|
||||||
|
// Auto-attach mol-polecat-work molecule to polecat agent bead
|
||||||
|
if err := attachPolecatWorkMolecule(targetAgent, hookWorkDir, townRoot); err != nil {
|
||||||
|
fmt.Printf(" %s Could not attach work molecule: %v\n", style.Dim.Render("Warning:"), err)
|
||||||
|
}
|
||||||
|
|
||||||
// Store args if provided
|
// Store args if provided
|
||||||
if slingArgs != "" {
|
if slingArgs != "" {
|
||||||
if err := storeArgsInBead(beadID, slingArgs); err != nil {
|
if err := storeArgsInBead(beadID, slingArgs); err != nil {
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/steveyegge/gastown/internal/beads"
|
"github.com/steveyegge/gastown/internal/beads"
|
||||||
|
"github.com/steveyegge/gastown/internal/config"
|
||||||
"github.com/steveyegge/gastown/internal/constants"
|
"github.com/steveyegge/gastown/internal/constants"
|
||||||
|
"github.com/steveyegge/gastown/internal/style"
|
||||||
"github.com/steveyegge/gastown/internal/tmux"
|
"github.com/steveyegge/gastown/internal/tmux"
|
||||||
"github.com/steveyegge/gastown/internal/workspace"
|
"github.com/steveyegge/gastown/internal/workspace"
|
||||||
)
|
)
|
||||||
@@ -373,3 +375,67 @@ func wakeRigAgents(rigName string) {
|
|||||||
_ = t.NudgeSession(witnessSession, "Polecat dispatched - check for work")
|
_ = t.NudgeSession(witnessSession, "Polecat dispatched - check for work")
|
||||||
_ = t.NudgeSession(refinerySession, "Polecat dispatched - check for merge requests")
|
_ = t.NudgeSession(refinerySession, "Polecat dispatched - check for merge requests")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isPolecatTarget checks if the target string refers to a polecat.
|
||||||
|
// Returns true if the target format is "rig/polecats/name".
|
||||||
|
// This is used to determine if we should respawn a dead polecat
|
||||||
|
// instead of failing when slinging work.
|
||||||
|
func isPolecatTarget(target string) bool {
|
||||||
|
parts := strings.Split(target, "/")
|
||||||
|
return len(parts) >= 3 && parts[1] == "polecats"
|
||||||
|
}
|
||||||
|
|
||||||
|
// attachPolecatWorkMolecule attaches the mol-polecat-work molecule to a polecat's agent bead.
|
||||||
|
// This ensures all polecats have the standard work molecule attached for guidance.
|
||||||
|
// The molecule is attached by storing it in the agent bead's description using attachment fields.
|
||||||
|
//
|
||||||
|
// Per issue #288: gt sling should auto-attach mol-polecat-work when slinging to polecats.
|
||||||
|
func attachPolecatWorkMolecule(targetAgent, hookWorkDir, townRoot string) error {
|
||||||
|
// Parse the polecat name from targetAgent (format: "rig/polecats/name")
|
||||||
|
parts := strings.Split(targetAgent, "/")
|
||||||
|
if len(parts) != 3 || parts[1] != "polecats" {
|
||||||
|
return fmt.Errorf("invalid polecat agent format: %s", targetAgent)
|
||||||
|
}
|
||||||
|
rigName := parts[0]
|
||||||
|
polecatName := parts[2]
|
||||||
|
|
||||||
|
// Get the polecat's agent bead ID
|
||||||
|
// Format: "<prefix>-<rig>-polecat-<name>" (e.g., "gt-gastown-polecat-Toast")
|
||||||
|
prefix := config.GetRigPrefix(townRoot, rigName)
|
||||||
|
agentBeadID := beads.PolecatBeadIDWithPrefix(prefix, rigName, polecatName)
|
||||||
|
|
||||||
|
// Resolve the rig directory for running bd commands.
|
||||||
|
// Use ResolveHookDir to ensure we run bd from the correct rig directory
|
||||||
|
// (not from the polecat's worktree, which doesn't have a .beads directory).
|
||||||
|
// This fixes issue #197: polecat fails to hook when slinging with molecule.
|
||||||
|
rigDir := beads.ResolveHookDir(townRoot, prefix+"-"+polecatName, hookWorkDir)
|
||||||
|
|
||||||
|
b := beads.New(rigDir)
|
||||||
|
|
||||||
|
// Check if molecule is already attached (avoid duplicate attach)
|
||||||
|
attachment, err := b.GetAttachment(agentBeadID)
|
||||||
|
if err == nil && attachment != nil && attachment.AttachedMolecule != "" {
|
||||||
|
// Already has a molecule attached - skip
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cook the mol-polecat-work formula to ensure the proto exists
|
||||||
|
// This is safe to run multiple times - cooking is idempotent
|
||||||
|
cookCmd := exec.Command("bd", "--no-daemon", "cook", "mol-polecat-work")
|
||||||
|
cookCmd.Dir = rigDir
|
||||||
|
cookCmd.Stderr = os.Stderr
|
||||||
|
if err := cookCmd.Run(); err != nil {
|
||||||
|
return fmt.Errorf("cooking mol-polecat-work formula: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach the molecule to the polecat's agent bead
|
||||||
|
// The molecule ID is the formula name "mol-polecat-work"
|
||||||
|
moleculeID := "mol-polecat-work"
|
||||||
|
_, err = b.AttachMolecule(agentBeadID, moleculeID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("attaching molecule %s to %s: %w", moleculeID, agentBeadID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s Attached %s to %s\n", style.Bold.Render("✓"), moleculeID, agentBeadID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user