fix(polecat): kill orphan sessions and clear stale hooks during allocation (#448)
ReconcilePool now detects and kills orphan tmux sessions (sessions without corresponding polecat directories). This prevents allocation from being blocked by broken state from crashed polecats. Changes: - Add tmux to Manager to check for orphan sessions during reconciliation - Add ReconcilePoolWith for testable session/directory reconciliation logic - Always clear hook_bead slot when reopening agent beads (fixes stale hooks) - Prune stale git worktree entries during reconciliation Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -330,7 +330,8 @@ func getPolecatManager(rigName string) (*polecat.Manager, *rig.Rig, error) {
|
||||
}
|
||||
|
||||
polecatGit := git.NewGit(r.Path)
|
||||
mgr := polecat.NewManager(r, polecatGit)
|
||||
t := tmux.NewTmux()
|
||||
mgr := polecat.NewManager(r, polecatGit, t)
|
||||
|
||||
return mgr, r, nil
|
||||
}
|
||||
@@ -363,7 +364,7 @@ func runPolecatList(cmd *cobra.Command, args []string) error {
|
||||
|
||||
for _, r := range rigs {
|
||||
polecatGit := git.NewGit(r.Path)
|
||||
mgr := polecat.NewManager(r, polecatGit)
|
||||
mgr := polecat.NewManager(r, polecatGit, t)
|
||||
polecatMgr := polecat.NewSessionManager(t, r)
|
||||
|
||||
polecats, err := mgr.List()
|
||||
|
||||
@@ -221,7 +221,8 @@ func runPolecatIdentityAdd(cmd *cobra.Command, args []string) error {
|
||||
// Generate name if not provided
|
||||
if polecatName == "" {
|
||||
polecatGit := git.NewGit(r.Path)
|
||||
mgr := polecat.NewManager(r, polecatGit)
|
||||
t := tmux.NewTmux()
|
||||
mgr := polecat.NewManager(r, polecatGit, t)
|
||||
polecatName, err = mgr.AllocateName()
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating polecat name: %w", err)
|
||||
@@ -294,7 +295,7 @@ func runPolecatIdentityList(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// Check if worktree exists
|
||||
worktreeExists := false
|
||||
mgr := polecat.NewManager(r, nil)
|
||||
mgr := polecat.NewManager(r, nil, t)
|
||||
if p, err := mgr.Get(name); err == nil && p != nil {
|
||||
worktreeExists = true
|
||||
}
|
||||
@@ -396,7 +397,7 @@ func runPolecatIdentityShow(cmd *cobra.Command, args []string) error {
|
||||
// Check worktree and session
|
||||
t := tmux.NewTmux()
|
||||
polecatMgr := polecat.NewSessionManager(t, r)
|
||||
mgr := polecat.NewManager(r, nil)
|
||||
mgr := polecat.NewManager(r, nil, t)
|
||||
|
||||
worktreeExists := false
|
||||
var clonePath string
|
||||
|
||||
@@ -64,9 +64,10 @@ func SpawnPolecatForSling(rigName string, opts SlingSpawnOptions) (*SpawnedPolec
|
||||
return nil, fmt.Errorf("rig '%s' not found", rigName)
|
||||
}
|
||||
|
||||
// Get polecat manager
|
||||
// Get polecat manager (with tmux for session-aware allocation)
|
||||
polecatGit := git.NewGit(r.Path)
|
||||
polecatMgr := polecat.NewManager(r, polecatGit)
|
||||
t := tmux.NewTmux()
|
||||
polecatMgr := polecat.NewManager(r, polecatGit, t)
|
||||
|
||||
// Allocate a new polecat name
|
||||
polecatName, err := polecatMgr.AllocateName()
|
||||
@@ -124,8 +125,7 @@ func SpawnPolecatForSling(rigName string, opts SlingSpawnOptions) (*SpawnedPolec
|
||||
fmt.Printf("Using account: %s\n", accountHandle)
|
||||
}
|
||||
|
||||
// Start session
|
||||
t := tmux.NewTmux()
|
||||
// Start session (reuse tmux from manager)
|
||||
polecatSessMgr := polecat.NewSessionManager(t, r)
|
||||
|
||||
// Check if already running
|
||||
|
||||
@@ -921,7 +921,7 @@ func runRigShutdown(cmd *cobra.Command, args []string) error {
|
||||
// Check all polecats for uncommitted work (unless nuclear)
|
||||
if !rigShutdownNuclear {
|
||||
polecatGit := git.NewGit(r.Path)
|
||||
polecatMgr := polecat.NewManager(r, polecatGit)
|
||||
polecatMgr := polecat.NewManager(r, polecatGit, nil) // nil tmux: just listing
|
||||
polecats, err := polecatMgr.List()
|
||||
if err == nil && len(polecats) > 0 {
|
||||
var problemPolecats []struct {
|
||||
@@ -1105,7 +1105,7 @@ func runRigStatus(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// Polecats
|
||||
polecatGit := git.NewGit(r.Path)
|
||||
polecatMgr := polecat.NewManager(r, polecatGit)
|
||||
polecatMgr := polecat.NewManager(r, polecatGit, t)
|
||||
polecats, err := polecatMgr.List()
|
||||
fmt.Printf("%s", style.Bold.Render("Polecats"))
|
||||
if err != nil || len(polecats) == 0 {
|
||||
@@ -1198,7 +1198,7 @@ func runRigStop(cmd *cobra.Command, args []string) error {
|
||||
// Check all polecats for uncommitted work (unless nuclear)
|
||||
if !rigStopNuclear {
|
||||
polecatGit := git.NewGit(r.Path)
|
||||
polecatMgr := polecat.NewManager(r, polecatGit)
|
||||
polecatMgr := polecat.NewManager(r, polecatGit, nil) // nil tmux: just listing
|
||||
polecats, err := polecatMgr.List()
|
||||
if err == nil && len(polecats) > 0 {
|
||||
var problemPolecats []struct {
|
||||
@@ -1330,7 +1330,7 @@ func runRigRestart(cmd *cobra.Command, args []string) error {
|
||||
// Check all polecats for uncommitted work (unless nuclear)
|
||||
if !rigRestartNuclear {
|
||||
polecatGit := git.NewGit(r.Path)
|
||||
polecatMgr := polecat.NewManager(r, polecatGit)
|
||||
polecatMgr := polecat.NewManager(r, polecatGit, nil) // nil tmux: just listing
|
||||
polecats, err := polecatMgr.List()
|
||||
if err == nil && len(polecats) > 0 {
|
||||
var problemPolecats []struct {
|
||||
|
||||
@@ -682,7 +682,7 @@ func cleanupPolecats(townRoot string) {
|
||||
|
||||
for _, r := range rigs {
|
||||
polecatGit := git.NewGit(r.Path)
|
||||
polecatMgr := polecat.NewManager(r, polecatGit)
|
||||
polecatMgr := polecat.NewManager(r, polecatGit, nil) // nil tmux: just listing, not allocating
|
||||
|
||||
polecats, err := polecatMgr.List()
|
||||
if err != nil {
|
||||
|
||||
@@ -494,7 +494,7 @@ func spawnSwarmWorkersFromBeads(r *rig.Rig, townRoot string, swarmID string, wor
|
||||
t := tmux.NewTmux()
|
||||
polecatSessMgr := polecat.NewSessionManager(t, r)
|
||||
polecatGit := git.NewGit(r.Path)
|
||||
polecatMgr := polecat.NewManager(r, polecatGit)
|
||||
polecatMgr := polecat.NewManager(r, polecatGit, t)
|
||||
|
||||
// Pair workers with tasks (round-robin if more tasks than workers)
|
||||
workerIdx := 0
|
||||
|
||||
Reference in New Issue
Block a user