fix: avoid replaying superseded compaction summaries

This commit is contained in:
amosblomqvist
2026-10-26 19:08:15 +02:00
parent 3dc88472ee
commit b81f0a3455
6 changed files with 191 additions and 13 deletions
+44
View File
@@ -256,6 +256,22 @@ describe("toLlmMessages honors the cut", () => {
assert.equal(JSON.stringify(out).indexOf("COMPACTED]"), JSON.stringify(out).lastIndexOf("COMPACTED]"), "header appears exactly once");
});
it("omits nine superseded summaries inside the retained tail without changing stored history", () => {
const kept = newUserMessage("retained exchange");
const details = { firstKeptMessageId: kept.id, tokensBefore: 200_000, at: "fixture" };
const old = Array.from({ length: 9 }, (_, i) => newCompactionMarker(`SUPERSEDED-${i}`, details));
const fresh = newCompactionMarker("LATEST SUMMARY", details);
const messages = [newUserMessage("discarded"), kept, ...old, newUserMessage("latest reply"), fresh];
const original = JSON.stringify(messages);
const out = toLlmMessages(messages);
const flat = JSON.stringify(out);
assert.equal(out.length, 3, "latest summary + retained exchange + latest reply");
assert.ok(flat.includes("LATEST SUMMARY"));
assert.ok(!flat.includes("SUPERSEDED-"));
assert.equal(flat.split(COMPACTED_HISTORY_HEADER).length - 1, 1);
assert.equal(JSON.stringify(messages), original, "projection never rewrites the record");
});
it("a pending (dangling) question call in the KEPT tail still gets the interrupted synthesis", () => {
const kept = [assistantWithCall("a1", "call_1")];
const marker = newCompactionMarker("S", { firstKeptMessageId: "a1", tokensBefore: 1, at: "t" });
@@ -270,6 +286,22 @@ describe("toLlmMessages honors the cut", () => {
assert.equal(out.length, 3, "no head swap — all three render (marker as its user text)");
});
it("an unresolved newest cutoff preserves the full-history fallback, including summary text", () => {
const kept = newUserMessage("retained exchange");
const old = newCompactionMarker("OLD SUMMARY", { firstKeptMessageId: kept.id, tokensBefore: 1, at: "fixture" });
const fresh = newCompactionMarker("LATEST SUMMARY", { firstKeptMessageId: "missing", tokensBefore: 2, at: "fixture" });
const messages = [newUserMessage("earliest exchange"), kept, old, newUserMessage("latest reply"), fresh];
const original = JSON.stringify(messages);
const out = toLlmMessages(messages);
assert.equal(out.length, messages.length);
assert.equal(out[0]!.content, "earliest exchange", "do not silently apply the older cutoff");
const flat = JSON.stringify(out);
assert.ok(flat.includes("OLD SUMMARY"));
assert.ok(flat.includes("LATEST SUMMARY"));
assert.equal(flat.split(COMPACTED_HISTORY_HEADER).length - 1, 2);
assert.equal(JSON.stringify(messages), original);
});
it("chained compaction: the newest marker's cut wins; the older marker falls in the dropped span", () => {
const oldMarker = newCompactionMarker("OLD", { firstKeptMessageId: "m1", tokensBefore: 1, at: "t" });
const m1 = newUserMessage("x");
@@ -303,6 +335,11 @@ describe("serializeForSummary + buildCompactionPrompt", () => {
assert.ok(!text.toLowerCase().includes("plan-block"), "UI-only markers never serialize");
});
it("preserves legacy summary text when the caller has no separately supplied prior summary", () => {
const marker = newCompactionMarker("LEGACY SUMMARY", { firstKeptMessageId: "missing", tokensBefore: 1, at: "fixture" });
assert.ok(serializeForSummary([marker]).includes("LEGACY SUMMARY"));
});
it("the prompt demands the four sections in order and carries the plan tree verbatim", () => {
const prompt = buildCompactionPrompt({ spanText: "TRANSCRIPT", planTreeText: "- [done] A (a1)\n- A → B", kind: "teach" });
const order = ["The learner's understanding", "The teaching arc", "The plan dependency tree", "The current moment"].map(
@@ -388,6 +425,11 @@ describe("toLlmMessages carries the leading system message (request chrome)", ()
const record = (): LhMessage[] => [
{ id: "old", role: "user", content: "dropped history", timestamp: 1 },
{ id: "kept", role: "user", content: "kept tail", timestamp: 2 },
newCompactionMarker("SUPERSEDED SUMMARY", {
firstKeptMessageId: "kept",
tokensBefore: 98,
at: new Date(0).toISOString(),
}),
newCompactionMarker("SUMMARY", {
firstKeptMessageId: "kept",
tokensBefore: 99,
@@ -407,6 +449,8 @@ describe("toLlmMessages carries the leading system message (request chrome)", ()
: ((m.content as { text?: string }[])[0]?.text ?? "");
assert.ok(out.slice(2).some((m) => m.role === "user" && textOf(m) === "kept tail"));
assert.ok(!out.some((m) => m.role === "user" && textOf(m) === "dropped history"));
assert.ok(!JSON.stringify(out).includes("SUPERSEDED SUMMARY"));
assert.equal(JSON.stringify(out).split(COMPACTED_HISTORY_HEADER).length - 1, 1);
});
it("without a cut the chrome renders EXACTLY ONCE (no duplicate)", () => {