fix(memory-core): filter stale recall entries in REM harness preview

This commit is contained in:
Vincent Koc
2026-06-10 14:35:44 +09:00
committed by GitHub
parent c7b4c6bfc5
commit 92418fc9da
3 changed files with 78 additions and 4 deletions

View File

@@ -45,6 +45,7 @@ Docs: https://docs.openclaw.ai
- Release/CI/E2E: Docker E2E and live Docker harness runs now apply default memory, CPU, and process ceilings while preserving explicit per-lane overrides.
- Release/CI/E2E: plugin lifecycle matrix resource sampling now fails phases that exceed RSS, wall-clock, or CPU ceilings instead of only logging the measurements.
- Release/CI/E2E: Codex npm plugin live assertions now cap transcript discovery and diagnostic log reads so failure proof stays bounded.
- Memory: keep doctor REM harness previews aligned with live REM by dropping short-term recall snippets whose source files disappeared before rendering preview output. Thanks @samzong and @frankekn.
- Tests/state isolation: QA Lab valid-tool-call metrics now require runtime tool-call evidence when runtime parity data is available instead of counting tool-backed scenario pass status alone.
- Tests/state isolation: QA Lab runtime parity now fails planned-only tool-call rows without matching tool results instead of treating matching mock plans as real tool evidence.
- Tests/state isolation: provider, media, auth, cron, task, session, sandbox, Gateway, and Codex timeout fixtures now scope more home/state/env data per test, reducing cross-test leakage and making release validation failures less noisy. (#90027, #89974)

View File

@@ -2916,6 +2916,75 @@ describe("previewRemHarness", () => {
expect(preview.grounded).toBeNull();
});
it("skips REM short-term candidates whose source file disappeared", async () => {
const workspaceDir = await createDreamingWorkspace();
const nowMs = new Date("2026-04-15T12:00:00.000Z").getTime();
await fs.writeFile(
path.join(workspaceDir, "memory", "2026-04-14.md"),
"Move backups to S3 Glacier.\n",
"utf-8",
);
await recordShortTermRecalls({
workspaceDir,
query: "live backup",
nowMs,
results: [
{
path: "memory/2026-04-14.md",
startLine: 1,
endLine: 1,
score: 0.91,
snippet: "Move backups to S3 Glacier.",
source: "memory",
},
],
});
await recordShortTermRecalls({
workspaceDir,
query: "stale provider setup",
nowMs,
results: [
{
path: "memory/.dreams/session-corpus/2026-04-16.txt",
startLine: 2,
endLine: 2,
score: 0.88,
snippet: "Assistant: Documented Ollama provider setup.",
source: "memory",
},
],
});
const preview = await previewRemHarness({
workspaceDir,
nowMs,
pluginConfig: {
dreaming: {
enabled: true,
phases: {
rem: {
enabled: true,
lookbackDays: 7,
limit: 10,
minPatternStrength: 0,
},
},
},
},
});
const candidateTruthSnippets = preview.rem.candidateTruths
.map((entry) => entry.snippet)
.join("\n");
const bodyText = preview.rem.bodyLines.join("\n");
expect(preview.recallEntryCount).toBe(1);
expect(preview.rem.sourceEntryCount).toBe(1);
expect(candidateTruthSnippets).toContain("Move backups to S3 Glacier.");
expect(candidateTruthSnippets).not.toContain("Documented Ollama provider setup");
expect(bodyText).toContain("Move backups to S3 Glacier.");
expect(bodyText).not.toContain("Documented Ollama provider setup");
});
it("skips REM preview when rem.limit=0 while still ranking deep candidates", async () => {
const workspaceDir = await createDreamingWorkspace();
const nowMs = new Date("2026-04-15T12:00:00.000Z").getTime();

View File

@@ -13,6 +13,7 @@ import {
} from "./dreaming-phases.js";
import { previewGroundedRemMarkdown, type GroundedRemPreviewResult } from "./rem-evidence.js";
import {
filterLiveShortTermRecallEntries,
rankShortTermPromotionCandidates,
readShortTermRecallEntries,
type PromotionCandidate,
@@ -131,10 +132,13 @@ export async function previewRemHarness(
workspaceDir: params.workspaceDir,
nowMs,
});
const recallEntries = filterRecallEntriesWithinLookback({
entries: allRecallEntries,
nowMs,
lookbackDays: remConfig.lookbackDays,
const recallEntries = await filterLiveShortTermRecallEntries({
workspaceDir: params.workspaceDir,
entries: filterRecallEntriesWithinLookback({
entries: allRecallEntries,
nowMs,
lookbackDays: remConfig.lookbackDays,
}),
});
const remPreviewLimit = resolveRemPreviewLimit(remConfig.limit, params.remPreviewLimit);
const remSkipped = remConfig.limit <= 0 || remPreviewLimit <= 0;