fix(dog): spawn session when dispatching work to dogs
Some checks failed
CI / Check for .beads changes (push) Has been skipped
CI / Check embedded formulas (push) Successful in 18s
CI / Test (push) Failing after 1m13s
CI / Lint (push) Successful in 21s
CI / Integration Tests (push) Successful in 1m1s
CI / Coverage Report (push) Has been skipped
Windows CI / Windows Build and Unit Tests (push) Has been cancelled

Dogs are just directories with state files - they don't have sessions
to receive and execute mail. This means `gt dog dispatch --plugin X`
would assign work and send mail, but nothing would ever execute.

This fix spawns a tmux session for the dog after dispatch, with an
initial prompt that tells Claude to check mail and execute the plugin
instructions. This makes dog dispatch actually work as intended.

The session is named `gt-<town>-deacon-<dogname>` following the pattern
from showDogStatus. If session creation fails, we log a warning but
don't fail the dispatch (mail was sent, work was assigned, human can
manually start the session).

Related: mayor guidance to have deacon execute dog work directly.
This is a stepping stone - dogs now execute their own work via sessions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-21 16:10:36 -08:00
committed by John Ogle
parent 93e22595cd
commit 920e9ac196

View File

@@ -791,6 +791,44 @@ func runDogDispatch(cmd *cobra.Command, args []string) error {
return fmt.Errorf("sending plugin mail to dog: %w", err)
}
// Spawn a session for the dog to execute the work.
// Without a session, the dog's mail inbox is never checked.
// See: https://github.com/steveyegge/gastown/issues/XXX (dog dispatch doesn't execute)
t := tmux.NewTmux()
townName, err := workspace.GetTownName(townRoot)
if err != nil {
townName = "gt" // fallback
}
dogSessionName := fmt.Sprintf("gt-%s-deacon-%s", townName, targetDog.Name)
// Kill any stale session first
if has, _ := t.HasSession(dogSessionName); has {
_ = t.KillSessionWithProcesses(dogSessionName)
}
// 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)
// Create session from dog's directory
if err := t.NewSessionWithCommand(dogSessionName, targetDog.Path, startCmd); err != nil {
if !dogDispatchJSON {
fmt.Printf(" Warning: could not spawn dog session: %v\n", err)
}
// 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
envVars := config.AgentEnv(config.AgentEnvConfig{
Role: "dog",
AgentName: targetDog.Name,
TownRoot: townRoot,
})
for k, v := range envVars {
_ = t.SetEnvironment(dogSessionName, k, v)
}
}
// Success - output result
if dogDispatchJSON {
return json.NewEncoder(os.Stdout).Encode(result)