From 288013ca0f5e702e84d34349a301b47821187706 Mon Sep 17 00:00:00 2001
From: Alix-007
Date: Wed, 24 Jun 2026 23:29:46 +0800
Subject: [PATCH] fix(evolution): skip cold path for heartbeat turns
---
pkg/agent/evolution_bridge.go | 7 +++++++
pkg/agent/evolution_bridge_test.go | 25 +++++++++++++++++++++++++
2 files changed, 32 insertions(+)
diff --git a/pkg/agent/evolution_bridge.go b/pkg/agent/evolution_bridge.go
index 2e54c869..134ec53d 100644
--- a/pkg/agent/evolution_bridge.go
+++ b/pkg/agent/evolution_bridge.go
@@ -202,6 +202,9 @@ func (b *evolutionBridge) handleTurnEndAsync(meta EventMeta, payload TurnEndPayl
FinalSuccessfulPath: append([]string(nil), payload.FinalSuccessfulPath...),
SkillContextSnapshots: toEvolutionSkillContextSnapshots(payload.SkillContextSnapshots),
}
+ if isEvolutionHeartbeatInput(input) {
+ return false
+ }
b.rememberScheduledColdPathWorkspace(input.Workspace)
b.closeMu.Lock()
@@ -228,6 +231,10 @@ func (b *evolutionBridge) handleTurnEndAsync(meta EventMeta, payload TurnEndPayl
return true
}
+func isEvolutionHeartbeatInput(input evolution.TurnCaseInput) bool {
+ return strings.EqualFold(strings.TrimSpace(input.SessionKey), "heartbeat")
+}
+
func (b *evolutionBridge) subscribeRuntimeEvents(ch runtimeevents.EventChannel) error {
if b == nil || ch == nil {
return nil
diff --git a/pkg/agent/evolution_bridge_test.go b/pkg/agent/evolution_bridge_test.go
index 8469acd8..cf142f7d 100644
--- a/pkg/agent/evolution_bridge_test.go
+++ b/pkg/agent/evolution_bridge_test.go
@@ -513,6 +513,31 @@ func TestEvolutionBridge_DraftModeAutomaticallyRunsColdPathAndCreatesDraftFile(t
waitForDrafts(t, filepath.Join(tmpDir, "state", "evolution", "skill-drafts.json"), 1)
}
+func TestEvolutionBridge_DraftModeDoesNotRunColdPathForHeartbeat(t *testing.T) {
+ tmpDir := t.TempDir()
+ seedReadyRule(t, tmpDir)
+
+ al := newEvolutionTestLoop(t, tmpDir, config.EvolutionConfig{
+ Enabled: true,
+ Mode: "draft",
+ }, &simpleMockProvider{
+ response: `{"target_skill_name":"weather","draft_type":"shortcut","change_kind":"append","human_summary":"unexpected heartbeat draft","body_or_patch":"## Unexpected"}`,
+ })
+ defer al.Close()
+
+ resp, err := al.ProcessHeartbeat(context.Background(), "check heartbeat tasks", "telegram", "chat-1")
+ if err != nil {
+ t.Fatalf("ProcessHeartbeat failed: %v", err)
+ }
+ if resp == "" {
+ t.Fatal("expected non-empty heartbeat response")
+ }
+
+ time.Sleep(150 * time.Millisecond)
+ assertNotExists(t, filepath.Join(tmpDir, "state", "evolution", "task-records.jsonl"))
+ assertNotExists(t, filepath.Join(tmpDir, "state", "evolution", "skill-drafts.json"))
+}
+
func TestEvolutionBridge_ScheduledModeDoesNotRunColdPathAfterTurn(t *testing.T) {
tmpDir := t.TempDir()
seedReadyRule(t, tmpDir)