fix(memory-qmd): streamline compatibility coverage

This commit is contained in:
Vincent Koc
2026-04-06 01:51:23 +01:00
parent f6dbcf4cda
commit 7c9108aaf7
7 changed files with 25 additions and 48 deletions

View File

@@ -0,0 +1,13 @@
import { describe, expect, it } from "vitest";
import { resolveQmdCollectionPatternFlags } from "./qmd-compat.js";
describe("resolveQmdCollectionPatternFlags", () => {
it("prefers modern --glob by default and falls back to legacy --mask", () => {
expect(resolveQmdCollectionPatternFlags(null)).toEqual(["--glob", "--mask"]);
expect(resolveQmdCollectionPatternFlags("--glob")).toEqual(["--glob", "--mask"]);
});
it("keeps preferring legacy --mask after a legacy-only qmd succeeds", () => {
expect(resolveQmdCollectionPatternFlags("--mask")).toEqual(["--mask", "--glob"]);
});
});

View File

@@ -0,0 +1,7 @@
export type QmdCollectionPatternFlag = "--glob" | "--mask";
export function resolveQmdCollectionPatternFlags(
preferredFlag: QmdCollectionPatternFlag | null,
): QmdCollectionPatternFlag[] {
return preferredFlag === "--mask" ? ["--mask", "--glob"] : ["--glob", "--mask"];
}

View File

@@ -895,49 +895,6 @@ describe("QmdMemoryManager", () => {
);
});
it("prefers --glob for collection add and falls back to --mask when --glob is rejected", async () => {
cfg = {
...cfg,
memory: {
backend: "qmd",
qmd: {
includeDefaultMemory: true,
update: { interval: "0s", debounceMs: 60_000, onBoot: false },
paths: [],
},
},
} as OpenClawConfig;
const addFlagCalls: string[] = [];
spawnMock.mockImplementation((_cmd: string, args: string[]) => {
if (args[0] === "collection" && args[1] === "list") {
const child = createMockChild({ autoClose: false });
emitAndClose(child, "stdout", "[]");
return child;
}
if (args[0] === "collection" && args[1] === "add") {
const child = createMockChild({ autoClose: false });
const flag = args.includes("--glob") ? "--glob" : args.includes("--mask") ? "--mask" : "";
addFlagCalls.push(flag);
if (flag === "--glob") {
emitAndClose(child, "stderr", "unknown flag: --glob", 1);
return child;
}
queueMicrotask(() => child.closeWith(0));
return child;
}
return createMockChild();
});
const { manager } = await createManager({ mode: "full" });
await manager.close();
expect(addFlagCalls).toEqual(["--glob", "--mask", "--mask", "--mask"]);
expect(logWarnMock).toHaveBeenCalledWith(
expect.stringContaining("retrying with legacy compatibility flag"),
);
});
it("migrates unscoped legacy collections from plain-text collection list output", async () => {
cfg = {
...cfg,

View File

@@ -41,6 +41,7 @@ import {
type ResolvedQmdConfig,
type ResolvedQmdMcporterConfig,
} from "openclaw/plugin-sdk/memory-core-host-engine-storage";
import { resolveQmdCollectionPatternFlags, type QmdCollectionPatternFlag } from "./qmd-compat.js";
type SqliteDatabase = import("node:sqlite").DatabaseSync;
@@ -165,7 +166,6 @@ type ManagedCollection = {
};
type QmdManagerMode = "full" | "status";
type QmdCollectionPatternFlag = "--glob" | "--mask";
type BuiltinQmdMcpTool = "query" | "search" | "vector_search" | "deep_search";
type QmdMcporterSearchParams =
| {
@@ -652,8 +652,7 @@ export class QmdMemoryManager implements MemorySearchManager {
}
private async addCollection(pathArg: string, name: string, pattern: string): Promise<void> {
const candidateFlags: QmdCollectionPatternFlag[] =
this.collectionPatternFlag === "--glob" ? ["--glob", "--mask"] : ["--mask", "--glob"];
const candidateFlags = resolveQmdCollectionPatternFlags(this.collectionPatternFlag);
let lastError: unknown;
for (const flag of candidateFlags) {
try {