fix(sling): auto-attach work molecule and handle dead polecats

Combines three related sling improvements:

1. Auto-attach mol-polecat-work (Issue #288)
   - Automatically attach work molecule when slinging to polecats
   - Ensures polecats have standard guidance molecule attached

2. Fix polecat hook with molecule (Issue #197)
   - Use beads.ResolveHookDir() for correct directory resolution
   - Prevents bd cook from failing in polecat worktree

3. Spawn fresh polecat when target has no session
   - When slinging to a dead polecat, spawn fresh one instead of failing
   - Fixes stale convoys not progressing due to done polecats
This commit is contained in:
mayor
2026-01-13 14:01:49 +01:00
committed by Daniel Sauer
parent 278b2f2d4d
commit 7924921d17
3 changed files with 112 additions and 1 deletions

View File

@@ -6,6 +6,7 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/beads"
@@ -253,7 +254,37 @@ func runSling(cmd *cobra.Command, args []string) error {
var targetWorkDir string
targetAgent, targetPane, targetWorkDir, err = resolveTargetAgent(target)
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)
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)
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)
if err := storeDispatcherInBead(beadID, actor); err != nil {
// Warn but don't fail - polecat will still complete work