fix(gsd): stop stale forensics context hijacks

This commit is contained in:
mastertyko 2026-04-09 20:19:53 +02:00
parent 0be7c44605
commit 4cde9e8eae
2 changed files with 38 additions and 2 deletions

View file

@ -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;
}

View file

@ -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");
});
});