fix: don't auto-create HEARTBEAT.md on workspace init (openclaw#12027) thanks @shadril238

Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: shadril238 <63901551+shadril238@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
Shadril Hassan Shifat
2026-02-14 06:24:22 +06:00
committed by GitHub
parent 0a724127dc
commit 386bb0c618
3 changed files with 25 additions and 6 deletions

View File

@@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Agents/Heartbeat: stop auto-creating `HEARTBEAT.md` during workspace bootstrap so missing files continue to run heartbeat as documented. (#11766) Thanks @shadril238.
- CLI: lazily load outbound provider dependencies and remove forced success-path exits so commands terminate naturally without killing intentional long-running foreground actions. (#12906) Thanks @DrCrinkle.
- Clawdock: avoid Zsh readonly variable collisions in helper scripts. (#15501) Thanks @nkelner.
- Discord: route autoThread replies to existing threads instead of the root channel. (#8302) Thanks @gavinbmoore, @thewilloftheshadow.

View File

@@ -1,9 +1,14 @@
import fs from "node:fs/promises";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { makeTempWorkspace, writeWorkspaceFile } from "../test-helpers/workspace.js";
import {
DEFAULT_AGENTS_FILENAME,
DEFAULT_BOOTSTRAP_FILENAME,
DEFAULT_HEARTBEAT_FILENAME,
DEFAULT_MEMORY_ALT_FILENAME,
DEFAULT_MEMORY_FILENAME,
ensureAgentWorkspace,
loadWorkspaceBootstrapFiles,
resolveDefaultAgentWorkspaceDir,
} from "./workspace.js";
@@ -19,6 +24,21 @@ describe("resolveDefaultAgentWorkspaceDir", () => {
});
});
describe("ensureAgentWorkspace", () => {
it("does not create HEARTBEAT.md during bootstrap file initialization", async () => {
const tempDir = await makeTempWorkspace("openclaw-workspace-init-");
const result = await ensureAgentWorkspace({ dir: tempDir, ensureBootstrapFiles: true });
await expect(fs.access(path.join(tempDir, DEFAULT_AGENTS_FILENAME))).resolves.toBeUndefined();
await expect(
fs.access(path.join(tempDir, DEFAULT_BOOTSTRAP_FILENAME)),
).resolves.toBeUndefined();
await expect(fs.access(path.join(tempDir, DEFAULT_HEARTBEAT_FILENAME))).rejects.toThrow();
expect("heartbeatPath" in result).toBe(false);
});
});
describe("loadWorkspaceBootstrapFiles", () => {
it("includes MEMORY.md when present", async () => {
const tempDir = await makeTempWorkspace("openclaw-workspace-");

View File

@@ -173,7 +173,6 @@ export async function ensureAgentWorkspace(params?: {
toolsPath?: string;
identityPath?: string;
userPath?: string;
heartbeatPath?: string;
bootstrapPath?: string;
}> {
const rawDir = params?.dir?.trim() ? params.dir.trim() : DEFAULT_AGENT_WORKSPACE_DIR;
@@ -189,11 +188,13 @@ export async function ensureAgentWorkspace(params?: {
const toolsPath = path.join(dir, DEFAULT_TOOLS_FILENAME);
const identityPath = path.join(dir, DEFAULT_IDENTITY_FILENAME);
const userPath = path.join(dir, DEFAULT_USER_FILENAME);
const heartbeatPath = path.join(dir, DEFAULT_HEARTBEAT_FILENAME);
// HEARTBEAT.md is intentionally NOT created from template.
// Per docs: "If the file is missing, the heartbeat still runs and the model decides what to do."
// Creating it from template (which is effectively empty) would cause heartbeat to be skipped.
const bootstrapPath = path.join(dir, DEFAULT_BOOTSTRAP_FILENAME);
const isBrandNewWorkspace = await (async () => {
const paths = [agentsPath, soulPath, toolsPath, identityPath, userPath, heartbeatPath];
const paths = [agentsPath, soulPath, toolsPath, identityPath, userPath];
const existing = await Promise.all(
paths.map(async (p) => {
try {
@@ -212,7 +213,6 @@ export async function ensureAgentWorkspace(params?: {
const toolsTemplate = await loadTemplate(DEFAULT_TOOLS_FILENAME);
const identityTemplate = await loadTemplate(DEFAULT_IDENTITY_FILENAME);
const userTemplate = await loadTemplate(DEFAULT_USER_FILENAME);
const heartbeatTemplate = await loadTemplate(DEFAULT_HEARTBEAT_FILENAME);
const bootstrapTemplate = await loadTemplate(DEFAULT_BOOTSTRAP_FILENAME);
await writeFileIfMissing(agentsPath, agentsTemplate);
@@ -220,7 +220,6 @@ export async function ensureAgentWorkspace(params?: {
await writeFileIfMissing(toolsPath, toolsTemplate);
await writeFileIfMissing(identityPath, identityTemplate);
await writeFileIfMissing(userPath, userTemplate);
await writeFileIfMissing(heartbeatPath, heartbeatTemplate);
if (isBrandNewWorkspace) {
await writeFileIfMissing(bootstrapPath, bootstrapTemplate);
}
@@ -233,7 +232,6 @@ export async function ensureAgentWorkspace(params?: {
toolsPath,
identityPath,
userPath,
heartbeatPath,
bootstrapPath,
};
}