Fix polecat auto-start by triggering pending spawns in daemon heartbeat (gt-iu23)
The daemon now calls triggerPendingSpawns() on each heartbeat, which: - Checks for POLECAT_STARTED messages in Deacon inbox - Polls pending spawns with WaitForClaudeReady (2s timeout) - Nudges ready polecats with Begin message - Prunes stale pending spawns (>5 min old) This ensures polecats get triggered even when Deacon is not in an active patrol cycle. Uses bootstrap mode regex detection which is acceptable for daemon operations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/steveyegge/gastown/internal/polecat"
|
||||||
"github.com/steveyegge/gastown/internal/tmux"
|
"github.com/steveyegge/gastown/internal/tmux"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -119,7 +120,12 @@ func (d *Daemon) heartbeat(state *State) {
|
|||||||
// 2. Send heartbeat to Deacon (simple notification, no decision-making)
|
// 2. Send heartbeat to Deacon (simple notification, no decision-making)
|
||||||
d.pokeDeacon()
|
d.pokeDeacon()
|
||||||
|
|
||||||
// 3. Process lifecycle requests
|
// 3. Trigger pending polecat spawns (bootstrap mode - ZFC violation acceptable)
|
||||||
|
// This ensures polecats get nudged even when Deacon isn't in a patrol cycle.
|
||||||
|
// Uses regex-based WaitForClaudeReady, which is acceptable for daemon bootstrap.
|
||||||
|
d.triggerPendingSpawns()
|
||||||
|
|
||||||
|
// 4. Process lifecycle requests
|
||||||
d.processLifecycleRequests()
|
d.processLifecycleRequests()
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
@@ -258,6 +264,55 @@ func (d *Daemon) pokeDeacon() {
|
|||||||
// The Deacon molecule is responsible for monitoring Mayor and Witnesses.
|
// The Deacon molecule is responsible for monitoring Mayor and Witnesses.
|
||||||
// The daemon only ensures Deacon is running and sends it heartbeats.
|
// The daemon only ensures Deacon is running and sends it heartbeats.
|
||||||
|
|
||||||
|
// triggerPendingSpawns polls pending polecat spawns and triggers those that are ready.
|
||||||
|
// This is bootstrap mode - uses regex-based WaitForClaudeReady which is acceptable
|
||||||
|
// for daemon operations when no AI agent is guaranteed to be running.
|
||||||
|
// The timeout is short (2s) to avoid blocking the heartbeat.
|
||||||
|
func (d *Daemon) triggerPendingSpawns() {
|
||||||
|
const triggerTimeout = 2 * time.Second
|
||||||
|
|
||||||
|
// Check for pending spawns (from POLECAT_STARTED messages in Deacon inbox)
|
||||||
|
pending, err := polecat.CheckInboxForSpawns(d.config.TownRoot)
|
||||||
|
if err != nil {
|
||||||
|
d.logger.Printf("Error checking pending spawns: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pending) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Printf("Found %d pending spawn(s), attempting to trigger...", len(pending))
|
||||||
|
|
||||||
|
// Trigger pending spawns (uses WaitForClaudeReady with short timeout)
|
||||||
|
results, err := polecat.TriggerPendingSpawns(d.config.TownRoot, triggerTimeout)
|
||||||
|
if err != nil {
|
||||||
|
d.logger.Printf("Error triggering spawns: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log results
|
||||||
|
triggered := 0
|
||||||
|
for _, r := range results {
|
||||||
|
if r.Triggered {
|
||||||
|
triggered++
|
||||||
|
d.logger.Printf("Triggered polecat: %s/%s", r.Spawn.Rig, r.Spawn.Polecat)
|
||||||
|
} else if r.Error != nil {
|
||||||
|
d.logger.Printf("Error triggering %s: %v", r.Spawn.Session, r.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if triggered > 0 {
|
||||||
|
d.logger.Printf("Triggered %d/%d pending spawn(s)", triggered, len(pending))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prune stale pending spawns (older than 5 minutes - likely dead sessions)
|
||||||
|
pruned, _ := polecat.PruneStalePending(d.config.TownRoot, 5*time.Minute)
|
||||||
|
if pruned > 0 {
|
||||||
|
d.logger.Printf("Pruned %d stale pending spawn(s)", pruned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// processLifecycleRequests checks for and processes lifecycle requests.
|
// processLifecycleRequests checks for and processes lifecycle requests.
|
||||||
func (d *Daemon) processLifecycleRequests() {
|
func (d *Daemon) processLifecycleRequests() {
|
||||||
d.ProcessLifecycleRequests()
|
d.ProcessLifecycleRequests()
|
||||||
|
|||||||
Reference in New Issue
Block a user