Thread tracking, decision extraction, boot context generation, pre-compaction snapshots, structured narratives. - 10 source files, 1983 LOC TypeScript - 9 test files, 270 tests passing - Zero runtime dependencies - Cerberus approved + all findings fixed - EN/DE pattern matching, atomic file writes - Graceful degradation (read-only workspace, corrupt JSON)
167 lines
6 KiB
TypeScript
167 lines
6 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { mkdtempSync, mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { tmpdir } from "node:os";
|
|
import { PreCompaction, buildHotSnapshot } from "../src/pre-compaction.js";
|
|
import { ThreadTracker } from "../src/thread-tracker.js";
|
|
import { resolveConfig } from "../src/config.js";
|
|
|
|
const logger = {
|
|
info: () => {},
|
|
warn: () => {},
|
|
error: () => {},
|
|
debug: () => {},
|
|
};
|
|
|
|
function makeWorkspace(): string {
|
|
const ws = mkdtempSync(join(tmpdir(), "cortex-precompact-"));
|
|
mkdirSync(join(ws, "memory", "reboot"), { recursive: true });
|
|
return ws;
|
|
}
|
|
|
|
describe("buildHotSnapshot", () => {
|
|
it("builds markdown from messages", () => {
|
|
const result = buildHotSnapshot([
|
|
{ role: "user", content: "Fix the auth bug" },
|
|
{ role: "assistant", content: "Done, JWT validation is fixed" },
|
|
], 15);
|
|
expect(result).toContain("Hot Snapshot");
|
|
expect(result).toContain("auth bug");
|
|
expect(result).toContain("[user]");
|
|
expect(result).toContain("[assistant]");
|
|
});
|
|
|
|
it("handles empty messages", () => {
|
|
const result = buildHotSnapshot([], 15);
|
|
expect(result).toContain("Hot Snapshot");
|
|
expect(result).toContain("No recent messages");
|
|
});
|
|
|
|
it("truncates long messages", () => {
|
|
const longMsg = "A".repeat(500);
|
|
const result = buildHotSnapshot([{ role: "user", content: longMsg }], 15);
|
|
expect(result).toContain("...");
|
|
expect(result.length).toBeLessThan(500);
|
|
});
|
|
|
|
it("limits to maxMessages (takes last N)", () => {
|
|
const messages = Array.from({ length: 20 }, (_, i) => ({
|
|
role: i % 2 === 0 ? "user" : "assistant",
|
|
content: `Message ${i}`,
|
|
}));
|
|
const result = buildHotSnapshot(messages, 5);
|
|
expect(result).toContain("Message 19");
|
|
expect(result).toContain("Message 15");
|
|
expect(result).not.toContain("Message 0");
|
|
});
|
|
});
|
|
|
|
describe("PreCompaction", () => {
|
|
it("creates instance without errors", () => {
|
|
const ws = makeWorkspace();
|
|
const config = resolveConfig({ workspace: ws });
|
|
const tracker = new ThreadTracker(ws, config.threadTracker, "both", logger);
|
|
const pipeline = new PreCompaction(ws, config, logger, tracker);
|
|
expect(pipeline).toBeTruthy();
|
|
});
|
|
|
|
it("runs without errors on empty workspace", () => {
|
|
const ws = makeWorkspace();
|
|
const config = resolveConfig({ workspace: ws });
|
|
const tracker = new ThreadTracker(ws, config.threadTracker, "both", logger);
|
|
const pipeline = new PreCompaction(ws, config, logger, tracker);
|
|
|
|
const result = pipeline.run([]);
|
|
expect(result.success).toBe(true);
|
|
expect(result.warnings).toHaveLength(0);
|
|
});
|
|
|
|
it("creates hot-snapshot.md", () => {
|
|
const ws = makeWorkspace();
|
|
const config = resolveConfig({ workspace: ws });
|
|
const tracker = new ThreadTracker(ws, config.threadTracker, "both", logger);
|
|
const pipeline = new PreCompaction(ws, config, logger, tracker);
|
|
|
|
pipeline.run([
|
|
{ role: "user", content: "Fix the auth bug" },
|
|
{ role: "assistant", content: "Done, the JWT validation is fixed" },
|
|
]);
|
|
|
|
const snapshotPath = join(ws, "memory", "reboot", "hot-snapshot.md");
|
|
expect(existsSync(snapshotPath)).toBe(true);
|
|
const content = readFileSync(snapshotPath, "utf-8");
|
|
expect(content).toContain("auth bug");
|
|
});
|
|
|
|
it("creates narrative.md", () => {
|
|
const ws = makeWorkspace();
|
|
const config = resolveConfig({ workspace: ws });
|
|
const tracker = new ThreadTracker(ws, config.threadTracker, "both", logger);
|
|
const pipeline = new PreCompaction(ws, config, logger, tracker);
|
|
|
|
pipeline.run([]);
|
|
|
|
const narrativePath = join(ws, "memory", "reboot", "narrative.md");
|
|
expect(existsSync(narrativePath)).toBe(true);
|
|
});
|
|
|
|
it("creates BOOTSTRAP.md", () => {
|
|
const ws = makeWorkspace();
|
|
const config = resolveConfig({ workspace: ws });
|
|
const tracker = new ThreadTracker(ws, config.threadTracker, "both", logger);
|
|
const pipeline = new PreCompaction(ws, config, logger, tracker);
|
|
|
|
pipeline.run([]);
|
|
|
|
const bootstrapPath = join(ws, "BOOTSTRAP.md");
|
|
expect(existsSync(bootstrapPath)).toBe(true);
|
|
});
|
|
|
|
it("reports correct messagesSnapshotted count", () => {
|
|
const ws = makeWorkspace();
|
|
const config = resolveConfig({ workspace: ws });
|
|
const tracker = new ThreadTracker(ws, config.threadTracker, "both", logger);
|
|
const pipeline = new PreCompaction(ws, config, logger, tracker);
|
|
|
|
const messages = Array.from({ length: 30 }, (_, i) => ({
|
|
role: "user" as const,
|
|
content: `Msg ${i}`,
|
|
}));
|
|
|
|
const result = pipeline.run(messages);
|
|
expect(result.messagesSnapshotted).toBe(config.preCompaction.maxSnapshotMessages);
|
|
});
|
|
|
|
it("handles errors gracefully — never throws", () => {
|
|
const ws = makeWorkspace();
|
|
writeFileSync(join(ws, "memory", "reboot", "threads.json"), "corrupt");
|
|
|
|
const config = resolveConfig({ workspace: ws });
|
|
const tracker = new ThreadTracker(ws, config.threadTracker, "both", logger);
|
|
const pipeline = new PreCompaction(ws, config, logger, tracker);
|
|
|
|
expect(() => pipeline.run([])).not.toThrow();
|
|
});
|
|
|
|
it("skips narrative when disabled", () => {
|
|
const ws = makeWorkspace();
|
|
const config = resolveConfig({ workspace: ws, narrative: { enabled: false } });
|
|
const tracker = new ThreadTracker(ws, config.threadTracker, "both", logger);
|
|
const pipeline = new PreCompaction(ws, config, logger, tracker);
|
|
|
|
pipeline.run([]);
|
|
// narrative.md should not be created (or at least pipeline won't error)
|
|
// The key assertion is it doesn't throw
|
|
});
|
|
|
|
it("skips boot context when disabled", () => {
|
|
const ws = makeWorkspace();
|
|
const config = resolveConfig({ workspace: ws, bootContext: { enabled: false } });
|
|
const tracker = new ThreadTracker(ws, config.threadTracker, "both", logger);
|
|
const pipeline = new PreCompaction(ws, config, logger, tracker);
|
|
|
|
pipeline.run([]);
|
|
const bootstrapPath = join(ws, "BOOTSTRAP.md");
|
|
expect(existsSync(bootstrapPath)).toBe(false);
|
|
});
|
|
});
|