refactor(swarm): remove idle polecat reuse logic (self-cleaning model)

The swarm dispatch command now always spawns fresh polecats instead of
searching for idle ones to reuse. With the self-cleaning model, polecats
self-nuke when done - there are no idle polecats to reuse.

Closes: gt-h4yc3

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
mayor
2026-01-10 22:45:56 -08:00
committed by Steve Yegge
parent 5607bc4f01
commit 1bed63f087

View File

@@ -131,11 +131,12 @@ Transitions the swarm from 'created' to 'active' state.`,
var swarmDispatchCmd = &cobra.Command{
Use: "dispatch <epic-id>",
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
}