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)