Skip to content

Commit 3caf32f

Browse files
julianknutsenjulianknutsenclaudesteveyegge
authored
fix(config): don't export empty GT_ROOT/BEADS_DIR in AgentEnv (gastownhall#385)
* fix(config): don't export empty GT_ROOT/BEADS_DIR in AgentEnv Fix polecats not having GT_ROOT environment variable set. The symptom was polecat sessions showing GT_ROOT="" instead of the expected town root. Root cause: AgentEnvSimple doesn't set TownRoot, but AgentEnv was always setting env["GT_ROOT"] = cfg.TownRoot even when empty. This empty value in export commands would override the tmux session environment. Changes: - Only set GT_ROOT and BEADS_DIR in env map if non-empty - Refactor daemon.go to use AgentEnv with full AgentEnvConfig instead of AgentEnvSimple + manual additions - Update test to verify keys are absent rather than empty Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(lint): silence unparam for unused executeExternalActions args The external action params (beadID, severity, description) are reserved for future email/SMS/slack implementations but currently unused. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: julianknutsen <julianknutsen@users.noreply.github> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: max <steve.yegge@gmail.com>
1 parent 3cdc986 commit 3caf32f

4 files changed

Lines changed: 44 additions & 16 deletions

File tree

internal/cmd/escalate_impl.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ func extractMailTargetsFromActions(actions []string) []string {
540540

541541
// executeExternalActions processes external notification actions (email:, sms:, slack).
542542
// For now, this logs warnings if contacts aren't configured - actual sending is future work.
543-
func executeExternalActions(actions []string, cfg *config.EscalationConfig, beadID, severity, description string) {
543+
func executeExternalActions(actions []string, cfg *config.EscalationConfig, _, _, _ string) {
544544
for _, action := range actions {
545545
switch {
546546
case strings.HasPrefix(action, "email:"):

internal/config/env.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,14 @@ func AgentEnv(cfg AgentEnvConfig) map[string]string {
8181
env["GIT_AUTHOR_NAME"] = cfg.AgentName
8282
}
8383

84-
env["GT_ROOT"] = cfg.TownRoot
85-
env["BEADS_DIR"] = cfg.BeadsDir
84+
// Only set GT_ROOT and BEADS_DIR if provided
85+
// Empty values would override tmux session environment
86+
if cfg.TownRoot != "" {
87+
env["GT_ROOT"] = cfg.TownRoot
88+
}
89+
if cfg.BeadsDir != "" {
90+
env["BEADS_DIR"] = cfg.BeadsDir
91+
}
8692

8793
// Set BEADS_AGENT_NAME for polecat/crew (uses same format as BD_ACTOR)
8894
if cfg.Role == "polecat" || cfg.Role == "crew" {

internal/config/env_test.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,32 @@ func TestAgentEnvSimple(t *testing.T) {
163163
assertEnv(t, env, "GT_ROLE", "polecat")
164164
assertEnv(t, env, "GT_RIG", "myrig")
165165
assertEnv(t, env, "GT_POLECAT", "Toast")
166-
// Simple doesn't set TownRoot/BeadsDir
167-
assertEnv(t, env, "GT_ROOT", "")
168-
assertEnv(t, env, "BEADS_DIR", "")
166+
// Simple doesn't set TownRoot/BeadsDir, so keys should be absent
167+
// (not empty strings which would override tmux session environment)
168+
assertNotSet(t, env, "GT_ROOT")
169+
assertNotSet(t, env, "BEADS_DIR")
170+
}
171+
172+
func TestAgentEnv_EmptyTownRootBeadsDirOmitted(t *testing.T) {
173+
t.Parallel()
174+
// Regression test: empty TownRoot/BeadsDir should NOT create keys in the map.
175+
// If they were set to empty strings, ExportPrefix would generate "export GT_ROOT= ..."
176+
// which overrides tmux session environment where these are correctly set.
177+
env := AgentEnv(AgentEnvConfig{
178+
Role: "polecat",
179+
Rig: "myrig",
180+
AgentName: "Toast",
181+
TownRoot: "", // explicitly empty
182+
BeadsDir: "", // explicitly empty
183+
})
184+
185+
// Keys should be absent, not empty strings
186+
assertNotSet(t, env, "GT_ROOT")
187+
assertNotSet(t, env, "BEADS_DIR")
188+
189+
// Other keys should still be set
190+
assertEnv(t, env, "GT_ROLE", "polecat")
191+
assertEnv(t, env, "GT_RIG", "myrig")
169192
}
170193

171194
func TestExportPrefix(t *testing.T) {

internal/daemon/daemon.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -849,17 +849,16 @@ func (d *Daemon) restartPolecatSession(rigName, polecatName, sessionName string)
849849
return fmt.Errorf("creating session: %w", err)
850850
}
851851

852-
// Set environment variables
853-
// Use centralized AgentEnvSimple for consistency across all role startup paths
854-
envVars := config.AgentEnvSimple("polecat", rigName, polecatName)
855-
856-
// Add polecat-specific beads configuration
857-
// Use ResolveBeadsDir to follow redirects for repos with tracked beads
852+
// Set environment variables using centralized AgentEnv
858853
rigPath := filepath.Join(d.config.TownRoot, rigName)
859-
beadsDir := beads.ResolveBeadsDir(rigPath)
860-
envVars["BEADS_DIR"] = beadsDir
861-
envVars["BEADS_NO_DAEMON"] = "1"
862-
envVars["BEADS_AGENT_NAME"] = fmt.Sprintf("%s/%s", rigName, polecatName)
854+
envVars := config.AgentEnv(config.AgentEnvConfig{
855+
Role: "polecat",
856+
Rig: rigName,
857+
AgentName: polecatName,
858+
TownRoot: d.config.TownRoot,
859+
BeadsDir: beads.ResolveBeadsDir(rigPath),
860+
BeadsNoDaemon: true,
861+
})
863862

864863
// Set all env vars in tmux session (for debugging) and they'll also be exported to Claude
865864
for k, v := range envVars {

0 commit comments

Comments
 (0)