From 4cde9e8eaed420784aa8b4deaf779e3b6fbb213e Mon Sep 17 00:00:00 2001 From: mastertyko <11311479+mastertyko@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:19:53 +0200 Subject: [PATCH] fix(gsd): stop stale forensics context hijacks --- .../gsd/bootstrap/system-context.ts | 10 +++++-- .../tests/forensics-context-persist.test.ts | 30 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/resources/extensions/gsd/bootstrap/system-context.ts b/src/resources/extensions/gsd/bootstrap/system-context.ts index 744e57606..8fe3890df 100644 --- a/src/resources/extensions/gsd/bootstrap/system-context.ts +++ b/src/resources/extensions/gsd/bootstrap/system-context.ts @@ -168,7 +168,7 @@ export async function buildBeforeAgentStartResult( const injection = await buildGuidedExecuteContextInjection(event.prompt, process.cwd()); // Re-inject forensics context on follow-up turns (#2941) - const forensicsInjection = !injection ? buildForensicsContextInjection(process.cwd()) : null; + const forensicsInjection = !injection ? buildForensicsContextInjection(process.cwd(), event.prompt) : null; const worktreeBlock = buildWorktreeContextBlock(); const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${knowledgeBlock}${codebaseBlock}${memoryBlock}${newSkillsBlock}${worktreeBlock}`; @@ -481,7 +481,7 @@ function oneLine(text: string): string { * Check for an active forensics session and return the prompt content * so it can be re-injected on follow-up turns. */ -function buildForensicsContextInjection(basePath: string): string | null { +export function buildForensicsContextInjection(basePath: string, prompt: string): string | null { const marker = readForensicsMarker(basePath); if (!marker) return null; @@ -492,6 +492,12 @@ function buildForensicsContextInjection(basePath: string): string | null { return null; } + const trimmed = prompt.trim().toLowerCase().replace(/[.!?,]+$/g, ""); + if (trimmed && !RESUME_INTENT_PATTERNS.test(trimmed)) { + clearForensicsMarker(basePath); + return null; + } + return marker.promptContent; } diff --git a/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts b/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts index ab6cf91e8..519bc985d 100644 --- a/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +++ b/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts @@ -126,4 +126,34 @@ describe("forensics context persistence (#2941)", () => { // Should not throw clearForensicsMarker(join(tmpBase, "nonexistent")); }); + + it("buildForensicsContextInjection keeps marker for low-entropy resume prompts", async () => { + const { buildForensicsContextInjection } = await import("../bootstrap/system-context.ts"); + + const markerPath = join(tmpBase, ".gsd", "runtime", "active-forensics.json"); + writeFileSync(markerPath, JSON.stringify({ + reportPath: "/some/report.md", + promptContent: "forensics prompt", + createdAt: new Date().toISOString(), + }), "utf-8"); + + const result = buildForensicsContextInjection(tmpBase, "continue"); + assert.equal(result, "forensics prompt"); + assert.ok(existsSync(markerPath), "resume-like follow-up should keep marker intact"); + }); + + it("buildForensicsContextInjection clears marker on unrelated user prompts", async () => { + const { buildForensicsContextInjection } = await import("../bootstrap/system-context.ts"); + + const markerPath = join(tmpBase, ".gsd", "runtime", "active-forensics.json"); + writeFileSync(markerPath, JSON.stringify({ + reportPath: "/some/report.md", + promptContent: "forensics prompt", + createdAt: new Date().toISOString(), + }), "utf-8"); + + const result = buildForensicsContextInjection(tmpBase, "please summarize the README"); + assert.equal(result, null); + assert.ok(!existsSync(markerPath), "unrelated follow-up should clear the stale marker"); + }); });