Răsfoiți Sursa

fix: tighten memory retrieval release blockers

Ma 7 luni în urmă
părinte
comite
29109f35b2

+ 74 - 0
packages/core/src/__tests__/memory-retrieval.test.ts

@@ -895,4 +895,78 @@ describe("retrieveMemorySelection", () => {
     ]);
     expect(result.hooks.map((hook) => hook.hookId)).not.toContain("stale-resolved");
   });
+
+  it("does not resurface a resolved hook just because mustKeep shares an artifact term", async () => {
+    root = await mkdtemp(join(tmpdir(), "inkos-memory-retrieval-resolved-artifact-test-"));
+    const bookDir = join(root, "book");
+    const storyDir = join(bookDir, "story");
+    const stateDir = join(storyDir, "state");
+    await mkdir(stateDir, { recursive: true });
+
+    await Promise.all([
+      writeFile(
+        join(stateDir, "manifest.json"),
+        JSON.stringify({
+          schemaVersion: 2,
+          language: "en",
+          lastAppliedChapter: 10,
+          projectionVersion: 1,
+          migrationWarnings: [],
+        }, null, 2),
+        "utf-8",
+      ),
+      writeFile(
+        join(stateDir, "current_state.json"),
+        JSON.stringify({
+          chapter: 10,
+          facts: [],
+        }, null, 2),
+        "utf-8",
+      ),
+      writeFile(
+        join(stateDir, "chapter_summaries.json"),
+        JSON.stringify({
+          rows: [],
+        }, null, 2),
+        "utf-8",
+      ),
+      writeFile(
+        join(stateDir, "hooks.json"),
+        JSON.stringify({
+          hooks: [
+            {
+              hookId: "mentor-oath",
+              startChapter: 8,
+              type: "relationship",
+              status: "open",
+              lastAdvancedChapter: 9,
+              expectedPayoff: "Mentor oath payoff",
+              notes: "Mentor oath debt with Lin Yue",
+            },
+            {
+              hookId: "old-seal",
+              startChapter: 3,
+              type: "artifact",
+              status: "resolved",
+              lastAdvancedChapter: 3,
+              expectedPayoff: "Seal already recovered",
+              notes: "Jade seal already recovered.",
+            },
+          ],
+        }, null, 2),
+        "utf-8",
+      ),
+    ]);
+
+    const result = await retrieveMemorySelection({
+      bookDir,
+      chapterNumber: 11,
+      goal: "Bring the focus back to the mentor oath conflict with Lin Yue.",
+      outlineNode: "Track the merchant guild's escape route.",
+      mustKeep: ["The jade seal cannot be destroyed."],
+    });
+
+    expect(result.hooks.map((hook) => hook.hookId)).toContain("mentor-oath");
+    expect(result.hooks.map((hook) => hook.hookId)).not.toContain("old-seal");
+  });
 });

+ 3 - 0
packages/core/src/__tests__/pipeline-runner.test.ts

@@ -1729,6 +1729,9 @@ describe("PipelineRunner", () => {
             mood: "tense",
             chapterType: "investigation",
           },
+          subplotOps: [],
+          emotionalArcOps: [],
+          characterMatrixOps: [],
           notes: [],
         },
       }),

+ 13 - 8
packages/core/src/utils/memory-retrieval.ts

@@ -55,14 +55,19 @@ export async function retrieveMemorySelection(params: {
     currentStateMarkdown,
     fallbackChapter,
   );
-  const queryTerms = extractQueryTerms(
+  const narrativeQueryTerms = extractQueryTerms(
+    params.goal,
+    params.outlineNode,
+    [],
+  );
+  const factQueryTerms = extractQueryTerms(
     params.goal,
     params.outlineNode,
     params.mustKeep ?? [],
   );
   const volumeSummaries = selectRelevantVolumeSummaries(
     parseVolumeSummariesMarkdown(volumeSummariesMarkdown),
-    queryTerms,
+    narrativeQueryTerms,
   );
 
   const memoryDb = openMemoryDB(params.bookDir);
@@ -92,10 +97,10 @@ export async function retrieveMemorySelection(params: {
         summaries: selectRelevantSummaries(
           memoryDb.getSummaries(1, Math.max(1, params.chapterNumber - 1)),
           params.chapterNumber,
-          queryTerms,
+          narrativeQueryTerms,
         ),
-        hooks: selectRelevantHooks(memoryDb.getActiveHooks(), queryTerms, params.chapterNumber),
-        facts: selectRelevantFacts(memoryDb.getCurrentFacts(), queryTerms),
+        hooks: selectRelevantHooks(memoryDb.getActiveHooks(), narrativeQueryTerms, params.chapterNumber),
+        facts: selectRelevantFacts(memoryDb.getCurrentFacts(), factQueryTerms),
         volumeSummaries,
         dbPath: join(storyDir, "memory.db"),
       };
@@ -112,9 +117,9 @@ export async function retrieveMemorySelection(params: {
   const hooks = structuredHooks?.hooks ?? parsePendingHooksMarkdown(hooksMarkdown);
 
   return {
-    summaries: selectRelevantSummaries(summaries, params.chapterNumber, queryTerms),
-    hooks: selectRelevantHooks(hooks, queryTerms, params.chapterNumber),
-    facts: selectRelevantFacts(facts, queryTerms),
+    summaries: selectRelevantSummaries(summaries, params.chapterNumber, narrativeQueryTerms),
+    hooks: selectRelevantHooks(hooks, narrativeQueryTerms, params.chapterNumber),
+    facts: selectRelevantFacts(facts, factQueryTerms),
     volumeSummaries,
   };
 }