fix(memory-flush): ban timestamped variant files in default flush prompt (#34951)

Merged via squash.

Prepared head SHA: efadda4988b460e6da07be72994d4951d64239d0
Co-authored-by: zerone0x <39543393+zerone0x@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
This commit is contained in:
zerone0x
2026-03-06 10:15:13 +08:00
committed by GitHub
parent 8088218f46
commit 94fdee2eac
5 changed files with 27 additions and 3 deletions

View File

@@ -158,6 +158,7 @@ Docs: https://docs.openclaw.ai
- TUI/webchat command-owner scope alignment: treat internal-channel gateway sessions with `operator.admin` as owner-authorized in command auth, restoring cron/gateway/connector tool access for affected TUI/webchat sessions while keeping external channels on identity-based owner checks. (from #35666, #35673, #35704) Thanks @Naylenv, @Octane0411, and @Sid-Qin.
- Discord/inbound timeout isolation: separate inbound worker timeout tracking from listener timeout budgets so queued Discord replies are no longer dropped when listener watchdog windows expire mid-run. (#36602) Thanks @dutifulbob.
- Memory/doctor SecretRef handling: treat SecretRef-backed memory-search API keys as configured, and fail embedding setup with explicit unresolved-secret errors instead of crashing. (#36835) Thanks @joshavant.
- Memory/flush default prompt: ban timestamped variant filenames during default memory flush runs so durable notes stay in the canonical daily `memory/YYYY-MM-DD.md` file. (#34951) thanks @zerone0x.
## 2026.3.2

View File

@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
import { resolveMemoryFlushPromptForRun } from "./memory-flush.js";
import { DEFAULT_MEMORY_FLUSH_PROMPT, resolveMemoryFlushPromptForRun } from "./memory-flush.js";
describe("resolveMemoryFlushPromptForRun", () => {
const cfg = {
@@ -36,3 +36,16 @@ describe("resolveMemoryFlushPromptForRun", () => {
expect((prompt.match(/Current time:/g) ?? []).length).toBe(1);
});
});
describe("DEFAULT_MEMORY_FLUSH_PROMPT", () => {
it("includes append-only instruction to prevent overwrites (#6877)", () => {
expect(DEFAULT_MEMORY_FLUSH_PROMPT).toMatch(/APPEND/i);
expect(DEFAULT_MEMORY_FLUSH_PROMPT).toContain("do not overwrite");
});
it("includes anti-fragmentation instruction to prevent timestamped variant files (#34919)", () => {
// Agents must not create YYYY-MM-DD-HHMM.md variants alongside the canonical file
expect(DEFAULT_MEMORY_FLUSH_PROMPT).toContain("timestamped variant");
expect(DEFAULT_MEMORY_FLUSH_PROMPT).toContain("YYYY-MM-DD.md");
});
});

View File

@@ -13,7 +13,8 @@ export const DEFAULT_MEMORY_FLUSH_FORCE_TRANSCRIPT_BYTES = 2 * 1024 * 1024;
export const DEFAULT_MEMORY_FLUSH_PROMPT = [
"Pre-compaction memory flush.",
"Store durable memories now (use memory/YYYY-MM-DD.md; create memory/ if needed).",
"IMPORTANT: If the file already exists, APPEND new content only and do not overwrite existing entries.",
"IMPORTANT: If the file already exists, APPEND new content only do not overwrite existing entries.",
"Do NOT create timestamped variant files (e.g., YYYY-MM-DD-HHMM.md); always use the canonical YYYY-MM-DD.md filename.",
`If nothing to store, reply with ${SILENT_REPLY_TOKEN}.`,
].join(" ");

View File

@@ -1,4 +1,5 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { captureFullEnv } from "../../test-utils/env.js";
import type { DaemonActionResponse } from "./response.js";
const loadConfigMock = vi.hoisted(() => vi.fn());
@@ -118,6 +119,7 @@ vi.mock("../../runtime.js", () => ({
}));
const { runDaemonInstall } = await import("./install.js");
const envSnapshot = captureFullEnv();
describe("runDaemonInstall", () => {
beforeEach(() => {
@@ -162,6 +164,12 @@ describe("runDaemonInstall", () => {
isGatewayDaemonRuntimeMock.mockReturnValue(true);
installDaemonServiceAndEmitMock.mockResolvedValue(undefined);
service.isLoaded.mockResolvedValue(false);
delete process.env.OPENCLAW_GATEWAY_TOKEN;
delete process.env.CLAWDBOT_GATEWAY_TOKEN;
});
afterEach(() => {
envSnapshot.restore();
});
it("fails install when token auth requires an unresolved token SecretRef", async () => {

View File

@@ -68,6 +68,7 @@ describe("restartGatewayProcessWithFreshPid", () => {
});
it("returns supervised when launchd/systemd hints are present", () => {
clearSupervisorHints();
process.env.LAUNCH_JOB_LABEL = "ai.openclaw.gateway";
const result = restartGatewayProcessWithFreshPid();
expect(result.mode).toBe("supervised");