feat(lifecycle): immediate daemon notification via SIGUSR1

- daemon.go: Add SIGUSR1 handler to process lifecycle requests immediately
- handoff.go: Signal daemon after sending lifecycle mail to deacon/
- mail.go: Show message IDs in hook output for direct reading

Previously, gt handoff would wait up to 5 min for daemon heartbeat poll.
Now lifecycle requests are processed within milliseconds.

Closes gt-zut3

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-20 20:28:36 -08:00
parent 57917521e6
commit e5c88ae9d4
3 changed files with 47 additions and 4 deletions

View File

@@ -85,7 +85,7 @@ func (d *Daemon) Run() error {
// Handle signals
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1)
// Heartbeat ticker
ticker := time.NewTicker(d.config.HeartbeatInterval)
@@ -103,8 +103,14 @@ func (d *Daemon) Run() error {
return d.shutdown(state)
case sig := <-sigChan:
d.logger.Printf("Received signal %v, shutting down", sig)
return d.shutdown(state)
if sig == syscall.SIGUSR1 {
// SIGUSR1: immediate lifecycle processing (from gt handoff)
d.logger.Println("Received SIGUSR1, processing lifecycle requests immediately")
d.processLifecycleRequests()
} else {
d.logger.Printf("Received signal %v, shutting down", sig)
return d.shutdown(state)
}
case <-ticker.C:
d.heartbeat(state)