From a68e5bf95b5e6f0457b355f5f15f742a65c473aa Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Fri, 26 Dec 2025 17:52:41 -0800 Subject: [PATCH] fix: ignore lifecycle requests older than 6 hours (gt-a41um) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Defense in depth: Even if message deletion fails, stale lifecycle requests (>6 hours old) are now ignored and deleted without execution. This prevents ancient LIFECYCLE messages from killing sessions if they somehow survive in the inbox. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- internal/daemon/lifecycle.go | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/internal/daemon/lifecycle.go b/internal/daemon/lifecycle.go index 13466e83..b99a177a 100644 --- a/internal/daemon/lifecycle.go +++ b/internal/daemon/lifecycle.go @@ -14,16 +14,21 @@ import ( // BeadsMessage represents a message from gt mail inbox --json. type BeadsMessage struct { - ID string `json:"id"` - From string `json:"from"` - To string `json:"to"` - Subject string `json:"subject"` - Body string `json:"body"` - Read bool `json:"read"` - Priority string `json:"priority"` - Type string `json:"type"` + ID string `json:"id"` + From string `json:"from"` + To string `json:"to"` + Subject string `json:"subject"` + Body string `json:"body"` + Timestamp string `json:"timestamp"` + Read bool `json:"read"` + Priority string `json:"priority"` + Type string `json:"type"` } +// MaxLifecycleMessageAge is the maximum age of a lifecycle message before it's ignored. +// Messages older than this are considered stale and deleted without execution. +const MaxLifecycleMessageAge = 6 * time.Hour + // ProcessLifecycleRequests checks for and processes lifecycle requests from the deacon inbox. func (d *Daemon) ProcessLifecycleRequests() { // Get mail for deacon identity (using gt mail, not bd mail) @@ -56,6 +61,19 @@ func (d *Daemon) ProcessLifecycleRequests() { continue // Not a lifecycle request } + // Check message age - ignore stale lifecycle requests + if msgTime, err := time.Parse(time.RFC3339, msg.Timestamp); err == nil { + age := time.Since(msgTime) + if age > MaxLifecycleMessageAge { + d.logger.Printf("Ignoring stale lifecycle request from %s (age: %v, max: %v) - deleting", + request.From, age.Round(time.Minute), MaxLifecycleMessageAge) + if err := d.closeMessage(msg.ID); err != nil { + d.logger.Printf("Warning: failed to delete stale message %s: %v", msg.ID, err) + } + continue + } + } + d.logger.Printf("Processing lifecycle request from %s: %s", request.From, request.Action) // CRITICAL: Delete message FIRST, before executing action.