diff --git a/internal/cmd/swarm.go b/internal/cmd/swarm.go index abeaff31..4620ae5d 100644 --- a/internal/cmd/swarm.go +++ b/internal/cmd/swarm.go @@ -131,11 +131,12 @@ Transitions the swarm from 'created' to 'active' state.`, var swarmDispatchCmd = &cobra.Command{ Use: "dispatch ", - Short: "Assign next ready task to an idle worker", - Long: `Dispatch the next ready task from an epic to an available worker. + Short: "Assign next ready task to a fresh polecat", + Long: `Dispatch the next ready task from an epic to a new polecat. -Finds the first unassigned task in the epic's ready front and slings it -to an idle polecat in the rig. +Finds the first unassigned task in the epic's ready front and spawns a +fresh polecat to work on it. Self-cleaning model: polecats are always +fresh - there are no idle polecats to reuse. Examples: gt swarm dispatch gt-abc # Dispatch next task from epic gt-abc @@ -458,49 +459,15 @@ func runSwarmDispatch(cmd *cobra.Command, args []string) error { return nil } - // Find idle polecats (no hooked work) - polecatGit := git.NewGit(foundRig.Path) - polecatMgr := polecat.NewManager(foundRig, polecatGit) - polecats, err := polecatMgr.List() - if err != nil { - return fmt.Errorf("listing polecats: %w", err) - } - - // Check which polecats have no hooked work - var idlePolecats []string - for _, p := range polecats { - // Check if polecat has hooked work by querying beads - hookCheckCmd := exec.Command("bd", "list", "--status=hooked", "--assignee", fmt.Sprintf("%s/polecats/%s", foundRig.Name, p.Name), "--json") - hookCheckCmd.Dir = foundRig.BeadsPath() - var hookOut bytes.Buffer - hookCheckCmd.Stdout = &hookOut - if err := hookCheckCmd.Run(); err == nil { - var hooked []interface{} - if err := json.Unmarshal(hookOut.Bytes(), &hooked); err == nil && len(hooked) == 0 { - idlePolecats = append(idlePolecats, p.Name) - } - } - } - - if len(idlePolecats) == 0 { - fmt.Println("No idle polecats available") - fmt.Printf("\nUnassigned ready tasks:\n") - for _, task := range unassigned { - fmt.Printf(" ○ %s: %s\n", task.ID, task.Title) - } - fmt.Printf("\nCreate a new polecat or wait for one to become idle.\n") - return nil - } - - // Dispatch first unassigned task to first idle polecat + // Self-cleaning model: Always spawn fresh polecats for work. + // There are no "idle" polecats - polecats self-nuke when done. + // Just sling to the rig and let gt sling spawn a fresh polecat. task := unassigned[0] - worker := idlePolecats[0] - target := fmt.Sprintf("%s/%s", foundRig.Name, worker) - fmt.Printf("Dispatching %s to %s...\n", task.ID, target) + fmt.Printf("Dispatching %s to fresh polecat in %s...\n", task.ID, foundRig.Name) - // Use gt sling to assign the task - slingCmd := exec.Command("gt", "sling", task.ID, target) + // Use gt sling to spawn a fresh polecat and assign the task + slingCmd := exec.Command("gt", "sling", task.ID, foundRig.Name) slingCmd.Dir = townRoot slingCmd.Stdout = os.Stdout slingCmd.Stderr = os.Stderr @@ -509,15 +476,12 @@ func runSwarmDispatch(cmd *cobra.Command, args []string) error { return fmt.Errorf("slinging task: %w", err) } - fmt.Printf("%s Dispatched %s: %s → %s\n", style.Bold.Render("✓"), task.ID, task.Title, target) + fmt.Printf("%s Dispatched %s: %s → fresh polecat\n", style.Bold.Render("✓"), task.ID, task.Title) - // Show remaining tasks and workers + // Show remaining tasks if len(unassigned) > 1 { fmt.Printf("\n%d more ready tasks available\n", len(unassigned)-1) } - if len(idlePolecats) > 1 { - fmt.Printf("%d more idle polecats available\n", len(idlePolecats)-1) - } return nil }