Skip to content

Commit 278b2f2

Browse files
steveyeggeclaude
andcommitted
fix(mayor): match handoff priming for gt may at startup (hq-osbot)
When starting Mayor via 'gt may at', the session now: 1. Works from townRoot (~/gt) instead of mayorDir (~/gt/mayor) 2. Includes startup beacon with explicit instructions in initial prompt 3. Removes redundant post-start nudges (beacon has instructions) This matches the 'gt handoff' behavior where the agent immediately knows to check hook and mail on startup. Fixes: hq-h3449 (P0 escalation - horrendous starting UX) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 791b388 commit 278b2f2

3 files changed

Lines changed: 21 additions & 21 deletions

File tree

internal/mayor/manager.go

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func (m *Manager) Start(agentOverride string) error {
6868
}
6969
}
7070

71-
// Ensure mayor directory exists
71+
// Ensure mayor directory exists (for Claude settings)
7272
mayorDir := m.mayorDir()
7373
if err := os.MkdirAll(mayorDir, 0755); err != nil {
7474
return fmt.Errorf("creating mayor directory: %w", err)
@@ -79,18 +79,25 @@ func (m *Manager) Start(agentOverride string) error {
7979
return fmt.Errorf("ensuring Claude settings: %w", err)
8080
}
8181

82-
// Build startup command first - the startup hook handles 'gt prime' automatically
82+
// Build startup beacon with explicit instructions (matches gt handoff behavior)
83+
// This ensures the agent has clear context immediately, not after nudges arrive
84+
beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
85+
Recipient: "mayor",
86+
Sender: "human",
87+
Topic: "cold-start",
88+
})
89+
90+
// Build startup command WITH the beacon prompt - the startup hook handles 'gt prime' automatically
8391
// Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes
84-
startupCmd, err := config.BuildAgentStartupCommandWithAgentOverride("mayor", "mayor", "", "", agentOverride)
92+
startupCmd, err := config.BuildAgentStartupCommandWithAgentOverride("mayor", "mayor", "", beacon, agentOverride)
8593
if err != nil {
8694
return fmt.Errorf("building startup command: %w", err)
8795
}
8896

89-
// Create session with command directly to avoid send-keys race condition.
90-
// This runs the command as the pane's initial process, avoiding the shell
91-
// readiness timing issues that cause "bad pattern" and command-not-found errors.
97+
// Create session in townRoot (not mayorDir) to match gt handoff behavior
98+
// This ensures Mayor works from the town root where all tools work correctly
9299
// See: https://github.com/anthropics/gastown/issues/280
93-
if err := t.NewSessionWithCommand(sessionID, mayorDir, startupCmd); err != nil {
100+
if err := t.NewSessionWithCommand(sessionID, m.townRoot, startupCmd); err != nil {
94101
return fmt.Errorf("creating tmux session: %w", err)
95102
}
96103

@@ -119,18 +126,8 @@ func (m *Manager) Start(agentOverride string) error {
119126

120127
time.Sleep(constants.ShutdownNotifyDelay)
121128

122-
// Inject startup nudge for predecessor discovery via /resume
123-
_ = session.StartupNudge(t, sessionID, session.StartupNudgeConfig{
124-
Recipient: "mayor",
125-
Sender: "human",
126-
Topic: "cold-start",
127-
}) // Non-fatal
128-
129-
// GUPP: Gas Town Universal Propulsion Principle
130-
// Send the propulsion nudge to trigger autonomous coordination.
131-
// Wait for beacon to be fully processed (needs to be separate prompt)
132-
time.Sleep(2 * time.Second)
133-
_ = t.NudgeSession(sessionID, session.PropulsionNudgeForRole("mayor", mayorDir)) // Non-fatal
129+
// Startup beacon with instructions is now included in the initial command,
130+
// so no separate nudge needed. The agent starts with full context immediately.
134131

135132
return nil
136133
}

internal/session/startup.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ func FormatStartupNudge(cfg StartupNudgeConfig) string {
6565
beacon := fmt.Sprintf("[GAS TOWN] %s <- %s • %s • %s",
6666
cfg.Recipient, cfg.Sender, timestamp, topic)
6767

68-
// For handoff, add explicit instructions so the agent knows what to do
68+
// For handoff and cold-start, add explicit instructions so the agent knows what to do
6969
// even if hooks haven't loaded CLAUDE.md yet
70-
if cfg.Topic == "handoff" {
70+
if cfg.Topic == "handoff" || cfg.Topic == "cold-start" {
7171
beacon += "\n\nCheck your hook and mail, then act on the hook if present:\n" +
7272
"1. `gt hook` - shows hooked work (if any)\n" +
7373
"2. `gt mail inbox` - check for messages\n" +

internal/session/startup_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ func TestFormatStartupNudge(t *testing.T) {
4141
"deacon",
4242
"<- mayor",
4343
"cold-start",
44+
"Check your hook and mail", // cold-start includes explicit instructions (like handoff)
45+
"gt hook",
46+
"gt mail inbox",
4447
},
4548
// No wantNot - timestamp contains ":"
4649
},

0 commit comments

Comments
 (0)