diff --git a/src/heartbeat/runner.test.ts b/src/heartbeat/runner.test.ts index 8a3570a3..1a04bc5f 100644 --- a/src/heartbeat/runner.test.ts +++ b/src/heartbeat/runner.test.ts @@ -65,6 +65,18 @@ describe("heartbeat runner", () => { } }); + it("bypasses empty-heartbeat-file check for cron-triggered wakes", async () => { + const dir = await mkdtemp(path.join(os.tmpdir(), "heartbeat-test-")); + try { + await writeFile(path.join(dir, "heartbeat.md"), "# keep empty\n", "utf-8"); + const agent = createStubAgent({ profileDir: dir, replyText: "HEARTBEAT_OK" }); + const result = await runHeartbeatOnce({ agent: agent as any, reason: "cron:test-job-id" }); + expect(result.status).toBe("ran"); + } finally { + await rm(dir, { recursive: true, force: true }); + } + }); + it("runs and returns ran for heartbeat acknowledgements", async () => { const agent = createStubAgent({ replyText: "HEARTBEAT_OK" }); const result = await runHeartbeatOnce({ agent: agent as any, reason: "manual" }); diff --git a/src/heartbeat/runner.ts b/src/heartbeat/runner.ts index f9ba6bfa..7e6dab53 100644 --- a/src/heartbeat/runner.ts +++ b/src/heartbeat/runner.ts @@ -156,8 +156,8 @@ export async function runHeartbeatOnce(opts: { } try { - const isExecEvent = opts.reason === "exec-event"; - if (!isExecEvent && (await isHeartbeatFileEmpty(agent))) { + const isForcedWake = opts.reason === "exec-event" || opts.reason?.startsWith("cron:"); + if (!isForcedWake && (await isHeartbeatFileEmpty(agent))) { emitHeartbeatEvent({ status: "skipped", reason: "empty-heartbeat-file",