diff --git a/server/src/__tests__/heartbeat-workspace-session.test.ts b/server/src/__tests__/heartbeat-workspace-session.test.ts index a969382862..0495f3bd93 100644 --- a/server/src/__tests__/heartbeat-workspace-session.test.ts +++ b/server/src/__tests__/heartbeat-workspace-session.test.ts @@ -2092,6 +2092,57 @@ describe("effective run session config freshness", () => { expect(decision.reasons.join("\n")).toContain("adapter config"); }); + it("does not reset for issue comment timestamps but still resets for workspace settings", async () => { + const base = await buildSessionConfigMetadata({ + workspaceConfig: { + requestedMode: "agent_default", + effectiveMode: "agent_default", + issueConfigRevisionAt: "2026-06-01T00:00:00.000Z", + issueSettings: null, + }, + }); + const commentOnly = await buildSessionConfigMetadata({ + workspaceConfig: { + requestedMode: "agent_default", + effectiveMode: "agent_default", + issueConfigRevisionAt: "2026-06-01T00:05:00.000Z", + issueSettings: null, + }, + }); + const workspaceChanged = await buildSessionConfigMetadata({ + workspaceConfig: { + requestedMode: "isolated_workspace", + effectiveMode: "isolated_workspace", + issueConfigRevisionAt: "2026-06-01T00:05:00.000Z", + issueSettings: { mode: "isolated_workspace" }, + }, + }); + + expect( + resolveTaskSessionConfigFreshness({ + hasTaskSession: true, + configuredModel: "gpt-5.4-mini", + taskSessionParams: sessionParamsWithConfigMetadata(base), + configMetadata: commentOnly, + }), + ).toMatchObject({ + reset: false, + changedCategories: [], + reasons: [], + }); + expect( + resolveTaskSessionConfigFreshness({ + hasTaskSession: true, + configuredModel: "gpt-5.4-mini", + taskSessionParams: sessionParamsWithConfigMetadata(base), + configMetadata: workspaceChanged, + }), + ).toMatchObject({ + reset: true, + changedCategories: ["workspaceConfig"], + }); + }); + it("keeps model-only compatibility as an additional reset reason", async () => { const base = await buildSessionConfigMetadata(); diff --git a/server/src/services/heartbeat.ts b/server/src/services/heartbeat.ts index 5b3ceae2a7..b92f81358b 100644 --- a/server/src/services/heartbeat.ts +++ b/server/src/services/heartbeat.ts @@ -4842,6 +4842,12 @@ function buildSessionConfigCategoryValues(input: { agentConfigRevision: unknown; }) { const sanitizedSecretManifest = sanitizeSecretManifestForConfigFingerprint(input.secretManifest); + const workspaceConfig = { ...parseObject(input.workspaceConfig) }; + // issues.updatedAt also advances for comments and status changes. Those are + // wake deltas, not execution-workspace configuration changes, so including + // the timestamp here makes every comment invalidate an otherwise reusable + // task session. + delete workspaceConfig.issueConfigRevisionAt; return { adapter: { adapterType: input.adapterType, @@ -4852,7 +4858,7 @@ function buildSessionConfigCategoryValues(input: { modelProfile: input.modelProfile, instructions: input.instructions, issueOverrides: input.issueOverrides, - workspaceConfig: input.workspaceConfig, + workspaceConfig, environment: input.environment, envBindings: { environment: { env: input.environmentEnv },