From 4f687a7440bd64089d6a2a05cf22ed27c66e6c88 Mon Sep 17 00:00:00 2001 From: pvtclawn Date: Thu, 12 Feb 2026 22:52:32 +0000 Subject: [PATCH] fix: prevent ghost reminder notifications (#13317) The heartbeat runner was incorrectly triggering CRON_EVENT_PROMPT whenever ANY system events existed during a cron heartbeat, even if those events were unrelated (e.g., HEARTBEAT_OK acks, exec completions). This caused phantom 'scheduled reminder' notifications with no actual reminder content. Fix: Only treat as cron event if pending events contain actual cron-related messages, excluding standard heartbeat acks and exec completion messages. Fixes #13317 --- src/infra/heartbeat-runner.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/infra/heartbeat-runner.ts b/src/infra/heartbeat-runner.ts index 1771875c04e..5cb21e5dc4e 100644 --- a/src/infra/heartbeat-runner.ts +++ b/src/infra/heartbeat-runner.ts @@ -489,7 +489,18 @@ export async function runHeartbeatOnce(opts: { const isCronEvent = Boolean(opts.reason?.startsWith("cron:")); const pendingEvents = isExecEvent || isCronEvent ? peekSystemEvents(sessionKey) : []; const hasExecCompletion = pendingEvents.some((evt) => evt.includes("Exec finished")); - const hasCronEvents = isCronEvent && pendingEvents.length > 0; + + // Fix for #13317: Only treat as cron event if there are actual cron-related messages, + // not just any system events (which could be heartbeat acks, exec completions, etc.) + const hasCronEvents = isCronEvent && pendingEvents.some((evt) => { + const trimmed = evt.trim(); + // Exclude standard heartbeat acks and exec completion messages + return ( + trimmed.length > 0 && + !trimmed.includes("HEARTBEAT_OK") && + !trimmed.includes("Exec finished") + ); + }); const prompt = hasExecCompletion ? EXEC_EVENT_PROMPT : hasCronEvents