fix(dog): spawn session and set BD_ACTOR for dog dispatch
Some checks failed
CI / Check for .beads changes (push) Has been skipped
CI / Check embedded formulas (push) Successful in 24s
CI / Test (push) Failing after 1m24s
CI / Lint (push) Failing after 16s
CI / Integration Tests (push) Successful in 1m9s
CI / Coverage Report (push) Has been skipped
Windows CI / Windows Build and Unit Tests (push) Has been cancelled
Some checks failed
CI / Check for .beads changes (push) Has been skipped
CI / Check embedded formulas (push) Successful in 24s
CI / Test (push) Failing after 1m24s
CI / Lint (push) Failing after 16s
CI / Integration Tests (push) Successful in 1m9s
CI / Coverage Report (push) Has been skipped
Windows CI / Windows Build and Unit Tests (push) Has been cancelled
Recovered from reflog - these commits were lost during a rebase/force-push. Dogs are directories with state files but no sessions. When `gt dog dispatch` assigned work and sent mail, nothing executed because no session existed. Changes: 1. Spawn tmux session after dispatch (gt-<town>-deacon-<dogname>) 2. Set BD_ACTOR=deacon/dogs/<name> so dogs can find their mail 3. Add dog case to AgentEnv for proper identity Session spawn is non-blocking - if it fails, mail was sent and human can manually start the session. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -836,6 +836,44 @@ func runDogDispatch(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("sending plugin mail to dog: %w", err)
|
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
|
// Success - output result
|
||||||
if dogDispatchJSON {
|
if dogDispatchJSON {
|
||||||
return json.NewEncoder(os.Stdout).Encode(result)
|
return json.NewEncoder(os.Stdout).Encode(result)
|
||||||
|
|||||||
@@ -61,6 +61,11 @@ func AgentEnv(cfg AgentEnvConfig) map[string]string {
|
|||||||
env["GIT_AUTHOR_NAME"] = "boot"
|
env["GIT_AUTHOR_NAME"] = "boot"
|
||||||
env["GIT_AUTHOR_EMAIL"] = "boot@gastown.local"
|
env["GIT_AUTHOR_EMAIL"] = "boot@gastown.local"
|
||||||
|
|
||||||
|
case "dog":
|
||||||
|
env["BD_ACTOR"] = fmt.Sprintf("deacon/dogs/%s", cfg.AgentName)
|
||||||
|
env["GIT_AUTHOR_NAME"] = fmt.Sprintf("dog-%s", cfg.AgentName)
|
||||||
|
env["GIT_AUTHOR_EMAIL"] = fmt.Sprintf("dog-%s@gastown.local", cfg.AgentName)
|
||||||
|
|
||||||
case "witness":
|
case "witness":
|
||||||
env["GT_RIG"] = cfg.Rig
|
env["GT_RIG"] = cfg.Rig
|
||||||
env["BD_ACTOR"] = fmt.Sprintf("%s/witness", cfg.Rig)
|
env["BD_ACTOR"] = fmt.Sprintf("%s/witness", cfg.Rig)
|
||||||
@@ -128,7 +133,7 @@ func AgentEnvSimple(role, rig, agentName string) map[string]string {
|
|||||||
|
|
||||||
// ShellQuote returns a shell-safe quoted string.
|
// ShellQuote returns a shell-safe quoted string.
|
||||||
// Values containing special characters are wrapped in single quotes.
|
// Values containing special characters are wrapped in single quotes.
|
||||||
// Single quotes within the value are escaped using the '\'' idiom.
|
// Single quotes within the value are escaped using the '\” idiom.
|
||||||
func ShellQuote(s string) string {
|
func ShellQuote(s string) string {
|
||||||
// Check if quoting is needed (contains shell special chars)
|
// Check if quoting is needed (contains shell special chars)
|
||||||
needsQuoting := false
|
needsQuoting := false
|
||||||
|
|||||||
@@ -125,6 +125,23 @@ func TestAgentEnv_Boot(t *testing.T) {
|
|||||||
assertNotSet(t, env, "BEADS_NO_DAEMON")
|
assertNotSet(t, env, "BEADS_NO_DAEMON")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAgentEnv_Dog(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
env := AgentEnv(AgentEnvConfig{
|
||||||
|
Role: "dog",
|
||||||
|
AgentName: "alpha",
|
||||||
|
TownRoot: "/town",
|
||||||
|
})
|
||||||
|
|
||||||
|
assertEnv(t, env, "GT_ROLE", "dog")
|
||||||
|
assertEnv(t, env, "BD_ACTOR", "deacon/dogs/alpha")
|
||||||
|
assertEnv(t, env, "GIT_AUTHOR_NAME", "dog-alpha")
|
||||||
|
assertEnv(t, env, "GIT_AUTHOR_EMAIL", "dog-alpha@gastown.local")
|
||||||
|
assertEnv(t, env, "GT_ROOT", "/town")
|
||||||
|
assertNotSet(t, env, "GT_RIG")
|
||||||
|
assertNotSet(t, env, "BEADS_NO_DAEMON")
|
||||||
|
}
|
||||||
|
|
||||||
func TestAgentEnv_WithRuntimeConfigDir(t *testing.T) {
|
func TestAgentEnv_WithRuntimeConfigDir(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
env := AgentEnv(AgentEnvConfig{
|
env := AgentEnv(AgentEnvConfig{
|
||||||
|
|||||||
Reference in New Issue
Block a user