From 8f18178ef17cf183533d11868e3f3312c5714100 Mon Sep 17 00:00:00 2001 From: zhangchi47 Date: Fri, 14 Aug 2026 16:45:57 +0800 Subject: [PATCH] fix(coding-agents): keep knowledge pages current Knowledge pages can present historical facts as current guidance when later evidence replaces, removes, or supersedes those facts. Record full refresh mode explicitly for the five seeded knowledge pages and dynamic initiative pages so their behavior does not rely on the API default. Add a shared current-state policy to their source queries so synthesis prefers later evidence, labels historical or superseded facts, and reports uncertainty when the current state is unclear. Expose one helper for applying that policy so seeded and dynamic page queries use the same prompt construction. Reuse the shared page trigger for both creation paths to prevent refresh configuration drift. Extend page seeding tests to verify the full refresh mode and policy marker for both page creation paths. Tests: npm test Build: npm run build --- .../src/core/hindsight.pages.test.ts | 4 ++ .../coding-agents/src/core/hindsight.ts | 10 ++-- .../coding-agents/src/core/missions.ts | 53 +++++++++++++------ 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/hindsight-integrations/coding-agents/src/core/hindsight.pages.test.ts b/hindsight-integrations/coding-agents/src/core/hindsight.pages.test.ts index beab28f7e..1d07e6257 100644 --- a/hindsight-integrations/coding-agents/src/core/hindsight.pages.test.ts +++ b/hindsight-integrations/coding-agents/src/core/hindsight.pages.test.ts @@ -175,7 +175,9 @@ describe("HindsightClient.seedPages", () => { /^knowledge:(feature-work|decision|convention|component|concept)$/ ); expect(post.body.max_tokens).toBe(PAGE_MAX_TOKENS); + expect(post.body.trigger.mode).toBe("full"); expect(post.body.trigger.refresh_after_consolidation).toBe(true); + expect(post.body.source_query).toContain("Current-state policy:"); expect(post.body.parent_id).toBeUndefined(); // seeded at the tree root } // Nothing on the mental-models surface. @@ -346,6 +348,8 @@ describe("HindsightClient.captureInitiative", () => { expect(pagePost.body.name).toBe("Retry backoff for the uploader"); expect(pagePost.body.parent_id).toBe("folder-abc"); expect(pagePost.body.tags).toEqual(["knowledge:feature-work"]); + expect(pagePost.body.trigger.mode).toBe("full"); + expect(pagePost.body.source_query).toContain("Current-state policy:"); // Marker retain POST to /memories: the ONLY tag is relatedPageId, pointing at the REAL // server-assigned page id ("pg"). diff --git a/hindsight-integrations/coding-agents/src/core/hindsight.ts b/hindsight-integrations/coding-agents/src/core/hindsight.ts index 8eab0f1ec..dcc5da045 100644 --- a/hindsight-integrations/coding-agents/src/core/hindsight.ts +++ b/hindsight-integrations/coding-agents/src/core/hindsight.ts @@ -11,6 +11,7 @@ import { PAGE_MAX_TOKENS, PAGE_TRIGGER, PAGES, + withCurrentStatePolicy, } from "./missions"; import { pool, semverGte, sleep } from "./util"; import type { RetainStamp } from "./retain-stamp"; @@ -570,13 +571,12 @@ export class HindsightClient { const folderId = await this.ensureFolder("Initiatives"); const r = await this.req("POST", this.bankUrl("/knowledge-base/pages"), { name: args.title, - source_query: `Summarize the "${args.title}" initiative: what is being built or changed and why, and its current state — drawn from the project's memory.`, + source_query: withCurrentStatePolicy( + `Summarize the "${args.title}" initiative: what is being built or changed and why, and its current state — drawn from the project's memory.` + ), parent_id: folderId, tags: ["knowledge:feature-work"], - trigger: { - fact_types: ["world", "experience", "observation"], - refresh_after_consolidation: true, - }, + trigger: PAGE_TRIGGER, }); try { const j = (await r.json()) as { page_id?: string; id?: string }; diff --git a/hindsight-integrations/coding-agents/src/core/missions.ts b/hindsight-integrations/coding-agents/src/core/missions.ts index 10e329534..e887810d1 100644 --- a/hindsight-integrations/coding-agents/src/core/missions.ts +++ b/hindsight-integrations/coding-agents/src/core/missions.ts @@ -192,46 +192,65 @@ export interface KnowledgePage { tags: string[]; } +const CURRENT_STATE_POLICY = [ + "Current-state policy:", + "- Describe the repository's current state, not only its history.", + "- Prefer later relevant evidence over earlier evidence when they concern the same subject.", + "- When later evidence updates, removes, replaces, or supersedes an earlier fact, " + + "do not present the earlier fact as current.", + "- Mention superseded, removed, or historical facts only with an explicit status label.", + "- If the evidence does not establish the current state, say that it is uncertain.", +].join("\n"); + +export function withCurrentStatePolicy(query: string): string { + return `${query}\n\n${CURRENT_STATE_POLICY}`; +} + export const PAGES: KnowledgePage[] = [ { name: "Component map", - source_query: + source_query: withCurrentStatePolicy( "From this project's commit history and past discussions, what are the main " + - "components/modules/subsystems, what is each responsible for, and how do they relate to or " + - "depend on one another? Describe the structure and responsibilities.", + "components/modules/subsystems, what is each responsible for, and how do they relate to or " + + "depend on one another? Describe the structure and responsibilities." + ), tags: ["knowledge:component"], }, { name: "Core concepts", - source_query: + source_query: withCurrentStatePolicy( "What are the core concepts, domain abstractions, and key entities in this project — " + - "the vocabulary a developer must understand? For each, explain what it represents and its role, " + - "drawn from how they are introduced and discussed across the history and conversations.", + "the vocabulary a developer must understand? For each, explain what it represents and its role, " + + "drawn from how they are introduced and discussed across the history and conversations." + ), tags: ["knowledge:concept"], }, { name: "Conventions and patterns", - source_query: + source_query: withCurrentStatePolicy( "What conventions, idioms, and recurring patterns does this project follow — its " + - "approach to testing, error handling, naming, structure, and how changes are typically made? " + - "Describe how THIS project does things, as evidenced across its history and discussions.", + "approach to testing, error handling, naming, structure, and how changes are typically made? " + + "Describe how THIS project does things, as evidenced across its history and discussions." + ), tags: ["knowledge:convention"], }, { name: "Key decisions and rationale", - source_query: + source_query: withCurrentStatePolicy( "What are the significant technical decisions made in this project and the rationale " + - "behind them — the durable 'why we do it this way' a developer should know? Summarize the " + - "decisions and their reasoning from the commit rationales and past conversations.", + "behind them — the durable 'why we do it this way' a developer should know? Summarize the " + + "decisions and their reasoning from the commit rationales and past conversations." + ), tags: ["knowledge:decision"], }, { name: "Initiatives and enhancements", - source_query: + source_query: withCurrentStatePolicy( "Based on this repository's commit history, what are the major initiatives, features, and " + - "enhancements the project has worked on? Summarize the themes and notable changes over time. " + - "When a source memory carries a tag of the form `relatedPageId:`, include a Markdown link " + - "`[[page:]]` to that page in the summary, so each initiative links to its detailed page.", + "enhancements the project has worked on? Summarize the themes and notable changes over time. " + + "When a source memory carries a tag of the form `relatedPageId:`, include a Markdown link " + + "`[[page:]]` to that page in the summary, so each initiative links to its detailed page." + ), tags: ["knowledge:feature-work"], }, ]; @@ -239,7 +258,9 @@ export const PAGES: KnowledgePage[] = [ // Refresh policy shared by every seeded page: a living document, rebuilt from all three fact // tiers whenever consolidation produces new material. export const PAGE_MAX_TOKENS = 4096; +// Keep full mode explicit so page behavior does not depend on the API default. export const PAGE_TRIGGER = { + mode: "full", fact_types: ["world", "experience", "observation"], refresh_after_consolidation: true, } as const;