Files
gastown/internal/session/startup.go
aleiby 31bd120077 fix(startup): unify agent startup with beacon + instructions in CLI prompt (#977)
All agents now receive their startup beacon + role-specific instructions via
the CLI prompt, making sessions identifiable in /resume picker while removing
unreliable post-startup nudges.

Changes:
- Rename FormatStartupNudge → FormatStartupBeacon, StartupNudgeConfig → BeaconConfig
- Remove StartupNudge() function (no longer needed)
- Remove PropulsionNudge() and PropulsionNudgeForRole() functions
- Update deacon, witness, refinery, polecat managers to include beacon in CLI prompt
- Update boot to inline beacon (can't import session due to import cycle)
- Update daemon/lifecycle.go to include beacon via BuildCommandWithPrompt
- Update cmd/deacon.go to include beacon in startup command
- Remove redundant StartupNudge and PropulsionNudge calls from all startup paths

The beacon is now part of the CLI prompt which is queued before Claude starts,
making it more reliable than post-startup nudges which had timing issues.
SessionStart hook runs gt prime automatically, so PropulsionNudge was redundant.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 18:08:57 -08:00

98 lines
3.8 KiB
Go

// Package session provides polecat session lifecycle management.
package session
import (
"fmt"
"time"
)
// BeaconConfig configures a startup beacon message.
// The beacon is injected into the CLI prompt to identify sessions in /resume picker.
type BeaconConfig struct {
// Recipient is the address of the agent being nudged.
// Examples: "gastown/crew/gus", "deacon", "gastown/witness"
Recipient string
// Sender is the agent initiating the nudge.
// Examples: "mayor", "deacon", "self" (for handoff)
Sender string
// Topic describes why the session was started.
// Examples: "cold-start", "handoff", "assigned", or a mol-id
Topic string
// MolID is an optional molecule ID being worked.
// If provided, appended to topic as "topic:mol-id"
MolID string
}
// FormatStartupBeacon builds the formatted startup beacon message.
// The beacon is injected into the CLI prompt, making sessions identifiable
// in Claude Code's /resume picker for predecessor discovery.
//
// Format: [GAS TOWN] <recipient> <- <sender> • <timestamp> • <topic[:mol-id]>
//
// Examples:
// - [GAS TOWN] gastown/crew/gus <- deacon • 2025-12-30T15:42 • assigned:gt-abc12
// - [GAS TOWN] deacon <- daemon • 2025-12-30T08:00 • patrol
// - [GAS TOWN] gastown/witness <- deacon • 2025-12-30T14:00 • patrol
func FormatStartupBeacon(cfg BeaconConfig) string {
// Use local time in compact format
timestamp := time.Now().Format("2006-01-02T15:04")
// Build topic string - append mol-id if provided
topic := cfg.Topic
if cfg.MolID != "" && cfg.Topic != "" {
topic = fmt.Sprintf("%s:%s", cfg.Topic, cfg.MolID)
} else if cfg.MolID != "" {
topic = cfg.MolID
} else if topic == "" {
topic = "ready"
}
// Build the beacon: [GAS TOWN] recipient <- sender • timestamp • topic
beacon := fmt.Sprintf("[GAS TOWN] %s <- %s • %s • %s",
cfg.Recipient, cfg.Sender, timestamp, topic)
// For handoff, cold-start, and attach, add explicit instructions so the agent knows
// what to do even if hooks haven't loaded CLAUDE.md yet
if cfg.Topic == "handoff" || cfg.Topic == "cold-start" || cfg.Topic == "attach" {
beacon += "\n\nCheck your hook and mail, then act on the hook if present:\n" +
"1. `gt hook` - shows hooked work (if any)\n" +
"2. `gt mail inbox` - check for messages\n" +
"3. If work is hooked → execute it immediately\n" +
"4. If nothing hooked → wait for instructions"
}
// For assigned, work is already on the hook - just tell them to run it
// This prevents the "helpful assistant" exploration pattern (see PRIMING.md)
if cfg.Topic == "assigned" {
beacon += "\n\nWork is on your hook. Run `gt hook` now and begin immediately."
}
// For start/restart, add fallback instructions in case SessionStart hook fails
// to inject context via gt prime. This prevents the "No recent activity" state
// where agents sit idle because they received only metadata, no instructions.
// See: gt-uoc64 (crew workers starting without proper context injection)
if cfg.Topic == "start" || cfg.Topic == "restart" {
beacon += "\n\nRun `gt prime` now for full context, then check your hook and mail."
}
return beacon
}
// BuildStartupPrompt creates the CLI prompt for agent startup.
//
// GUPP (Gas Town Universal Propulsion Principle) implementation:
// - Beacon identifies session for /resume predecessor discovery
// - Instructions tell agent to start working immediately
// - SessionStart hook runs `gt prime` which injects full context including
// "AUTONOMOUS WORK MODE" instructions when work is hooked
//
// This replaces the old two-step StartupNudge + PropulsionNudge pattern.
// The beacon is processed in Claude's first turn along with gt prime context,
// so no separate propulsion nudge is needed.
func BuildStartupPrompt(cfg BeaconConfig, instructions string) string {
return FormatStartupBeacon(cfg) + "\n\n" + instructions
}