diff --git a/packages/junior/src/chat/runtime/reply-executor.ts b/packages/junior/src/chat/runtime/reply-executor.ts
index aaa30563e..b1d6156f0 100644
--- a/packages/junior/src/chat/runtime/reply-executor.ts
+++ b/packages/junior/src/chat/runtime/reply-executor.ts
@@ -214,7 +214,7 @@ async function loadPiMessagesForTurn(args: {
if (projection.length > 0) {
return {
canCompact: true,
- piMessages: projection,
+ piMessages: stripRuntimeTurnContext(projection),
};
}
diff --git a/packages/junior/tests/unit/misc/respond-helpers-runtime-context.test.ts b/packages/junior/tests/unit/misc/respond-helpers-runtime-context.test.ts
index b4b568eb7..68ce4a076 100644
--- a/packages/junior/tests/unit/misc/respond-helpers-runtime-context.test.ts
+++ b/packages/junior/tests/unit/misc/respond-helpers-runtime-context.test.ts
@@ -1,6 +1,10 @@
import { describe, expect, it } from "vitest";
import type { PiMessage } from "@/chat/pi/messages";
-import { prependMissingRuntimeTurnContext } from "@/chat/respond-helpers";
+import {
+ hasRuntimeTurnContext,
+ prependMissingRuntimeTurnContext,
+ stripRuntimeTurnContext,
+} from "@/chat/respond-helpers";
describe("prependMissingRuntimeTurnContext", () => {
it("leaves recorded bootstrap prompts unchanged", () => {
@@ -47,6 +51,62 @@ describe("prependMissingRuntimeTurnContext", () => {
expect(JSON.stringify(updated[0])).not.toContain("slack:C123:updated");
});
+ it("injects new requester context after stripping stale projected context", () => {
+ // Simulates a thread started by user A, where the runtime turn context
+ // from the first turn was persisted into the session log projection.
+ // When user B sends a follow-up message, `loadPiMessagesForTurn` must
+ // strip the stale context before returning, so `hasRuntimeTurnContext`
+ // returns false and a fresh context block carrying user B's identity
+ // is injected instead.
+ const projectionWithStaleContext: PiMessage[] = [
+ {
+ role: "user",
+ content: [
+ {
+ type: "text",
+ text: [
+ "",
+ "",
+ "- full_name: User Alpha",
+ "- user_name: user.alpha",
+ "- user_id: U_ALPHA",
+ "",
+ "",
+ ].join("\n"),
+ },
+ { type: "text", text: "original question" },
+ ],
+ timestamp: 1,
+ },
+ {
+ role: "assistant",
+ content: [{ type: "text", text: "original answer" }],
+ timestamp: 2,
+ },
+ ] as PiMessage[];
+
+ // Before stripping: stale context is present, so no new context would be injected
+ expect(hasRuntimeTurnContext(projectionWithStaleContext)).toBe(true);
+
+ // After stripping (what loadPiMessagesForTurn now does): fresh injection is possible
+ const stripped = stripRuntimeTurnContext(projectionWithStaleContext);
+ expect(hasRuntimeTurnContext(stripped)).toBe(false);
+
+ const userBContext = [
+ "",
+ "",
+ "- user_name: user.beta",
+ "- user_id: U_BETA",
+ "",
+ "",
+ ].join("\n");
+ const updated = prependMissingRuntimeTurnContext(stripped, userBContext);
+
+ expect(JSON.stringify(updated)).toContain("user.beta");
+ expect(JSON.stringify(updated)).not.toContain("user.alpha");
+ expect(JSON.stringify(updated)).not.toContain("User Alpha");
+ });
+
it("adds bootstrap context to a pre-prompt user boundary", () => {
const messages: PiMessage[] = [
{