fix: honor rig agent when starting witness/refinery
This commit is contained in:
@@ -371,7 +371,7 @@ func ensureRefinerySession(rigName string, r *rig.Rig) (bool, error) {
|
||||
|
||||
// Launch Claude directly (no respawn loop - daemon handles restart)
|
||||
// Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes
|
||||
if err := t.SendKeys(sessionName, config.BuildAgentStartupCommand("refinery", bdActor, "", "")); err != nil {
|
||||
if err := t.SendKeys(sessionName, config.BuildAgentStartupCommand("refinery", bdActor, r.Path, "")); err != nil {
|
||||
return false, fmt.Errorf("sending command: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -1118,6 +1118,53 @@ func TestBuildCrewStartupCommandWithAgentOverride(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildStartupCommand_UsesRigAgentWhenRigPathProvided(t *testing.T) {
|
||||
townRoot := t.TempDir()
|
||||
rigPath := filepath.Join(townRoot, "testrig")
|
||||
|
||||
townSettings := NewTownSettings()
|
||||
townSettings.DefaultAgent = "gemini"
|
||||
if err := SaveTownSettings(TownSettingsPath(townRoot), townSettings); err != nil {
|
||||
t.Fatalf("SaveTownSettings: %v", err)
|
||||
}
|
||||
|
||||
rigSettings := NewRigSettings()
|
||||
rigSettings.Agent = "codex"
|
||||
if err := SaveRigSettings(RigSettingsPath(rigPath), rigSettings); err != nil {
|
||||
t.Fatalf("SaveRigSettings: %v", err)
|
||||
}
|
||||
|
||||
cmd := BuildStartupCommand(map[string]string{"GT_ROLE": "witness"}, rigPath, "")
|
||||
if !strings.Contains(cmd, "codex") {
|
||||
t.Fatalf("expected rig agent (codex) in command: %q", cmd)
|
||||
}
|
||||
if strings.Contains(cmd, "gemini --approval-mode yolo") {
|
||||
t.Fatalf("did not expect town default agent in command: %q", cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRuntimeCommand_UsesRigAgentWhenRigPathProvided(t *testing.T) {
|
||||
townRoot := t.TempDir()
|
||||
rigPath := filepath.Join(townRoot, "testrig")
|
||||
|
||||
townSettings := NewTownSettings()
|
||||
townSettings.DefaultAgent = "gemini"
|
||||
if err := SaveTownSettings(TownSettingsPath(townRoot), townSettings); err != nil {
|
||||
t.Fatalf("SaveTownSettings: %v", err)
|
||||
}
|
||||
|
||||
rigSettings := NewRigSettings()
|
||||
rigSettings.Agent = "codex"
|
||||
if err := SaveRigSettings(RigSettingsPath(rigPath), rigSettings); err != nil {
|
||||
t.Fatalf("SaveRigSettings: %v", err)
|
||||
}
|
||||
|
||||
cmd := GetRuntimeCommand(rigPath)
|
||||
if !strings.HasPrefix(cmd, "codex") {
|
||||
t.Fatalf("GetRuntimeCommand() = %q, want prefix %q", cmd, "codex")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRuntimeConfigFromSettings(t *testing.T) {
|
||||
// Create temp rig with custom runtime config
|
||||
dir := t.TempDir()
|
||||
|
||||
@@ -211,8 +211,8 @@ func (d *Daemon) executeLifecycleAction(request *LifecycleRequest) error {
|
||||
// ParsedIdentity holds the components extracted from an agent identity string.
|
||||
// This is used to look up the appropriate role bead for lifecycle config.
|
||||
type ParsedIdentity struct {
|
||||
RoleType string // mayor, deacon, witness, refinery, crew, polecat
|
||||
RigName string // Empty for town-level agents (mayor, deacon)
|
||||
RoleType string // mayor, deacon, witness, refinery, crew, polecat
|
||||
RigName string // Empty for town-level agents (mayor, deacon)
|
||||
AgentName string // Empty for singletons (mayor, deacon, witness, refinery)
|
||||
}
|
||||
|
||||
@@ -436,12 +436,17 @@ func (d *Daemon) getStartCommand(roleConfig *beads.RoleConfig, parsed *ParsedIde
|
||||
return beads.ExpandRolePattern(roleConfig.StartCommand, d.config.TownRoot, parsed.RigName, parsed.AgentName, parsed.RoleType)
|
||||
}
|
||||
|
||||
rigPath := ""
|
||||
if parsed != nil && parsed.RigName != "" {
|
||||
rigPath = filepath.Join(d.config.TownRoot, parsed.RigName)
|
||||
}
|
||||
|
||||
// Default command for all agents - use runtime config
|
||||
defaultCmd := "exec " + config.GetRuntimeCommand("")
|
||||
defaultCmd := "exec " + config.GetRuntimeCommand(rigPath)
|
||||
|
||||
// Polecats need environment variables set in the command
|
||||
if parsed.RoleType == "polecat" {
|
||||
return config.BuildPolecatStartupCommand(parsed.RigName, parsed.AgentName, "", "")
|
||||
return config.BuildPolecatStartupCommand(parsed.RigName, parsed.AgentName, rigPath, "")
|
||||
}
|
||||
|
||||
return defaultCmd
|
||||
@@ -572,7 +577,7 @@ func (d *Daemon) getAgentBeadInfo(agentBeadID string) (*AgentBeadInfo, error) {
|
||||
Type string `json:"issue_type"`
|
||||
Description string `json:"description"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
HookBead string `json:"hook_bead"` // Read from database column
|
||||
HookBead string `json:"hook_bead"` // Read from database column
|
||||
AgentState string `json:"agent_state"` // Read from database column
|
||||
}
|
||||
|
||||
@@ -913,7 +918,7 @@ func (d *Daemon) getDeadAgents() []deadAgentInfo {
|
||||
var agents []struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"issue_type"`
|
||||
HookBead string `json:"hook_bead"` // Read from database column
|
||||
HookBead string `json:"hook_bead"` // Read from database column
|
||||
AgentState string `json:"agent_state"` // Read from database column
|
||||
}
|
||||
|
||||
@@ -972,4 +977,3 @@ Action needed: Either restart the agent or reassign the work.`,
|
||||
d.logger.Printf("Notified %s of orphaned work for %s", witnessAddr, agentID)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ import (
|
||||
|
||||
// Common errors
|
||||
var (
|
||||
ErrNotRunning = errors.New("refinery not running")
|
||||
ErrNotRunning = errors.New("refinery not running")
|
||||
ErrAlreadyRunning = errors.New("refinery already running")
|
||||
ErrNoQueue = errors.New("no items in queue")
|
||||
ErrNoQueue = errors.New("no items in queue")
|
||||
)
|
||||
|
||||
// Manager handles refinery lifecycle and queue operations.
|
||||
@@ -205,7 +205,7 @@ func (m *Manager) Start(foreground bool) error {
|
||||
// NOTE: No gt prime injection needed - SessionStart hook handles it automatically
|
||||
// Restarts are handled by daemon via LIFECYCLE mail, not shell loops
|
||||
// Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes
|
||||
command := config.BuildAgentStartupCommand("refinery", bdActor, "", "")
|
||||
command := config.BuildAgentStartupCommand("refinery", bdActor, m.rig.Path, "")
|
||||
if err := t.SendKeys(sessionID, command); err != nil {
|
||||
// Clean up the session on failure (best-effort cleanup)
|
||||
_ = t.KillSession(sessionID)
|
||||
@@ -566,7 +566,6 @@ func (m *Manager) pushWithRetry(targetBranch string, config MergeConfig) error {
|
||||
return fmt.Errorf("push failed after %d retries: %v", config.PushRetryCount, lastErr)
|
||||
}
|
||||
|
||||
|
||||
// formatAge formats a duration since the given time.
|
||||
func formatAge(t time.Time) string {
|
||||
d := time.Since(t)
|
||||
@@ -587,8 +586,8 @@ func formatAge(t time.Time) string {
|
||||
func (m *Manager) notifyWorkerConflict(mr *MergeRequest) {
|
||||
router := mail.NewRouter(m.workDir)
|
||||
msg := &mail.Message{
|
||||
From: fmt.Sprintf("%s/refinery", m.rig.Name),
|
||||
To: fmt.Sprintf("%s/%s", m.rig.Name, mr.Worker),
|
||||
From: fmt.Sprintf("%s/refinery", m.rig.Name),
|
||||
To: fmt.Sprintf("%s/%s", m.rig.Name, mr.Worker),
|
||||
Subject: "Merge conflict - rebase required",
|
||||
Body: fmt.Sprintf(`Your branch %s has conflicts with %s.
|
||||
|
||||
@@ -608,8 +607,8 @@ Then the Refinery will retry the merge.`,
|
||||
func (m *Manager) notifyWorkerMerged(mr *MergeRequest) {
|
||||
router := mail.NewRouter(m.workDir)
|
||||
msg := &mail.Message{
|
||||
From: fmt.Sprintf("%s/refinery", m.rig.Name),
|
||||
To: fmt.Sprintf("%s/%s", m.rig.Name, mr.Worker),
|
||||
From: fmt.Sprintf("%s/refinery", m.rig.Name),
|
||||
To: fmt.Sprintf("%s/%s", m.rig.Name, mr.Worker),
|
||||
Subject: "Work merged successfully",
|
||||
Body: fmt.Sprintf(`Your branch %s has been merged to %s.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user