diff --git a/src/plugin.ts b/src/plugin.ts index ebe86e9..594935d 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -108,45 +108,37 @@ function buildCompactionPrompt(privateContext: string): string { "", "## Relevant Files", "", - "At the end of the summary, extract durable memory entries for future sessions.", + "At the end of the summary, include a Memory candidates section only if there are durable facts that will change future behavior.", "", - "Memory quality bar:", - "Extract only durable facts that will change future behavior: user preferences, decisions with rationale, stable constraints, or hard-to-rediscover references.", - "", - "Do not extract trivia: transient IDs/revisions, task progress, test/file counts, bare status updates, local UI details, or facts easily rediscovered from the repo.", - "", - "When unsure, skip it. Fewer high-signal memories are better than many low-value ones.", + "CRITICAL MEMORY RULES:", + "- Most compactions should produce ZERO memories. Empty is correct when nothing durable changed.", + "- NO completion or progress statements: do not extract completed work, passing tests, commits, PR status, wave/task/phase completion, or current state.", + "- NO session-internal implementation notes: do not extract what files were edited, what bug was just fixed, what command just ran, or what the assistant reviewed.", + "- feedback ONLY means stable user preferences or user instructions, written in imperative/future-facing form.", + "- decision ONLY means rules that apply to FUTURE work, not decisions already implemented in this session.", + "- project/reference ONLY when the fact is stable across sessions and hard to rediscover from the repository.", + "- If unsure, skip it.", "", "Good memory examples:", "- [feedback] User prefers architecture reviews in Traditional Chinese.", - "- [decision] Use frozen workspace memory snapshots plus ephemeral hot state for cache stability.", - "- [project] The plugin should piggyback memory extraction on OpenCode compaction and avoid extra LLM calls.", - "- [reference] Workspace memory appears in frozen system[1]; pending memories appear in hot session state until compaction.", + "- [decision] Do not add semantic merge to memory dedupe.", + "- [project] This repository is an OpenCode plugin using local JSON stores.", + "- [reference] Workspace memory is rendered as frozen system[1]; pending memories remain in hot state until compaction.", "", "Bad memory examples to skip:", - "- 42 tests passed.", "- Wave 2 completed successfully.", - "- Modified 5 files.", - "- commit 4309cb8 contains the latest fix.", - "- TypeError: Cannot read properties of undefined.", - "- Currently running npm test.", - "", - "A memory should still be useful if a new agent opens this workspace next week.", - "", - "Only extract facts that are likely to stay true across sessions.", - "Do not extract session-specific progress like exact test counts, file counts, or phase numbers.", - "For progress, extract the stable goal or durable milestone, not the current number.", - "For references, extract configuration values that do not usually change between sessions.", - "For feedback, extract unresolved issues or user preferences that future sessions need to know.", - "Use exactly this candidate format, including square brackets around the type:", + "- 180 tests passed and CI is green.", + "- Implemented owner-aware cleanup in plugin.ts.", + "- The assistant reviewed code reviewer feedback and updated the plan.", + "- Commit a762e86 contains the owner scope fix.", "", + "Format when there ARE durable memories:", "Memory candidates:", - "- [feedback] content", - "- [project] content", - "- [decision] content", - "- [reference] content", + "- [feedback|decision|project|reference] future-facing durable fact", "", - "Do not write '- project content'; write '- [project] content'.", + "Format when there are NO durable memories:", + "Memory candidates:", + "(none)", "", "Background context, use this to inform the summary above.", "Do not output this context verbatim:", diff --git a/tests/plugin.test.ts b/tests/plugin.test.ts index 7611aae..daa7401 100644 --- a/tests/plugin.test.ts +++ b/tests/plugin.test.ts @@ -297,9 +297,9 @@ test("compaction hook sets output.prompt with ---free template", async () => { "Prompt should include concrete positive memory examples"); assert.equal(prompt!.includes("Bad memory examples to skip:"), true, "Prompt should include concrete negative memory examples"); - assert.equal(prompt!.includes("42 tests passed"), true, + assert.equal(prompt!.includes("180 tests passed"), true, "Prompt should explicitly reject test-count snapshots"); - assert.equal(prompt!.includes("commit 4309cb8"), true, + assert.equal(prompt!.includes("Commit a762e86"), true, "Prompt should explicitly reject commit-hash snapshots"); // Should contain our context data (hot session state) @@ -317,6 +317,27 @@ test("compaction hook sets output.prompt with ---free template", async () => { } }); +test("compaction prompt forbids progress and session-internal memory candidates", async () => { + const tmpDir = await mkdtemp(join(tmpdir(), "memory-plugin-prompt-")); + try { + const plugin = await MemoryV2Plugin({ directory: tmpDir, client: mockRootClient() }); + const output = { prompt: "", context: [] as string[] }; + + await (plugin as Record)["experimental.session.compacting"]( + { sessionID: "prompt-session", model: {} }, + output, + ); + + assert.match(output.prompt, /CRITICAL MEMORY RULES/); + assert.match(output.prompt, /NO completion or progress statements/i); + assert.match(output.prompt, /NO session-internal implementation notes/i); + assert.match(output.prompt, /feedback ONLY/i); + assert.match(output.prompt, /Most compactions should produce ZERO memories/i); + } finally { + await rm(tmpDir, { recursive: true, force: true }); + } +}); + test("compaction hook merges existing output.context from other plugins", async () => { const tmpDir = await mkdtemp(join(tmpdir(), "memory-plugin-test-"));