fix: check restart/shutdown before cycle in lifecycle parser
The parseLifecycleRequest function was checking for "cycle" first, but since the title already contains "lifecycle:" (which includes "cycle"), all lifecycle messages matched as cycle actions, making restart and shutdown unreachable. Fixed by: 1. Checking restart/shutdown before cycle 2. Using " cycle" (with leading space) to match the word, not prefix Closes gt-rixa 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -82,12 +82,15 @@ func (d *Daemon) parseLifecycleRequest(msg *BeadsMessage) *LifecycleRequest {
|
||||
var action LifecycleAction
|
||||
var from string
|
||||
|
||||
if strings.Contains(title, "cycle") || strings.Contains(title, "cycling") {
|
||||
action = ActionCycle
|
||||
} else if strings.Contains(title, "restart") {
|
||||
// Check restart/shutdown before cycle.
|
||||
// Note: Can't use Contains(title, "cycle") because "lifecycle:" contains "cycle".
|
||||
// Use " cycle" (with leading space) to match the word, not the prefix.
|
||||
if strings.Contains(title, "restart") {
|
||||
action = ActionRestart
|
||||
} else if strings.Contains(title, "shutdown") || strings.Contains(title, "stop") {
|
||||
action = ActionShutdown
|
||||
} else if strings.Contains(title, " cycle") || strings.Contains(title, "cycling") {
|
||||
action = ActionCycle
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -23,9 +23,6 @@ func TestParseLifecycleRequest_Cycle(t *testing.T) {
|
||||
{"LIFECYCLE: mayor requesting cycle", ActionCycle},
|
||||
{"lifecycle: gastown-witness requesting cycling", ActionCycle},
|
||||
{"LIFECYCLE: witness requesting cycle now", ActionCycle},
|
||||
// NOTE: Due to implementation detail, "lifecycle" contains "cycle",
|
||||
// so any LIFECYCLE: message matches cycle first. This test documents
|
||||
// current behavior. See TestParseLifecycleRequest_PrefixMatchesCycle.
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
@@ -44,22 +41,20 @@ func TestParseLifecycleRequest_Cycle(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLifecycleRequest_PrefixMatchesCycle(t *testing.T) {
|
||||
// NOTE: This test documents a quirk in the implementation:
|
||||
// The word "lifecycle" contains "cycle", so when parsing checks
|
||||
// strings.Contains(title, "cycle"), ALL lifecycle: messages match.
|
||||
// This means restart and shutdown are effectively unreachable via
|
||||
// the current implementation. This test documents actual behavior.
|
||||
func TestParseLifecycleRequest_RestartAndShutdown(t *testing.T) {
|
||||
// Verify that restart and shutdown are correctly parsed.
|
||||
// Previously, the "lifecycle:" prefix contained "cycle", which caused
|
||||
// all messages to match as cycle. Fixed by checking restart/shutdown
|
||||
// before cycle, and using " cycle" (with space) to avoid prefix match.
|
||||
d := testDaemon()
|
||||
|
||||
tests := []struct {
|
||||
title string
|
||||
expected LifecycleAction
|
||||
}{
|
||||
// These all match "cycle" due to "lifecycle" containing "cycle"
|
||||
{"LIFECYCLE: mayor requesting restart", ActionCycle},
|
||||
{"LIFECYCLE: mayor requesting shutdown", ActionCycle},
|
||||
{"lifecycle: witness requesting stop", ActionCycle},
|
||||
{"LIFECYCLE: mayor requesting restart", ActionRestart},
|
||||
{"LIFECYCLE: mayor requesting shutdown", ActionShutdown},
|
||||
{"lifecycle: witness requesting stop", ActionShutdown},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
@@ -73,7 +68,7 @@ func TestParseLifecycleRequest_PrefixMatchesCycle(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
if result.Action != tc.expected {
|
||||
t.Errorf("parseLifecycleRequest(%q) action = %s, expected %s (documents current behavior)", tc.title, result.Action, tc.expected)
|
||||
t.Errorf("parseLifecycleRequest(%q) action = %s, expected %s", tc.title, result.Action, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user