feat: add --agent overrides to start/attach

This commit is contained in:
jv
2026-01-07 12:36:35 +13:00
committed by Steve Yegge
parent 3b9ca71fc4
commit 6afd85df4b
7 changed files with 168 additions and 45 deletions
+14 -11
View File
@@ -8,17 +8,18 @@ import (
// Crew command flags // Crew command flags
var ( var (
crewRig string crewRig string
crewBranch bool crewBranch bool
crewJSON bool crewJSON bool
crewForce bool crewForce bool
crewPurge bool crewPurge bool
crewNoTmux bool crewNoTmux bool
crewDetached bool crewDetached bool
crewMessage string crewMessage string
crewAccount string crewAccount string
crewAll bool crewAgentOverride string
crewDryRun bool crewAll bool
crewDryRun bool
) )
var crewCmd = &cobra.Command{ var crewCmd = &cobra.Command{
@@ -328,6 +329,7 @@ func init() {
crewAtCmd.Flags().BoolVar(&crewNoTmux, "no-tmux", false, "Just print directory path") crewAtCmd.Flags().BoolVar(&crewNoTmux, "no-tmux", false, "Just print directory path")
crewAtCmd.Flags().BoolVarP(&crewDetached, "detached", "d", false, "Start session without attaching") crewAtCmd.Flags().BoolVarP(&crewDetached, "detached", "d", false, "Start session without attaching")
crewAtCmd.Flags().StringVar(&crewAccount, "account", "", "Claude Code account handle to use (overrides default)") crewAtCmd.Flags().StringVar(&crewAccount, "account", "", "Claude Code account handle to use (overrides default)")
crewAtCmd.Flags().StringVar(&crewAgentOverride, "agent", "", "Agent alias to run crew worker with (overrides rig/town default)")
crewRemoveCmd.Flags().StringVar(&crewRig, "rig", "", "Rig to use") crewRemoveCmd.Flags().StringVar(&crewRig, "rig", "", "Rig to use")
crewRemoveCmd.Flags().BoolVar(&crewForce, "force", false, "Force remove (skip safety checks)") crewRemoveCmd.Flags().BoolVar(&crewForce, "force", false, "Force remove (skip safety checks)")
@@ -350,6 +352,7 @@ func init() {
crewStartCmd.Flags().BoolVar(&crewAll, "all", false, "Start all crew members in the rig") crewStartCmd.Flags().BoolVar(&crewAll, "all", false, "Start all crew members in the rig")
crewStartCmd.Flags().StringVar(&crewAccount, "account", "", "Claude Code account handle to use") crewStartCmd.Flags().StringVar(&crewAccount, "account", "", "Claude Code account handle to use")
crewStartCmd.Flags().StringVar(&crewAgentOverride, "agent", "", "Agent alias to run crew worker with (overrides rig/town default)")
crewStopCmd.Flags().StringVar(&crewRig, "rig", "", "Rig to use (filter when using --all)") crewStopCmd.Flags().StringVar(&crewRig, "rig", "", "Rig to use (filter when using --all)")
crewStopCmd.Flags().BoolVar(&crewAll, "all", false, "Stop all running crew sessions") crewStopCmd.Flags().BoolVar(&crewAll, "all", false, "Stop all running crew sessions")
+14 -5
View File
@@ -150,8 +150,11 @@ func runCrewAt(cmd *cobra.Command, args []string) error {
// This gives cleaner lifecycle: Claude exits → session ends (no intermediate shell) // This gives cleaner lifecycle: Claude exits → session ends (no intermediate shell)
// Pass "gt prime" as initial prompt so Claude loads context immediately // Pass "gt prime" as initial prompt so Claude loads context immediately
// Export GT_ROLE and BD_ACTOR since tmux SetEnvironment only affects new panes // Export GT_ROLE and BD_ACTOR since tmux SetEnvironment only affects new panes
claudeCmd := config.BuildCrewStartupCommand(r.Name, name, r.Path, "gt prime") startupCmd, err := config.BuildCrewStartupCommandWithAgentOverride(r.Name, name, r.Path, "gt prime", crewAgentOverride)
if err := t.RespawnPane(paneID, claudeCmd); err != nil { if err != nil {
return fmt.Errorf("building startup command: %w", err)
}
if err := t.RespawnPane(paneID, startupCmd); err != nil {
return fmt.Errorf("starting claude: %w", err) return fmt.Errorf("starting claude: %w", err)
} }
@@ -174,8 +177,11 @@ func runCrewAt(cmd *cobra.Command, args []string) error {
// Use respawn-pane to replace shell with Claude directly // Use respawn-pane to replace shell with Claude directly
// Pass "gt prime" as initial prompt so Claude loads context immediately // Pass "gt prime" as initial prompt so Claude loads context immediately
// Export GT_ROLE and BD_ACTOR since tmux SetEnvironment only affects new panes // Export GT_ROLE and BD_ACTOR since tmux SetEnvironment only affects new panes
claudeCmd := config.BuildCrewStartupCommand(r.Name, name, r.Path, "gt prime") startupCmd, err := config.BuildCrewStartupCommandWithAgentOverride(r.Name, name, r.Path, "gt prime", crewAgentOverride)
if err := t.RespawnPane(paneID, claudeCmd); err != nil { if err != nil {
return fmt.Errorf("building startup command: %w", err)
}
if err := t.RespawnPane(paneID, startupCmd); err != nil {
return fmt.Errorf("restarting claude: %w", err) return fmt.Errorf("restarting claude: %w", err)
} }
} }
@@ -185,7 +191,10 @@ func runCrewAt(cmd *cobra.Command, args []string) error {
if isInTmuxSession(sessionID) { if isInTmuxSession(sessionID) {
// We're in the session at a shell prompt - just start the agent directly // We're in the session at a shell prompt - just start the agent directly
// Pass "gt prime" as initial prompt so it loads context immediately // Pass "gt prime" as initial prompt so it loads context immediately
agentCfg := config.ResolveAgentConfig(townRoot, r.Path) agentCfg, _, err := config.ResolveAgentConfigWithOverride(townRoot, r.Path, crewAgentOverride)
if err != nil {
return fmt.Errorf("resolving agent: %w", err)
}
fmt.Printf("Starting %s in current session...\n", agentCfg.Command) fmt.Printf("Starting %s in current session...\n", agentCfg.Command)
return execAgent(agentCfg, "gt prime") return execAgent(agentCfg, "gt prime")
} }
+1
View File
@@ -338,6 +338,7 @@ func runCrewStart(cmd *cobra.Command, args []string) error {
// Set the start.go flags before calling runStartCrew // Set the start.go flags before calling runStartCrew
startCrewRig = rigName startCrewRig = rigName
startCrewAccount = crewAccount startCrewAccount = crewAccount
startCrewAgentOverride = crewAgentOverride
// Use rig/name format for runStartCrew // Use rig/name format for runStartCrew
fullName := rigName + "/" + name fullName := rigName + "/" + name
+14 -6
View File
@@ -89,6 +89,8 @@ Stops the current session (if running) and starts a fresh one.`,
RunE: runDeaconRestart, RunE: runDeaconRestart,
} }
var deaconAgentOverride string
var deaconHeartbeatCmd = &cobra.Command{ var deaconHeartbeatCmd = &cobra.Command{
Use: "heartbeat [action]", Use: "heartbeat [action]",
Short: "Update the Deacon heartbeat", Short: "Update the Deacon heartbeat",
@@ -203,7 +205,6 @@ Examples:
RunE: runDeaconStaleHooks, RunE: runDeaconStaleHooks,
} }
var ( var (
triggerTimeout time.Duration triggerTimeout time.Duration
@@ -258,6 +259,10 @@ func init() {
deaconStaleHooksCmd.Flags().BoolVar(&staleHooksDryRun, "dry-run", false, deaconStaleHooksCmd.Flags().BoolVar(&staleHooksDryRun, "dry-run", false,
"Preview what would be unhooked without making changes") "Preview what would be unhooked without making changes")
deaconStartCmd.Flags().StringVar(&deaconAgentOverride, "agent", "", "Agent alias to run the Deacon with (overrides town default)")
deaconAttachCmd.Flags().StringVar(&deaconAgentOverride, "agent", "", "Agent alias to run the Deacon with (overrides town default)")
deaconRestartCmd.Flags().StringVar(&deaconAgentOverride, "agent", "", "Agent alias to run the Deacon with (overrides town default)")
rootCmd.AddCommand(deaconCmd) rootCmd.AddCommand(deaconCmd)
} }
@@ -275,7 +280,7 @@ func runDeaconStart(cmd *cobra.Command, args []string) error {
return fmt.Errorf("Deacon session already running. Attach with: gt deacon attach") return fmt.Errorf("Deacon session already running. Attach with: gt deacon attach")
} }
if err := startDeaconSession(t, sessionName); err != nil { if err := startDeaconSession(t, sessionName, deaconAgentOverride); err != nil {
return err return err
} }
@@ -287,7 +292,7 @@ func runDeaconStart(cmd *cobra.Command, args []string) error {
} }
// startDeaconSession creates and initializes the Deacon tmux session. // startDeaconSession creates and initializes the Deacon tmux session.
func startDeaconSession(t *tmux.Tmux, sessionName string) error { func startDeaconSession(t *tmux.Tmux, sessionName, agentOverride string) error {
// Find workspace root // Find workspace root
townRoot, err := workspace.FindFromCwdOrError() townRoot, err := workspace.FindFromCwdOrError()
if err != nil { if err != nil {
@@ -326,7 +331,11 @@ func startDeaconSession(t *tmux.Tmux, sessionName string) error {
// Restarts are handled by daemon via ensureDeaconRunning on each heartbeat // Restarts are handled by daemon via ensureDeaconRunning on each heartbeat
// The startup hook handles context loading automatically // The startup hook handles context loading automatically
// Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes // Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes
if err := t.SendKeys(sessionName, config.BuildAgentStartupCommand("deacon", "deacon", "", "")); err != nil { startupCmd, err := config.BuildAgentStartupCommandWithAgentOverride("deacon", "deacon", "", "", agentOverride)
if err != nil {
return fmt.Errorf("building startup command: %w", err)
}
if err := t.SendKeys(sessionName, startupCmd); err != nil {
return fmt.Errorf("sending command: %w", err) return fmt.Errorf("sending command: %w", err)
} }
@@ -394,7 +403,7 @@ func runDeaconAttach(cmd *cobra.Command, args []string) error {
if !running { if !running {
// Auto-start if not running // Auto-start if not running
fmt.Println("Deacon session not running, starting...") fmt.Println("Deacon session not running, starting...")
if err := startDeaconSession(t, sessionName); err != nil { if err := startDeaconSession(t, sessionName, deaconAgentOverride); err != nil {
return err return err
} }
} }
@@ -942,4 +951,3 @@ func runDeaconStaleHooks(cmd *cobra.Command, args []string) error {
return nil return nil
} }
+14 -5
View File
@@ -31,6 +31,8 @@ The Mayor is the global coordinator for Gas Town, running as a persistent
tmux session. Use the subcommands to start, stop, attach, and check status.`, tmux session. Use the subcommands to start, stop, attach, and check status.`,
} }
var mayorAgentOverride string
var mayorStartCmd = &cobra.Command{ var mayorStartCmd = &cobra.Command{
Use: "start", Use: "start",
Short: "Start the Mayor session", Short: "Start the Mayor session",
@@ -84,6 +86,10 @@ func init() {
mayorCmd.AddCommand(mayorStatusCmd) mayorCmd.AddCommand(mayorStatusCmd)
mayorCmd.AddCommand(mayorRestartCmd) mayorCmd.AddCommand(mayorRestartCmd)
mayorStartCmd.Flags().StringVar(&mayorAgentOverride, "agent", "", "Agent alias to run the Mayor with (overrides town default)")
mayorAttachCmd.Flags().StringVar(&mayorAgentOverride, "agent", "", "Agent alias to run the Mayor with (overrides town default)")
mayorRestartCmd.Flags().StringVar(&mayorAgentOverride, "agent", "", "Agent alias to run the Mayor with (overrides town default)")
rootCmd.AddCommand(mayorCmd) rootCmd.AddCommand(mayorCmd)
} }
@@ -101,7 +107,7 @@ func runMayorStart(cmd *cobra.Command, args []string) error {
return fmt.Errorf("Mayor session already running. Attach with: gt mayor attach") return fmt.Errorf("Mayor session already running. Attach with: gt mayor attach")
} }
if err := startMayorSession(t, sessionName); err != nil { if err := startMayorSession(t, sessionName, mayorAgentOverride); err != nil {
return err return err
} }
@@ -113,7 +119,7 @@ func runMayorStart(cmd *cobra.Command, args []string) error {
} }
// startMayorSession creates and initializes the Mayor tmux session. // startMayorSession creates and initializes the Mayor tmux session.
func startMayorSession(t *tmux.Tmux, sessionName string) error { func startMayorSession(t *tmux.Tmux, sessionName, agentOverride string) error {
// Find workspace root // Find workspace root
townRoot, err := workspace.FindFromCwdOrError() townRoot, err := workspace.FindFromCwdOrError()
if err != nil { if err != nil {
@@ -139,8 +145,11 @@ func startMayorSession(t *tmux.Tmux, sessionName string) error {
// Use SendKeysDelayed to allow shell initialization after NewSession // Use SendKeysDelayed to allow shell initialization after NewSession
// Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes // Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes
// Mayor uses default runtime config (empty rigPath) since it's not rig-specific // Mayor uses default runtime config (empty rigPath) since it's not rig-specific
claudeCmd := config.BuildAgentStartupCommand("mayor", "mayor", "", "") startupCmd, err := config.BuildAgentStartupCommandWithAgentOverride("mayor", "mayor", "", "", agentOverride)
if err := t.SendKeysDelayed(sessionName, claudeCmd, 200); err != nil { if err != nil {
return fmt.Errorf("building startup command: %w", err)
}
if err := t.SendKeysDelayed(sessionName, startupCmd, 200); err != nil {
return fmt.Errorf("sending command: %w", err) return fmt.Errorf("sending command: %w", err)
} }
@@ -208,7 +217,7 @@ func runMayorAttach(cmd *cobra.Command, args []string) error {
if !running { if !running {
// Auto-start if not running // Auto-start if not running
fmt.Println("Mayor session not running, starting...") fmt.Println("Mayor session not running, starting...")
if err := startMayorSession(t, sessionName); err != nil { if err := startMayorSession(t, sessionName, mayorAgentOverride); err != nil {
return err return err
} }
} }
+28 -18
View File
@@ -24,16 +24,18 @@ import (
) )
var ( var (
startAll bool startAll bool
startCrewRig string startAgentOverride string
startCrewAccount string startCrewRig string
shutdownGraceful bool startCrewAccount string
shutdownWait int startCrewAgentOverride string
shutdownAll bool shutdownGraceful bool
shutdownForce bool shutdownWait int
shutdownYes bool shutdownAll bool
shutdownPolecatsOnly bool shutdownForce bool
shutdownNuclear bool shutdownYes bool
shutdownPolecatsOnly bool
shutdownNuclear bool
) )
var startCmd = &cobra.Command{ var startCmd = &cobra.Command{
@@ -104,9 +106,11 @@ Examples:
func init() { func init() {
startCmd.Flags().BoolVarP(&startAll, "all", "a", false, startCmd.Flags().BoolVarP(&startAll, "all", "a", false,
"Also start Witnesses and Refineries for all rigs") "Also start Witnesses and Refineries for all rigs")
startCmd.Flags().StringVar(&startAgentOverride, "agent", "", "Agent alias to run Mayor/Deacon with (overrides town default)")
startCrewCmd.Flags().StringVar(&startCrewRig, "rig", "", "Rig to use") startCrewCmd.Flags().StringVar(&startCrewRig, "rig", "", "Rig to use")
startCrewCmd.Flags().StringVar(&startCrewAccount, "account", "", "Claude Code account handle to use") startCrewCmd.Flags().StringVar(&startCrewAccount, "account", "", "Claude Code account handle to use")
startCrewCmd.Flags().StringVar(&startCrewAgentOverride, "agent", "", "Agent alias to run crew worker with (overrides rig/town default)")
startCmd.AddCommand(startCrewCmd) startCmd.AddCommand(startCrewCmd)
shutdownCmd.Flags().BoolVarP(&shutdownGraceful, "graceful", "g", false, shutdownCmd.Flags().BoolVarP(&shutdownGraceful, "graceful", "g", false,
@@ -155,7 +159,7 @@ func runStart(cmd *cobra.Command, args []string) error {
fmt.Printf("Starting Gas Town from %s\n\n", style.Dim.Render(townRoot)) fmt.Printf("Starting Gas Town from %s\n\n", style.Dim.Render(townRoot))
// Start core agents (Mayor and Deacon) // Start core agents (Mayor and Deacon)
if err := startCoreAgents(t); err != nil { if err := startCoreAgents(t, startAgentOverride); err != nil {
return err return err
} }
@@ -182,7 +186,7 @@ func runStart(cmd *cobra.Command, args []string) error {
} }
// startCoreAgents starts Mayor and Deacon sessions. // startCoreAgents starts Mayor and Deacon sessions.
func startCoreAgents(t *tmux.Tmux) error { func startCoreAgents(t *tmux.Tmux, agentOverride string) error {
// Get session names // Get session names
mayorSession := getMayorSessionName() mayorSession := getMayorSessionName()
deaconSession := getDeaconSessionName() deaconSession := getDeaconSessionName()
@@ -193,7 +197,7 @@ func startCoreAgents(t *tmux.Tmux) error {
fmt.Printf(" %s Mayor already running\n", style.Dim.Render("○")) fmt.Printf(" %s Mayor already running\n", style.Dim.Render("○"))
} else { } else {
fmt.Printf(" %s Starting Mayor...\n", style.Bold.Render("→")) fmt.Printf(" %s Starting Mayor...\n", style.Bold.Render("→"))
if err := startMayorSession(t, mayorSession); err != nil { if err := startMayorSession(t, mayorSession, agentOverride); err != nil {
return fmt.Errorf("starting Mayor: %w", err) return fmt.Errorf("starting Mayor: %w", err)
} }
fmt.Printf(" %s Mayor started\n", style.Bold.Render("✓")) fmt.Printf(" %s Mayor started\n", style.Bold.Render("✓"))
@@ -205,7 +209,7 @@ func startCoreAgents(t *tmux.Tmux) error {
fmt.Printf(" %s Deacon already running\n", style.Dim.Render("○")) fmt.Printf(" %s Deacon already running\n", style.Dim.Render("○"))
} else { } else {
fmt.Printf(" %s Starting Deacon...\n", style.Bold.Render("→")) fmt.Printf(" %s Starting Deacon...\n", style.Bold.Render("→"))
if err := startDeaconSession(t, deaconSession); err != nil { if err := startDeaconSession(t, deaconSession, agentOverride); err != nil {
return fmt.Errorf("starting Deacon: %w", err) return fmt.Errorf("starting Deacon: %w", err)
} }
fmt.Printf(" %s Deacon started\n", style.Bold.Render("✓")) fmt.Printf(" %s Deacon started\n", style.Bold.Render("✓"))
@@ -799,8 +803,11 @@ func runStartCrew(cmd *cobra.Command, args []string) error {
if !t.IsClaudeRunning(sessionID) { if !t.IsClaudeRunning(sessionID) {
// Claude has exited, restart it with "gt prime" as initial prompt // Claude has exited, restart it with "gt prime" as initial prompt
fmt.Printf("Session exists, restarting Claude...\n") fmt.Printf("Session exists, restarting Claude...\n")
claudeCmd := config.BuildCrewStartupCommand(rigName, name, r.Path, "gt prime") startupCmd, err := config.BuildCrewStartupCommandWithAgentOverride(rigName, name, r.Path, "gt prime", startCrewAgentOverride)
if err := t.SendKeys(sessionID, claudeCmd); err != nil { if err != nil {
return fmt.Errorf("building startup command: %w", err)
}
if err := t.SendKeys(sessionID, startupCmd); err != nil {
return fmt.Errorf("restarting claude: %w", err) return fmt.Errorf("restarting claude: %w", err)
} }
} else { } else {
@@ -833,8 +840,11 @@ func runStartCrew(cmd *cobra.Command, args []string) error {
// Start claude with skip permissions and proper env vars for seance // Start claude with skip permissions and proper env vars for seance
// Pass "gt prime" as initial prompt so context is loaded immediately // Pass "gt prime" as initial prompt so context is loaded immediately
claudeCmd := config.BuildCrewStartupCommand(rigName, name, r.Path, "gt prime") startupCmd, err := config.BuildCrewStartupCommandWithAgentOverride(rigName, name, r.Path, "gt prime", startCrewAgentOverride)
if err := t.SendKeys(sessionID, claudeCmd); err != nil { if err != nil {
return fmt.Errorf("building startup command: %w", err)
}
if err := t.SendKeys(sessionID, startupCmd); err != nil {
return fmt.Errorf("starting claude: %w", err) return fmt.Errorf("starting claude: %w", err)
} }
+83
View File
@@ -1035,6 +1035,89 @@ func TestBuildPolecatStartupCommandWithAgentOverride(t *testing.T) {
} }
} }
func TestBuildAgentStartupCommandWithAgentOverride(t *testing.T) {
townRoot := t.TempDir()
if err := os.MkdirAll(filepath.Join(townRoot, "mayor"), 0755); err != nil {
t.Fatalf("MkdirAll: %v", err)
}
if err := os.WriteFile(filepath.Join(townRoot, "mayor", "town.json"), []byte("{}"), 0600); err != nil {
t.Fatalf("WriteFile town.json: %v", err)
}
townSettings := NewTownSettings()
townSettings.DefaultAgent = "gemini"
if err := SaveTownSettings(TownSettingsPath(townRoot), townSettings); err != nil {
t.Fatalf("SaveTownSettings: %v", err)
}
originalWd, _ := os.Getwd()
t.Cleanup(func() { _ = os.Chdir(originalWd) })
if err := os.Chdir(townRoot); err != nil {
t.Fatalf("Chdir: %v", err)
}
t.Run("empty override uses default agent", func(t *testing.T) {
cmd, err := BuildAgentStartupCommandWithAgentOverride("mayor", "mayor", "", "", "")
if err != nil {
t.Fatalf("BuildAgentStartupCommandWithAgentOverride: %v", err)
}
if !strings.Contains(cmd, "GT_ROLE=mayor") {
t.Fatalf("expected GT_ROLE export in command: %q", cmd)
}
if !strings.Contains(cmd, "BD_ACTOR=mayor") {
t.Fatalf("expected BD_ACTOR export in command: %q", cmd)
}
if !strings.Contains(cmd, "gemini --approval-mode yolo") {
t.Fatalf("expected gemini command in output: %q", cmd)
}
})
t.Run("override switches agent", func(t *testing.T) {
cmd, err := BuildAgentStartupCommandWithAgentOverride("mayor", "mayor", "", "", "codex")
if err != nil {
t.Fatalf("BuildAgentStartupCommandWithAgentOverride: %v", err)
}
if !strings.Contains(cmd, "codex") {
t.Fatalf("expected codex command in output: %q", cmd)
}
})
}
func TestBuildCrewStartupCommandWithAgentOverride(t *testing.T) {
townRoot := t.TempDir()
rigPath := filepath.Join(townRoot, "testrig")
townSettings := NewTownSettings()
if err := SaveTownSettings(TownSettingsPath(townRoot), townSettings); err != nil {
t.Fatalf("SaveTownSettings: %v", err)
}
if err := SaveRigSettings(RigSettingsPath(rigPath), NewRigSettings()); err != nil {
t.Fatalf("SaveRigSettings: %v", err)
}
cmd, err := BuildCrewStartupCommandWithAgentOverride("testrig", "max", rigPath, "gt prime", "gemini")
if err != nil {
t.Fatalf("BuildCrewStartupCommandWithAgentOverride: %v", err)
}
if !strings.Contains(cmd, "GT_ROLE=crew") {
t.Fatalf("expected GT_ROLE export in command: %q", cmd)
}
if !strings.Contains(cmd, "GT_RIG=testrig") {
t.Fatalf("expected GT_RIG export in command: %q", cmd)
}
if !strings.Contains(cmd, "GT_CREW=max") {
t.Fatalf("expected GT_CREW export in command: %q", cmd)
}
if !strings.Contains(cmd, "BD_ACTOR=testrig/crew/max") {
t.Fatalf("expected BD_ACTOR export in command: %q", cmd)
}
if !strings.Contains(cmd, "gemini --approval-mode yolo") {
t.Fatalf("expected gemini command in output: %q", cmd)
}
}
func TestLoadRuntimeConfigFromSettings(t *testing.T) { func TestLoadRuntimeConfigFromSettings(t *testing.T) {
// Create temp rig with custom runtime config // Create temp rig with custom runtime config
dir := t.TempDir() dir := t.TempDir()