Skip to content

Commit 05af679

Browse files
committed
fix(memory-core): split idempotencyKey from sessionKey in dreaming narrative
sessionKey uses a day-bucket for session reuse within the same calendar day, while idempotencyKey includes the raw nowMs timestamp to stay unique per run. Previously both used the day-bucket, causing the gateway to dedupe fresh narrative sweeps as stale cached results. Also updates the test to assert the split key formats.
1 parent a55d21a commit 05af679

2 files changed

Lines changed: 23 additions & 3 deletions

File tree

extensions/memory-core/src/dreaming-narrative.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,8 @@ describe("generateAndAppendDreamNarrative", () => {
585585
const logger = createMockLogger();
586586
const nowMs = Date.parse("2026-04-05T03:00:00Z");
587587
const workspaceHash = createHash("sha1").update(workspaceDir).digest("hex").slice(0, 12);
588-
const expectedSessionKey = `dreaming-narrative-light-${workspaceHash}-${nowMs}`;
588+
const expectedSessionKey = `dreaming-narrative-light-${workspaceHash}-2026-04-05`;
589+
const expectedIdempotencyKey = `dreaming-narrative-light-${workspaceHash}-${nowMs}`;
589590

590591
await generateAndAppendDreamNarrative({
591592
subagent,
@@ -601,7 +602,7 @@ describe("generateAndAppendDreamNarrative", () => {
601602

602603
expect(subagent.run).toHaveBeenCalledOnce();
603604
expect(subagent.run.mock.calls[0][0]).toMatchObject({
604-
idempotencyKey: expectedSessionKey,
605+
idempotencyKey: expectedIdempotencyKey,
605606
sessionKey: expectedSessionKey,
606607
deliver: false,
607608
});

extensions/memory-core/src/dreaming-narrative.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ function buildRequestScopedFallbackNarrative(data: NarrativePhaseData): string {
138138
async function startNarrativeRunOrFallback(params: {
139139
subagent: SubagentSurface;
140140
sessionKey: string;
141+
idempotencyKey: string;
141142
message: string;
142143
data: NarrativePhaseData;
143144
workspaceDir: string;
@@ -147,7 +148,7 @@ async function startNarrativeRunOrFallback(params: {
147148
}): Promise<string | null> {
148149
try {
149150
const run = await params.subagent.run({
150-
idempotencyKey: params.sessionKey,
151+
idempotencyKey: params.idempotencyKey,
151152
sessionKey: params.sessionKey,
152153
message: params.message,
153154
extraSystemPrompt: NARRATIVE_SYSTEM_PROMPT,
@@ -191,6 +192,18 @@ function buildNarrativeSessionKey(params: {
191192
return `dreaming-narrative-${params.phase}-${workspaceHash}-${dayBucket}`;
192193
}
193194

195+
function buildNarrativeIdempotencyKey(params: {
196+
workspaceDir: string;
197+
phase: NarrativePhaseData["phase"];
198+
nowMs: number;
199+
}): string {
200+
const workspaceHash = createHash("sha1").update(params.workspaceDir).digest("hex").slice(0, 12);
201+
// Idempotency key must be unique per run so the gateway does not cache/dedupe
202+
// fresh narrative sweeps within the same day-bucket session (see P1 review on
203+
// PR #68437 — day-bucket idempotency caused stale cached narratives).
204+
return `dreaming-narrative-${params.phase}-${workspaceHash}-${params.nowMs}`;
205+
}
206+
194207
// ── Prompt building ────────────────────────────────────────────────────
195208

196209
export function buildNarrativePrompt(data: NarrativePhaseData): string {
@@ -855,6 +868,11 @@ export async function generateAndAppendDreamNarrative(params: {
855868
phase: params.data.phase,
856869
nowMs,
857870
});
871+
const idempotencyKey = buildNarrativeIdempotencyKey({
872+
workspaceDir: params.workspaceDir,
873+
phase: params.data.phase,
874+
nowMs,
875+
});
858876
const message = buildNarrativePrompt(params.data);
859877
let runId: string | null = null;
860878
let waitStatus: string | null = null;
@@ -863,6 +881,7 @@ export async function generateAndAppendDreamNarrative(params: {
863881
runId = await startNarrativeRunOrFallback({
864882
subagent: params.subagent,
865883
sessionKey,
884+
idempotencyKey,
866885
message,
867886
data: params.data,
868887
workspaceDir: params.workspaceDir,

0 commit comments

Comments
 (0)