fix: spawn and swarm bug fixes from MVP testing

- spawn.go: Parse bd show --json as array, fix issue_type json tag
- session/manager.go: Check filesystem directly in hasPolecat()
  to handle newly-created polecats
- swarm/manager.go: Use bd show to get children via dependents field
  instead of non-existent bd list --parent flag

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-16 22:07:13 -08:00
parent 59921d52c8
commit e1c041f3b0
3 changed files with 41 additions and 20 deletions

View File

@@ -60,7 +60,7 @@ type BeadsIssue struct {
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Priority int `json:"priority"` Priority int `json:"priority"`
Type string `json:"type"` Type string `json:"issue_type"`
Status string `json:"status"` Status string `json:"status"`
} }
@@ -256,12 +256,16 @@ func fetchBeadsIssue(rigPath, issueID string) (*BeadsIssue, error) {
return nil, err return nil, err
} }
var issue BeadsIssue // bd show --json returns an array, take the first element
if err := json.Unmarshal(stdout.Bytes(), &issue); err != nil { var issues []BeadsIssue
if err := json.Unmarshal(stdout.Bytes(), &issues); err != nil {
return nil, fmt.Errorf("parsing issue: %w", err) return nil, fmt.Errorf("parsing issue: %w", err)
} }
if len(issues) == 0 {
return nil, fmt.Errorf("issue not found: %s", issueID)
}
return &issue, nil return &issues[0], nil
} }
// buildSpawnContext creates the initial context message for the polecat. // buildSpawnContext creates the initial context message for the polecat.

View File

@@ -4,6 +4,7 @@ package session
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
@@ -72,12 +73,13 @@ func (m *Manager) polecatDir(polecat string) string {
// hasPolecat checks if the polecat exists in this rig. // hasPolecat checks if the polecat exists in this rig.
func (m *Manager) hasPolecat(polecat string) bool { func (m *Manager) hasPolecat(polecat string) bool {
for _, p := range m.rig.Polecats { // Check filesystem directly to handle newly-created polecats
if p == polecat { polecatPath := m.polecatDir(polecat)
return true info, err := os.Stat(polecatPath)
} if err != nil {
return false
} }
return false return info.IsDir()
} }
// Start creates and starts a new session for a polecat. // Start creates and starts a new session for a polecat.

View File

@@ -278,8 +278,8 @@ func isValidTransition(from, to SwarmState) bool {
// loadTasksFromBeads loads child issues from beads CLI. // loadTasksFromBeads loads child issues from beads CLI.
func (m *Manager) loadTasksFromBeads(epicID string) ([]SwarmTask, error) { func (m *Manager) loadTasksFromBeads(epicID string) ([]SwarmTask, error) {
// Run: bd list --parent <epicID> --json // Run: bd show <epicID> --json to get epic with children
cmd := exec.Command("bd", "list", "--parent", epicID, "--json") cmd := exec.Command("bd", "show", epicID, "--json")
cmd.Dir = m.workDir cmd.Dir = m.workDir
var stdout, stderr bytes.Buffer var stdout, stderr bytes.Buffer
@@ -287,24 +287,39 @@ func (m *Manager) loadTasksFromBeads(epicID string) ([]SwarmTask, error) {
cmd.Stderr = &stderr cmd.Stderr = &stderr
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
return nil, fmt.Errorf("bd list: %s", strings.TrimSpace(stderr.String())) return nil, fmt.Errorf("bd show: %s", strings.TrimSpace(stderr.String()))
} }
// Parse JSON output // Parse JSON output - bd show returns an array
var issues []struct { var issues []struct {
ID string `json:"id"` ID string `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Status string `json:"status"` Status string `json:"status"`
Dependents []struct {
ID string `json:"id"`
Title string `json:"title"`
Status string `json:"status"`
DependencyType string `json:"dependency_type"`
} `json:"dependents"`
} }
if err := json.Unmarshal(stdout.Bytes(), &issues); err != nil { if err := json.Unmarshal(stdout.Bytes(), &issues); err != nil {
return nil, fmt.Errorf("parsing bd output: %w", err) return nil, fmt.Errorf("parsing bd output: %w", err)
} }
if len(issues) == 0 {
return nil, fmt.Errorf("epic not found: %s", epicID)
}
// Extract parent-child dependents as tasks
var tasks []SwarmTask var tasks []SwarmTask
for _, issue := range issues { for _, dep := range issues[0].Dependents {
if dep.DependencyType != "parent-child" {
continue
}
state := TaskPending state := TaskPending
switch issue.Status { switch dep.Status {
case "in_progress": case "in_progress":
state = TaskInProgress state = TaskInProgress
case "closed": case "closed":
@@ -312,8 +327,8 @@ func (m *Manager) loadTasksFromBeads(epicID string) ([]SwarmTask, error) {
} }
tasks = append(tasks, SwarmTask{ tasks = append(tasks, SwarmTask{
IssueID: issue.ID, IssueID: dep.ID,
Title: issue.Title, Title: dep.Title,
State: state, State: state,
}) })
} }