From e529e20a04e436f8d69f7dcf578fce952552a3cd Mon Sep 17 00:00:00 2001 From: scout/crew/riker Date: Wed, 21 Jan 2026 21:00:58 -0800 Subject: [PATCH] fix(dog): export env vars in startup command and add dog identity detection The previous fix only added the "dog" case to AgentEnv but dogs still couldn't find their mail because: 1. BuildAgentStartupCommand doesn't pass AgentName, so BD_ACTOR was empty 2. detectSenderFromRole had no "dog" case, falling back to "overseer" Fixes: - Add BuildDogStartupCommand that properly passes dog name to AgentEnv - Update dog dispatch to use BuildDogStartupCommand - Add "dog" case to detectSenderFromRole that reads BD_ACTOR The env vars are now exported in the startup command (via exec env) so the agent inherits them when it starts, rather than relying on tmux setenv which only affects new shells. Co-Authored-By: Claude Opus 4.5 --- internal/cmd/dog.go | 6 ++++-- internal/cmd/mail_identity.go | 8 ++++++++ internal/config/loader.go | 12 ++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/internal/cmd/dog.go b/internal/cmd/dog.go index 9a8174c0..6c8cb147 100644 --- a/internal/cmd/dog.go +++ b/internal/cmd/dog.go @@ -808,7 +808,7 @@ func runDogDispatch(cmd *cobra.Command, args []string) error { // Build startup command with initial prompt to check mail and execute plugin initialPrompt := fmt.Sprintf("I am dog %s. Check my mail inbox with 'gt mail inbox' and execute the plugin instructions I received.", targetDog.Name) - startCmd := config.BuildAgentStartupCommand("dog", "", townRoot, targetDog.Path, initialPrompt) + startCmd := config.BuildDogStartupCommand(targetDog.Name, targetDog.Path, townRoot, initialPrompt) // Create session from dog's directory if err := t.NewSessionWithCommand(dogSessionName, targetDog.Path, startCmd); err != nil { @@ -818,7 +818,9 @@ func runDogDispatch(cmd *cobra.Command, args []string) error { // Non-fatal: mail was sent, dog is marked as working, but no session to execute // The deacon or human can manually start the session later } else { - // Set environment for the dog session + // Also set tmux environment as a fallback (belt and suspenders). + // The startup command exports these vars, but setting them in tmux + // environment ensures new panes/windows in the session inherit them. envVars := config.AgentEnv(config.AgentEnvConfig{ Role: "dog", AgentName: targetDog.Name, diff --git a/internal/cmd/mail_identity.go b/internal/cmd/mail_identity.go index 70d087ba..2a2264a6 100644 --- a/internal/cmd/mail_identity.go +++ b/internal/cmd/mail_identity.go @@ -129,6 +129,14 @@ func detectSenderFromRole(role string) string { return fmt.Sprintf("%s/refinery", rig) } return detectSenderFromCwd() + case "dog": + // Dogs use BD_ACTOR directly since they're town-level (no rig) + // Format: deacon/dogs/ + actor := os.Getenv("BD_ACTOR") + if actor != "" { + return actor + } + return detectSenderFromCwd() default: // Unknown role, try cwd detection return detectSenderFromCwd() diff --git a/internal/config/loader.go b/internal/config/loader.go index 612f6cae..6ab24a18 100644 --- a/internal/config/loader.go +++ b/internal/config/loader.go @@ -1472,6 +1472,18 @@ func BuildCrewStartupCommandWithAgentOverride(rigName, crewName, rigPath, prompt return BuildStartupCommandWithAgentOverride(envVars, rigPath, prompt, agentOverride) } +// BuildDogStartupCommand builds the startup command for a deacon dog. +// Dogs are town-level agents, so they don't have a rig. +// Sets GT_ROLE=dog, BD_ACTOR=deacon/dogs/, and GT_ROOT. +func BuildDogStartupCommand(dogName, dogPath, townRoot, prompt string) string { + envVars := AgentEnv(AgentEnvConfig{ + Role: "dog", + AgentName: dogName, + TownRoot: townRoot, + }) + return BuildStartupCommand(envVars, dogPath, prompt) +} + // ExpectedPaneCommands returns tmux pane command names that indicate the runtime is running. // Claude can report as "node" (older versions) or "claude" (newer versions). // Other runtimes typically report their executable name.