mirror of
https://github.com/wassname/pi-dynamic-context-pruning.git
synced 2026-06-27 16:46:12 +08:00
fix: Infinity anchorTimestamp ghost block, prompt tag name, test cleanup
- resolveAnchorTimestamp returns endTimestamp + 1 instead of Infinity - Validate timestamps are finite before creating blocks - Skip corrupted blocks in overlap checks and compression application - Include block timestamp range in overlap error messages - Filter out corrupted blocks on session restore - Fix prompt tag name: dcp-message-id → dcp-id to match injected tags - Remove duplicate test, keep corrupted-block resilience test
This commit is contained in:
+3
-3
@@ -13,7 +13,7 @@ You operate in a context-constrained environment. Manage context continuously to
|
|||||||
|
|
||||||
The ONLY tool you have for context management is \`compress\`. It replaces older conversation content with technical summaries you produce.
|
The ONLY tool you have for context management is \`compress\`. It replaces older conversation content with technical summaries you produce.
|
||||||
|
|
||||||
\`<dcp-message-id>\` and \`<dcp-system-reminder>\` tags are environment-injected metadata. Do not output them.
|
\`<dcp-id>\` and \`<dcp-system-reminder>\` tags are environment-injected metadata. Do not output them.
|
||||||
|
|
||||||
THE PHILOSOPHY OF COMPRESS
|
THE PHILOSOPHY OF COMPRESS
|
||||||
\`compress\` transforms conversation content into dense, high-fidelity summaries. This is not cleanup — it is crystallization. Your summary becomes the authoritative record of what transpired.
|
\`compress\` transforms conversation content into dense, high-fidelity summaries. This is not cleanup — it is crystallization. Your summary becomes the authoritative record of what transpired.
|
||||||
@@ -114,7 +114,7 @@ You specify boundaries by ID using the injected IDs visible in the conversation:
|
|||||||
- \`mNNN\` IDs identify raw messages (3 digits, zero-padded, e.g. \`m001\`, \`m042\`)
|
- \`mNNN\` IDs identify raw messages (3 digits, zero-padded, e.g. \`m001\`, \`m042\`)
|
||||||
- \`bN\` IDs identify previously compressed blocks
|
- \`bN\` IDs identify previously compressed blocks
|
||||||
|
|
||||||
Each message has an ID inside XML metadata tags like \`<dcp-message-id>...</dcp-message-id>\`.
|
Each message has an ID inside XML metadata tags like \`<dcp-id>...</dcp-id>\`.
|
||||||
The ID tag appears at the end of the message it belongs to — it identifies the message above it, not the one below it.
|
The ID tag appears at the end of the message it belongs to — it identifies the message above it, not the one below it.
|
||||||
Treat these tags as boundary metadata only, not as tool result content.
|
Treat these tags as boundary metadata only, not as tool result content.
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ Prefer multiple short, closed ranges over one large range when several independe
|
|||||||
export const MANUAL_MODE_SYSTEM_PROMPT = `
|
export const MANUAL_MODE_SYSTEM_PROMPT = `
|
||||||
You are operating in DCP manual mode for context management.
|
You are operating in DCP manual mode for context management.
|
||||||
|
|
||||||
\`<dcp-message-id>\` and \`<dcp-system-reminder>\` tags are environment-injected metadata. Do not output them.
|
\`<dcp-id>\` and \`<dcp-system-reminder>\` tags are environment-injected metadata. Do not output them.
|
||||||
|
|
||||||
In manual mode you do NOT proactively compress conversation content. Compression is a deliberate, user-directed action.
|
In manual mode you do NOT proactively compress conversation content. Compression is a deliberate, user-directed action.
|
||||||
|
|
||||||
|
|||||||
+18
-37
@@ -709,38 +709,30 @@ function findOrphanedToolUse(result: any[]): string | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Test 10 — INFINITY ANCHOR BUG (regression test)
|
// Test 10 — CORRUPTED BLOCK WITH NULL/INFINITY TIMESTAMPS (resilience)
|
||||||
//
|
//
|
||||||
// Previously, when a compression block's range extended to the end of the
|
// Blocks from older sessions may have null/Infinity timestamps due to JSON
|
||||||
// conversation, resolveAnchorTimestamp returned Infinity. This caused:
|
// round-trip corruption. These blocks should be skipped during compression
|
||||||
// 1. JSON serialization turned Infinity into null, corrupting saved state
|
// application and should not block new compress operations.
|
||||||
// 2. Null timestamps in overlap checks caused false positives (every range
|
|
||||||
// appeared to overlap the ghost block)
|
|
||||||
// 3. The model entered a compression spiral, unable to consolidate blocks
|
|
||||||
//
|
|
||||||
// Fix: resolveAnchorTimestamp returns endTimestamp + 1 instead of Infinity.
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
{
|
{
|
||||||
console.log("TEST 10: Infinity anchor timestamp regression");
|
console.log("TEST 10: corrupted block with null/Infinity timestamps is skipped");
|
||||||
|
|
||||||
// Conversation where the last message is at timestamp 4000.
|
|
||||||
// Compression block covers up to the end, so anchor should be 4001, not Infinity.
|
|
||||||
const messages: any[] = [
|
const messages: any[] = [
|
||||||
{ role: "user", content: [{ type: "text", text: "read file" }], timestamp: 1000 },
|
{ role: "user", content: [{ type: "text", text: "hello" }], timestamp: 1000 },
|
||||||
{ role: "assistant", content: [{ type: "toolCall", id: "toolu_1", name: "read", arguments: {} }], timestamp: 2000 },
|
{ role: "assistant", content: [{ type: "text", text: "hi" }], timestamp: 2000 },
|
||||||
{ role: "toolResult", toolCallId: "toolu_1", toolName: "read", isError: false, content: [{ type: "text", text: "file data" }], timestamp: 3000 },
|
{ role: "user", content: [{ type: "text", text: "bye" }], timestamp: 3000 },
|
||||||
{ role: "user", content: [{ type: "text", text: "thanks" }], timestamp: 4000 },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Block that extends to the end of conversation
|
// Block with corrupted timestamps (null from JSON round-trip)
|
||||||
const state = makeState([
|
const state = makeState([
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
topic: "file read",
|
topic: "ghost block",
|
||||||
summary: "File was read.",
|
summary: "This block has corrupted timestamps.",
|
||||||
startTimestamp: 1000,
|
startTimestamp: null as any, // null from JSON deserialization of Infinity
|
||||||
endTimestamp: 4000,
|
endTimestamp: null as any,
|
||||||
anchorTimestamp: 4001, // Fixed: was Infinity before the bugfix
|
anchorTimestamp: null as any,
|
||||||
active: true,
|
active: true,
|
||||||
summaryTokenEstimate: 5,
|
summaryTokenEstimate: 5,
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
@@ -757,23 +749,12 @@ function findOrphanedToolUse(result: any[]): string | null {
|
|||||||
console.log(` role="${m.role}" ts=${m.timestamp} content="${preview}"`);
|
console.log(` role="${m.role}" ts=${m.timestamp} content="${preview}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The synthetic message timestamp must be finite (not Infinity)
|
// All 3 original messages should survive (ghost block was skipped)
|
||||||
const synthetic = result.find(
|
assert.strictEqual(result.length, 3, `FAIL — expected 3 messages, got ${result.length}`);
|
||||||
(m: any) => m.role === "user" && typeof m.content?.[0]?.text === "string" && m.content[0].text.includes("Compressed section")
|
console.log(" PASS: corrupted block skipped, all original messages preserved");
|
||||||
);
|
|
||||||
assert.ok(synthetic, "FAIL — no synthetic compressed message found");
|
|
||||||
assert.ok(
|
|
||||||
Number.isFinite(synthetic.timestamp),
|
|
||||||
`FAIL — synthetic message has non-finite timestamp: ${synthetic.timestamp}`
|
|
||||||
);
|
|
||||||
console.log(` PASS: synthetic message has finite timestamp (${synthetic.timestamp})`);
|
|
||||||
|
|
||||||
console.log("TEST 10 PASSED\n");
|
console.log("TEST 10 PASSED\n");
|
||||||
}
|
}//
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Test 11 — CORRUPTED BLOCK WITH NULL/INFINITY TIMESTAMPS (resilience)
|
|
||||||
//
|
|
||||||
// Blocks from older sessions may have null/Infinity timestamps due to JSON
|
// Blocks from older sessions may have null/Infinity timestamps due to JSON
|
||||||
// round-trip corruption. These blocks should be skipped during compression
|
// round-trip corruption. These blocks should be skipped during compression
|
||||||
// application and should not block new compress operations.
|
// application and should not block new compress operations.
|
||||||
|
|||||||
Reference in New Issue
Block a user