Skip to content

Commit cb1fadb

Browse files
A3Ackermangastown/crew/navani
andauthored
fix: inject CLAUDE_CONFIG_DIR in witness, refinery, and deacon startup (gt-9he) (#3450)
Startup paths for witness, refinery, and deacon sessions were missing ResolveAccountConfigDir resolution, causing infra sessions to start without CLAUDE_CONFIG_DIR and use the wrong account. Add the same resolution pattern already used by daemon/lifecycle.go and mayor/manager.go. Co-authored-by: gastown/crew/navani <gastown.crew.navani@gastown.local>
1 parent 50584ce commit cb1fadb

4 files changed

Lines changed: 79 additions & 35 deletions

File tree

internal/cmd/deacon.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,14 @@ func startDeaconSession(t *tmux.Tmux, sessionName, agentOverride string) error {
508508
return fmt.Errorf("creating deacon directory: %w", err)
509509
}
510510

511+
// Resolve CLAUDE_CONFIG_DIR from accounts.json so deacon sessions
512+
// use the correct account. Mirrors the daemon restart path (lifecycle.go).
513+
accountsPath := constants.MayorAccountsPath(townRoot)
514+
runtimeConfigDir, _, _ := config.ResolveAccountConfigDir(accountsPath, "")
515+
if runtimeConfigDir == "" {
516+
runtimeConfigDir = os.Getenv("CLAUDE_CONFIG_DIR")
517+
}
518+
511519
// Ensure runtime settings exist (autonomous role needs mail in SessionStart)
512520
runtimeConfig := config.ResolveRoleAgentConfig("deacon", townRoot, deaconDir)
513521
if err := runtime.EnsureSettingsForRole(deaconDir, deaconDir, "deacon", runtimeConfig); err != nil {
@@ -520,11 +528,12 @@ func startDeaconSession(t *tmux.Tmux, sessionName, agentOverride string) error {
520528
Topic: "patrol",
521529
}, "I am Deacon. First run `gt deacon heartbeat`. Then check gt hook, if empty create mol-deacon-patrol wisp and execute it.")
522530
startupCmd, err := config.BuildStartupCommandFromConfig(config.AgentEnvConfig{
523-
Role: "deacon",
524-
TownRoot: townRoot,
525-
Prompt: initialPrompt,
526-
Topic: "patrol",
527-
SessionName: sessionName,
531+
Role: "deacon",
532+
TownRoot: townRoot,
533+
RuntimeConfigDir: runtimeConfigDir,
534+
Prompt: initialPrompt,
535+
Topic: "patrol",
536+
SessionName: sessionName,
528537
}, "", initialPrompt, agentOverride)
529538
if err != nil {
530539
return fmt.Errorf("building startup command: %w", err)
@@ -540,9 +549,10 @@ func startDeaconSession(t *tmux.Tmux, sessionName, agentOverride string) error {
540549
// Set environment (non-fatal: session works without these)
541550
// Use centralized AgentEnv for consistency across all role startup paths
542551
envVars := config.AgentEnv(config.AgentEnvConfig{
543-
Role: "deacon",
544-
TownRoot: townRoot,
545-
Agent: agentOverride,
552+
Role: "deacon",
553+
TownRoot: townRoot,
554+
RuntimeConfigDir: runtimeConfigDir,
555+
Agent: agentOverride,
546556
})
547557
for k, v := range envVars {
548558
_ = t.SetEnvironment(sessionName, k, v)

internal/refinery/manager.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,15 @@ func (m *Manager) Start(foreground bool, agentOverride string) error {
167167
// Ensure runtime settings exist in the shared refinery parent directory.
168168
// Settings are passed to Claude Code via --settings flag.
169169
townRoot := filepath.Dir(m.rig.Path)
170+
171+
// Resolve CLAUDE_CONFIG_DIR from accounts.json so refinery sessions
172+
// use the correct account. Mirrors the daemon restart path (lifecycle.go).
173+
accountsPath := constants.MayorAccountsPath(townRoot)
174+
runtimeConfigDir, _, _ := config.ResolveAccountConfigDir(accountsPath, "")
175+
if runtimeConfigDir == "" {
176+
runtimeConfigDir = os.Getenv("CLAUDE_CONFIG_DIR")
177+
}
178+
170179
runtimeConfig := config.ResolveRoleAgentConfig("refinery", townRoot, m.rig.Path)
171180
refinerySettingsDir := config.RoleSettingsDir("refinery", m.rig.Path)
172181
if err := runtime.EnsureSettingsForRole(refinerySettingsDir, refineryRigDir, "refinery", runtimeConfig); err != nil {
@@ -185,12 +194,13 @@ func (m *Manager) Start(foreground bool, agentOverride string) error {
185194
}, "Run `gt prime --hook` and begin patrol.")
186195

187196
command, err := config.BuildStartupCommandFromConfig(config.AgentEnvConfig{
188-
Role: "refinery",
189-
Rig: m.rig.Name,
190-
TownRoot: townRoot,
191-
Prompt: initialPrompt,
192-
Topic: "patrol",
193-
SessionName: sessionID,
197+
Role: "refinery",
198+
Rig: m.rig.Name,
199+
TownRoot: townRoot,
200+
RuntimeConfigDir: runtimeConfigDir,
201+
Prompt: initialPrompt,
202+
Topic: "patrol",
203+
SessionName: sessionID,
194204
}, m.rig.Path, initialPrompt, agentOverride)
195205
if err != nil {
196206
return fmt.Errorf("building startup command: %w", err)
@@ -208,11 +218,12 @@ func (m *Manager) Start(foreground bool, agentOverride string) error {
208218
// Set environment variables (non-fatal: session works without these)
209219
// Use centralized AgentEnv for consistency across all role startup paths
210220
envVars := config.AgentEnv(config.AgentEnvConfig{
211-
Role: "refinery",
212-
Rig: m.rig.Name,
213-
TownRoot: townRoot,
214-
Agent: agentOverride,
215-
SessionName: sessionID,
221+
Role: "refinery",
222+
Rig: m.rig.Name,
223+
TownRoot: townRoot,
224+
RuntimeConfigDir: runtimeConfigDir,
225+
Agent: agentOverride,
226+
SessionName: sessionID,
216227
})
217228
envVars = session.MergeRuntimeLivenessEnv(envVars, runtimeConfig)
218229

internal/witness/manager.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,15 @@ func (m *Manager) Start(foreground bool, agentOverride string, envOverrides []st
158158
// package config) to prevent concurrent rig starts from corrupting the
159159
// global agent registry.
160160
townRoot := m.townRoot()
161+
162+
// Resolve CLAUDE_CONFIG_DIR from accounts.json so witness sessions
163+
// use the correct account. Mirrors the daemon restart path (lifecycle.go).
164+
accountsPath := constants.MayorAccountsPath(townRoot)
165+
runtimeConfigDir, _, _ := config.ResolveAccountConfigDir(accountsPath, "")
166+
if runtimeConfigDir == "" {
167+
runtimeConfigDir = os.Getenv("CLAUDE_CONFIG_DIR")
168+
}
169+
161170
runtimeConfig := config.ResolveRoleAgentConfig("witness", townRoot, m.rig.Path)
162171
witnessSettingsDir := config.RoleSettingsDir("witness", m.rig.Path)
163172
if err := runtime.EnsureSettingsForRole(witnessSettingsDir, witnessDir, "witness", runtimeConfig); err != nil {
@@ -180,7 +189,7 @@ func (m *Manager) Start(foreground bool, agentOverride string, envOverrides []st
180189
// NOTE: No gt prime injection needed - SessionStart hook handles it automatically
181190
// Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes
182191
// Pass m.rig.Path so rig agent settings are honored (not town-level defaults)
183-
command, err := buildWitnessStartCommand(m.rig.Path, m.rig.Name, townRoot, sessionID, agentOverride, roleConfig)
192+
command, err := buildWitnessStartCommand(m.rig.Path, m.rig.Name, townRoot, sessionID, agentOverride, roleConfig, runtimeConfigDir)
184193
if err != nil {
185194
return err
186195
}
@@ -197,11 +206,12 @@ func (m *Manager) Start(foreground bool, agentOverride string, envOverrides []st
197206
// Set environment variables (non-fatal: session works without these)
198207
// Use centralized AgentEnv for consistency across all role startup paths
199208
envVars := config.AgentEnv(config.AgentEnvConfig{
200-
Role: "witness",
201-
Rig: m.rig.Name,
202-
TownRoot: townRoot,
203-
Agent: agentOverride,
204-
SessionName: sessionID,
209+
Role: "witness",
210+
Rig: m.rig.Name,
211+
TownRoot: townRoot,
212+
RuntimeConfigDir: runtimeConfigDir,
213+
Agent: agentOverride,
214+
SessionName: sessionID,
205215
})
206216
envVars = session.MergeRuntimeLivenessEnv(envVars, runtimeConfig)
207217
for k, v := range envVars {
@@ -311,7 +321,7 @@ func roleConfigEnvVars(roleConfig *beads.RoleConfig, townRoot, rigName string) m
311321
return expanded
312322
}
313323

314-
func buildWitnessStartCommand(rigPath, rigName, townRoot, sessionName, agentOverride string, roleConfig *beads.RoleConfig) (string, error) {
324+
func buildWitnessStartCommand(rigPath, rigName, townRoot, sessionName, agentOverride string, roleConfig *beads.RoleConfig, runtimeConfigDir string) (string, error) {
315325
if agentOverride != "" {
316326
roleConfig = nil
317327
}
@@ -343,12 +353,13 @@ func buildWitnessStartCommand(rigPath, rigName, townRoot, sessionName, agentOver
343353
Topic: "patrol",
344354
}, "Run `gt prime --hook` and begin patrol.")
345355
command, err := config.BuildStartupCommandFromConfig(config.AgentEnvConfig{
346-
Role: "witness",
347-
Rig: rigName,
348-
TownRoot: townRoot,
349-
Prompt: initialPrompt,
350-
Topic: "patrol",
351-
SessionName: sessionName,
356+
Role: "witness",
357+
Rig: rigName,
358+
TownRoot: townRoot,
359+
RuntimeConfigDir: runtimeConfigDir,
360+
Prompt: initialPrompt,
361+
Topic: "patrol",
362+
SessionName: sessionName,
352363
}, rigPath, initialPrompt, agentOverride)
353364
if err != nil {
354365
return "", fmt.Errorf("building startup command: %w", err)

internal/witness/manager_test.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func TestBuildWitnessStartCommand_UsesRoleConfig(t *testing.T) {
1313
StartCommand: "exec run --town {town} --rig {rig} --role {role}",
1414
}
1515

16-
got, err := buildWitnessStartCommand("/town/rig", "gastown", "/town", "", "", roleCfg)
16+
got, err := buildWitnessStartCommand("/town/rig", "gastown", "/town", "", "", roleCfg, "")
1717
if err != nil {
1818
t.Fatalf("buildWitnessStartCommand: %v", err)
1919
}
@@ -26,7 +26,7 @@ func TestBuildWitnessStartCommand_UsesRoleConfig(t *testing.T) {
2626

2727
func TestBuildWitnessStartCommand_DefaultsToRuntime(t *testing.T) {
2828
t.Parallel()
29-
got, err := buildWitnessStartCommand("/town/rig", "gastown", "/town", "", "", nil)
29+
got, err := buildWitnessStartCommand("/town/rig", "gastown", "/town", "", "", nil, "")
3030
if err != nil {
3131
t.Fatalf("buildWitnessStartCommand: %v", err)
3232
}
@@ -68,13 +68,25 @@ func TestRoleConfigEnvVars_NilConfig(t *testing.T) {
6868
}
6969
}
7070

71+
func TestBuildWitnessStartCommand_IncludesConfigDir(t *testing.T) {
72+
t.Parallel()
73+
got, err := buildWitnessStartCommand("/town/rig", "gastown", "/town", "", "", nil, "/home/user/.claude-accounts/work")
74+
if err != nil {
75+
t.Fatalf("buildWitnessStartCommand: %v", err)
76+
}
77+
78+
if !strings.Contains(got, "CLAUDE_CONFIG_DIR=/home/user/.claude-accounts/work") {
79+
t.Errorf("expected CLAUDE_CONFIG_DIR in command, got %q", got)
80+
}
81+
}
82+
7183
func TestBuildWitnessStartCommand_AgentOverrideWins(t *testing.T) {
7284
t.Parallel()
7385
roleCfg := &beads.RoleConfig{
7486
StartCommand: "exec run --role {role}",
7587
}
7688

77-
got, err := buildWitnessStartCommand("/town/rig", "gastown", "/town", "", "codex", roleCfg)
89+
got, err := buildWitnessStartCommand("/town/rig", "gastown", "/town", "", "codex", roleCfg, "")
7890
if err != nil {
7991
t.Fatalf("buildWitnessStartCommand: %v", err)
8092
}

0 commit comments

Comments
 (0)