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.