From e5edf34b6bf309b9aed66577018885cc9e0c413e Mon Sep 17 00:00:00 2001 From: Nicky Leach Date: Wed, 8 Jul 2026 16:43:46 +0000 Subject: [PATCH] fix(server): preserve heartbeat workspace environment Preserve environmentId when heartbeat refreshes issue workspace settings so accepted-plan children keep the same pre-realization environment intent. Co-Authored-By: Paperclip --- ...heartbeat-accepted-plan-workspace-refresh.test.ts | 3 +++ server/src/services/heartbeat.ts | 7 +++++-- server/src/services/issues.ts | 12 ++++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/server/src/__tests__/heartbeat-accepted-plan-workspace-refresh.test.ts b/server/src/__tests__/heartbeat-accepted-plan-workspace-refresh.test.ts index 5ae4be084aa..0854bcc39d5 100644 --- a/server/src/__tests__/heartbeat-accepted-plan-workspace-refresh.test.ts +++ b/server/src/__tests__/heartbeat-accepted-plan-workspace-refresh.test.ts @@ -290,6 +290,7 @@ describeEmbeddedPostgres("accepted plan workspace refresh", () => { const projectWorkspaceId = randomUUID(); const sharedExecutionWorkspaceId = randomUUID(); const issueId = randomUUID(); + const issueEnvironmentId = randomUUID(); const agentId = randomUUID(); const repoRoot = await createGitRepo(); tempRoots.push(repoRoot); @@ -367,6 +368,7 @@ describeEmbeddedPostgres("accepted plan workspace refresh", () => { executionWorkspaceId: sharedExecutionWorkspaceId, executionWorkspaceSettings: { mode: "isolated_workspace", + environmentId: issueEnvironmentId, }, createdAt: new Date(), updatedAt: new Date(), @@ -444,6 +446,7 @@ describeEmbeddedPostgres("accepted plan workspace refresh", () => { expect(refreshedIssue?.executionWorkspaceId).not.toBe(sharedExecutionWorkspaceId); expect(refreshedIssue?.executionWorkspaceSettings).toMatchObject({ mode: "isolated_workspace", + environmentId: issueEnvironmentId, }); const isolatedRows = await db diff --git a/server/src/services/heartbeat.ts b/server/src/services/heartbeat.ts index 759f3a7b644..2ead3691d35 100644 --- a/server/src/services/heartbeat.ts +++ b/server/src/services/heartbeat.ts @@ -10265,7 +10265,7 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {}) : null; const isolatedWorkspacesEnabled = (await instanceSettings.getExperimental()).enableIsolatedWorkspaces; const issueExecutionWorkspaceSettings = isolatedWorkspacesEnabled - ? parseIssueExecutionWorkspaceSettings(issueContext?.executionWorkspaceSettings) + ? parseIssueExecutionWorkspaceSettings(issueContext?.executionWorkspaceSettings, { includeEnvironmentId: true }) : null; const contextProjectId = readNonEmptyString(context.projectId); const executionProjectId = issueContext?.projectId ?? contextProjectId; @@ -11108,7 +11108,10 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {}) }; } if (Object.keys(nextIssuePatch).length > 0) { - await issuesSvc.update(issueId, nextIssuePatch); + await issuesSvc.update(issueId, { + ...nextIssuePatch, + preserveExecutionWorkspaceEnvironmentId: true, + }); } } if (persistedExecutionWorkspace) { diff --git a/server/src/services/issues.ts b/server/src/services/issues.ts index 0c26f316359..eafb00b8b34 100644 --- a/server/src/services/issues.ts +++ b/server/src/services/issues.ts @@ -6167,6 +6167,7 @@ export function issueService(db: Db) { blockedByIssueIds?: string[]; actorAgentId?: string | null; actorUserId?: string | null; + preserveExecutionWorkspaceEnvironmentId?: boolean; }, dbOrTx: any = db, ) => { @@ -6182,6 +6183,7 @@ export function issueService(db: Db) { blockedByIssueIds, actorAgentId, actorUserId, + preserveExecutionWorkspaceEnvironmentId, ...issueData } = data; const isolatedWorkspacesEnabled = (await instanceSettings.getExperimental()).enableIsolatedWorkspaces; @@ -6244,8 +6246,14 @@ export function issueService(db: Db) { : existing.executionWorkspacePreference; const nextExecutionWorkspaceSettings = issueData.executionWorkspaceSettings !== undefined - ? parseIssueExecutionWorkspaceSettings(issueData.executionWorkspaceSettings) - : parseIssueExecutionWorkspaceSettings(existing.executionWorkspaceSettings); + ? parseIssueExecutionWorkspaceSettings( + issueData.executionWorkspaceSettings, + { includeEnvironmentId: preserveExecutionWorkspaceEnvironmentId === true }, + ) + : parseIssueExecutionWorkspaceSettings( + existing.executionWorkspaceSettings, + { includeEnvironmentId: preserveExecutionWorkspaceEnvironmentId === true }, + ); if (issueData.executionWorkspaceSettings !== undefined) { patch.executionWorkspaceSettings = nextExecutionWorkspaceSettings ? { ...nextExecutionWorkspaceSettings }