From ce8ef670f875aad1c0239b9ab9874335ab0204b9 Mon Sep 17 00:00:00 2001 From: zouyicheng Date: Wed, 20 May 2026 16:44:31 +0800 Subject: [PATCH] prompt(memory): tighten memoryExtractor against transient snapshots + role-name filenames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 2026-05-20 dogfood (Bug 3) found memoryExtractor over-producing: one night's memory tree held 113 files, including ~24 timestamped status / progress snapshots saved as permanent type:project entries, and files whose names leaked the producing role (webSearcher/generalist-*.md, generalist/generalist-*.md). Root cause was the prompt itself. It explicitly said "DO save ... ongoing-work status", directly inducing the snapshots, while the adjacent "temporary task context" exclusion contradicted it. Filename guidance was just "concise kebab/snake-case" with nothing about provenance. Three prompt changes: - New "What counts as durable" section with a concrete test — "would this entry be wrong or useless an hour from now?" — and an explicit Skip list for point-in-time status / progress snapshots. Replaces the contradictory "ongoing-work status" / "temporary task context" bullets. - filename field: name the topic, never prefix with a role / agent / task name; reuse an existing entry's filename verbatim when updating. - Workflow step 2: when refining an existing entry, write to its exact filename — never create a -update / -v2 / -final sibling. The cross-layer duplication (main re-extracting background-task-result blocks into L1) is not a prompt issue and is left to a separate code change. memoryExtractor role-prompt snapshot hash updated in both snapshot tests (15ad654 -> 296e169); no other role prompt changed. typecheck clean, 1462/1462 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/agents/bundled/memoryExtractor.ts | 13 +++++++++---- src/agents/role-prompt.snapshot.test.ts | 2 +- src/prompt/prompt-for-role.test.ts | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/agents/bundled/memoryExtractor.ts b/src/agents/bundled/memoryExtractor.ts index 5414f679..436c3365 100644 --- a/src/agents/bundled/memoryExtractor.ts +++ b/src/agents/bundled/memoryExtractor.ts @@ -5,14 +5,21 @@ The framework decides where each MemoryWrite lands; you only supply the entry co ## Workflow 1. The request contains: (a) a list of existing memories already on disk, (b) the conversation segment to analyze. Read both. -2. Decide what durable signal is worth saving without duplicating the existing list. Update or skip overlapping content rather than recreating it. When unsure if a similar entry exists, use MemoryRead / Grep to check before writing. +2. Decide what is worth saving by the durability test below, without duplicating the existing list. When the conversation only refines something already in that list, write to that entry's exact filename to replace it — never create a \`-update\` / \`-v2\` / \`-final\` sibling. When unsure whether a similar entry exists, use MemoryRead / Grep to check before writing. 3. Call MemoryWrite 0 to 3 times. Each save: supply filename, type (one of: user, feedback, project, reference), description, content. 4. If nothing is worth saving, reply exactly "no new memories" and stop. +## What counts as durable + +A memory must still be true and useful weeks from now. Before each save, ask: would this entry be wrong or useless an hour from now? If so, skip it. + +- **Save**: user preferences, project conventions, technical decisions and their rationale, feedback / corrections, stable reference pointers, and project direction or constraints not recorded elsewhere. +- **Skip**: point-in-time status or progress snapshots ("task 3 of 5 done", "current price is …", "latest run returned …") and anything else valid only for a short window — these go stale. Also skip code snippets, file paths, file structure, and git history — those are recoverable from the codebase. A clock time or a "latest" / "progress" / "status" marker in a filename or body is a strong sign the entry is transient. + ## Memory format reference Fields each MemoryWrite call must populate: -- \`filename\` — concise kebab/snake-case identifier; \`.md\` is optional. +- \`filename\` — concise kebab/snake-case identifier that names the memory's topic. Do not prefix it with a role, agent, or task name — name what the memory is about, not who produced it or which task surfaced it. When updating an entry from the existing list, reuse its filename verbatim. \`.md\` is optional. - \`description\` — one-line hook used for recall ranking later; be specific. - \`type\` — one of: \`user\`, \`feedback\`, \`project\`, \`reference\`. - \`content\` — markdown body. @@ -20,8 +27,6 @@ Fields each MemoryWrite call must populate: Body conventions: - For \`feedback\` or \`project\` entries: include a **Why:** line (the reason this matters) and a **How to apply:** line (when this guidance kicks in). - Convert relative dates ("yesterday", "last week", "今天") to absolute YYYY-MM-DD. -- Do NOT save code snippets, file paths, file structure details, git history, or temporary task context — those are derivable from the codebase or git, not memory-worthy. -- DO save user preferences, project conventions, technical decisions, feedback / corrections, and ongoing-work status that is not otherwise documented. ## Output discipline diff --git a/src/agents/role-prompt.snapshot.test.ts b/src/agents/role-prompt.snapshot.test.ts index 738b9aeb..d62a2cb6 100644 --- a/src/agents/role-prompt.snapshot.test.ts +++ b/src/agents/role-prompt.snapshot.test.ts @@ -26,7 +26,7 @@ const SNAPSHOT_HASHES: Record = { coder: 'cfffff34b48c034574d961511176664db6c26436d802dfb0522b4ee9236803d6', archivist: 'f9225f6479d11d4c2a9dacb7af7976ed5d1001138b55a750481cfd439afc0c7a', reviewer: '5e5e59d0aa6dfa86e33179fe96eb785c5187abcad09bb4708c431fee5a764ba7', - memoryExtractor: '15ad65436a62ca507821dac8e4e94df8b8ad1aa49306d2615caabfa2ff8f618a', + memoryExtractor: '296e1693f3ed433a9a3f1526bc24bc6291809ebbe8c7a5dbb78c7b2100e2fccf', memoryCurator: '22c073df9e2148ade8570e758402a5066ff2e785a866d182a46f21ac1350f4e1', } diff --git a/src/prompt/prompt-for-role.test.ts b/src/prompt/prompt-for-role.test.ts index cba9572d..35934963 100644 --- a/src/prompt/prompt-for-role.test.ts +++ b/src/prompt/prompt-for-role.test.ts @@ -27,7 +27,7 @@ const SNAPSHOT_HASHES: Record = { coder: 'cfffff34b48c034574d961511176664db6c26436d802dfb0522b4ee9236803d6', archivist: 'f9225f6479d11d4c2a9dacb7af7976ed5d1001138b55a750481cfd439afc0c7a', reviewer: '5e5e59d0aa6dfa86e33179fe96eb785c5187abcad09bb4708c431fee5a764ba7', - memoryExtractor: '15ad65436a62ca507821dac8e4e94df8b8ad1aa49306d2615caabfa2ff8f618a', + memoryExtractor: '296e1693f3ed433a9a3f1526bc24bc6291809ebbe8c7a5dbb78c7b2100e2fccf', memoryCurator: '22c073df9e2148ade8570e758402a5066ff2e785a866d182a46f21ac1350f4e1', }