mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 02:22:43 +00:00
feat: add post-compaction read audit (Layer 3)
This commit is contained in:
197
src/auto-reply/reply/post-compaction-audit.test.ts
Normal file
197
src/auto-reply/reply/post-compaction-audit.test.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import {
|
||||
auditPostCompactionReads,
|
||||
extractReadPaths,
|
||||
formatAuditWarning,
|
||||
} from "./post-compaction-audit.js";
|
||||
|
||||
describe("extractReadPaths", () => {
|
||||
it("extracts file paths from Read tool calls", () => {
|
||||
const messages = [
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "tool_use",
|
||||
name: "read",
|
||||
input: { file_path: "WORKFLOW_AUTO.md" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "tool_use",
|
||||
name: "read",
|
||||
input: { file_path: "memory/2026-02-16.md" },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const paths = extractReadPaths(messages);
|
||||
expect(paths).toEqual(["WORKFLOW_AUTO.md", "memory/2026-02-16.md"]);
|
||||
});
|
||||
|
||||
it("handles path parameter (alternative to file_path)", () => {
|
||||
const messages = [
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "tool_use",
|
||||
name: "read",
|
||||
input: { path: "AGENTS.md" },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const paths = extractReadPaths(messages);
|
||||
expect(paths).toEqual(["AGENTS.md"]);
|
||||
});
|
||||
|
||||
it("ignores non-assistant messages", () => {
|
||||
const messages = [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "tool_use",
|
||||
name: "read",
|
||||
input: { file_path: "should_be_ignored.md" },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const paths = extractReadPaths(messages);
|
||||
expect(paths).toEqual([]);
|
||||
});
|
||||
|
||||
it("ignores non-read tool calls", () => {
|
||||
const messages = [
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "tool_use",
|
||||
name: "exec",
|
||||
input: { command: "cat WORKFLOW_AUTO.md" },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const paths = extractReadPaths(messages);
|
||||
expect(paths).toEqual([]);
|
||||
});
|
||||
|
||||
it("handles empty messages array", () => {
|
||||
const paths = extractReadPaths([]);
|
||||
expect(paths).toEqual([]);
|
||||
});
|
||||
|
||||
it("handles messages with non-array content", () => {
|
||||
const messages = [
|
||||
{
|
||||
role: "assistant",
|
||||
content: "text only",
|
||||
},
|
||||
];
|
||||
|
||||
const paths = extractReadPaths(messages);
|
||||
expect(paths).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("auditPostCompactionReads", () => {
|
||||
const workspaceDir = "/Users/test/workspace";
|
||||
|
||||
it("passes when all required files are read", () => {
|
||||
const readPaths = ["WORKFLOW_AUTO.md", "memory/2026-02-16.md"];
|
||||
const result = auditPostCompactionReads(readPaths, workspaceDir);
|
||||
|
||||
expect(result.passed).toBe(true);
|
||||
expect(result.missingPatterns).toEqual([]);
|
||||
});
|
||||
|
||||
it("fails when no files are read", () => {
|
||||
const result = auditPostCompactionReads([], workspaceDir);
|
||||
|
||||
expect(result.passed).toBe(false);
|
||||
expect(result.missingPatterns).toContain("WORKFLOW_AUTO.md");
|
||||
expect(result.missingPatterns.some((p) => p.includes("memory"))).toBe(true);
|
||||
});
|
||||
|
||||
it("reports only missing files", () => {
|
||||
const readPaths = ["WORKFLOW_AUTO.md"];
|
||||
const result = auditPostCompactionReads(readPaths, workspaceDir);
|
||||
|
||||
expect(result.passed).toBe(false);
|
||||
expect(result.missingPatterns).not.toContain("WORKFLOW_AUTO.md");
|
||||
expect(result.missingPatterns.some((p) => p.includes("memory"))).toBe(true);
|
||||
});
|
||||
|
||||
it("matches RegExp patterns against relative paths", () => {
|
||||
const readPaths = ["memory/2026-02-16.md"];
|
||||
const result = auditPostCompactionReads(readPaths, workspaceDir);
|
||||
|
||||
expect(result.passed).toBe(false);
|
||||
expect(result.missingPatterns).toContain("WORKFLOW_AUTO.md");
|
||||
expect(result.missingPatterns.length).toBe(1);
|
||||
});
|
||||
|
||||
it("normalizes relative paths when matching", () => {
|
||||
const readPaths = ["./WORKFLOW_AUTO.md", "memory/2026-02-16.md"];
|
||||
const result = auditPostCompactionReads(readPaths, workspaceDir);
|
||||
|
||||
expect(result.passed).toBe(true);
|
||||
expect(result.missingPatterns).toEqual([]);
|
||||
});
|
||||
|
||||
it("normalizes absolute paths when matching", () => {
|
||||
const readPaths = [
|
||||
"/Users/test/workspace/WORKFLOW_AUTO.md",
|
||||
"/Users/test/workspace/memory/2026-02-16.md",
|
||||
];
|
||||
const result = auditPostCompactionReads(readPaths, workspaceDir);
|
||||
|
||||
expect(result.passed).toBe(true);
|
||||
expect(result.missingPatterns).toEqual([]);
|
||||
});
|
||||
|
||||
it("accepts custom required reads list", () => {
|
||||
const readPaths = ["custom.md"];
|
||||
const customRequired = ["custom.md"];
|
||||
const result = auditPostCompactionReads(readPaths, workspaceDir, customRequired);
|
||||
|
||||
expect(result.passed).toBe(true);
|
||||
expect(result.missingPatterns).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatAuditWarning", () => {
|
||||
it("formats warning message with missing patterns", () => {
|
||||
const missingPatterns = ["WORKFLOW_AUTO.md", "memory\\/\\d{4}-\\d{2}-\\d{2}\\.md"];
|
||||
const message = formatAuditWarning(missingPatterns);
|
||||
|
||||
expect(message).toContain("⚠️ Post-Compaction Audit");
|
||||
expect(message).toContain("WORKFLOW_AUTO.md");
|
||||
expect(message).toContain("memory");
|
||||
expect(message).toContain("Please read them now");
|
||||
});
|
||||
|
||||
it("formats single missing pattern", () => {
|
||||
const missingPatterns = ["WORKFLOW_AUTO.md"];
|
||||
const message = formatAuditWarning(missingPatterns);
|
||||
|
||||
expect(message).toContain("WORKFLOW_AUTO.md");
|
||||
// Check that the missing patterns list only contains WORKFLOW_AUTO.md
|
||||
const lines = message.split("\n");
|
||||
const patternLines = lines.filter((l) => l.trim().startsWith("- "));
|
||||
expect(patternLines).toHaveLength(1);
|
||||
expect(patternLines[0]).toContain("WORKFLOW_AUTO.md");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user