fix: reject incompatible pinned workspaces

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-08-13 21:16:41 +00:00
1 parent 79efb23338
commit 4f26f3f7b8
2 files changed
+67 -23

No files matched your search

@@ -26,6 +26,8 @@ import {
parseSessionCompactionPolicy,
PINNED_EXECUTION_WORKSPACE_RESTORE_FAILED_ERROR_CODE,
PINNED_EXECUTION_WORKSPACE_UNAVAILABLE_ERROR_CODE,
PINNED_EXECUTION_WORKSPACE_INCOMPATIBLE_ERROR_CODE,
ExecutionWorkspaceReuseFailure,
provisionExecutionWorkspaceForFreshnessDecision,
reconcileReusedExecutionWorkspaceProjectWorkspaceId,
resolveExecutionWorkspaceConfigFreshness,
@@ -41,7 +43,7 @@ import {
stripHostWorkspaceProvisionForLowTrustSandbox,
stripWorkspaceRuntimeFromExecutionRunConfig,
shouldResetTaskSessionForModelChange,
shouldReusePinnedExecutionWorkspaceForRun,
isPinnedExecutionWorkspaceIncompatibleWithTrust,
stripConfiguredModelFromSessionParams,
stripPaperclipSessionMetadataFromSessionParams,
normalizeSessionParams,
@@ -1661,31 +1663,58 @@ describe("effective run execution workspace config freshness", () => {
});
});
it("does not reuse an available shared pin for a low-trust isolated run", () => {
expect(shouldReusePinnedExecutionWorkspaceForRun({
it("marks an available shared pin incompatible with a low-trust isolated run", () => {
expect(isPinnedExecutionWorkspaceIncompatibleWithTrust({
requestedShouldReuseExisting: true,
trustPresetKind: "low_trust_review",
requestedExecutionWorkspaceMode: "isolated_workspace",
existingExecutionWorkspaceMode: "shared_workspace",
})).toBe(false);
})).toBe(true);
});
it("reuses an available isolated pin for a low-trust isolated run", () => {
expect(shouldReusePinnedExecutionWorkspaceForRun({
it("accepts an available isolated pin for a low-trust isolated run", () => {
expect(isPinnedExecutionWorkspaceIncompatibleWithTrust({
requestedShouldReuseExisting: true,
trustPresetKind: "low_trust_review",
requestedExecutionWorkspaceMode: "isolated_workspace",
existingExecutionWorkspaceMode: "isolated_workspace",
})).toBe(true);
})).toBe(false);
});
it("keeps unavailable low-trust pins on the named reuse-failure path", () => {
expect(shouldReusePinnedExecutionWorkspaceForRun({
it("keeps unavailable low-trust pins on the named availability-failure path", () => {
expect(isPinnedExecutionWorkspaceIncompatibleWithTrust({
requestedShouldReuseExisting: true,
trustPresetKind: "low_trust_review",
requestedExecutionWorkspaceMode: "isolated_workspace",
existingExecutionWorkspaceMode: null,
})).toBe(true);
})).toBe(false);
});
it("reports a named incompatibility without realizing a replacement workspace", () => {
const metadata = buildWorkspaceConfigMetadata();
const workspaceConfigFreshness = resolveExecutionWorkspaceConfigFreshness({
hasExistingWorkspace: true,
existingWorkspaceMetadata: persistedWorkspaceConfigFingerprint(metadata),
nextMetadata: metadata,
});
const failure = new ExecutionWorkspaceReuseFailure({
reason: PINNED_EXECUTION_WORKSPACE_INCOMPATIBLE_ERROR_CODE,
issueRef: { id: "issue-1", identifier: "PAP-42" },
runId: "run-1",
executionWorkspaceId: "workspace-shared",
workspaceConfigFreshness,
});
expect(failure).toMatchObject({
code: PINNED_EXECUTION_WORKSPACE_INCOMPATIBLE_ERROR_CODE,
message: expect.stringContaining("incompatible with the run's isolation policy"),
resultJson: {
executionWorkspaceReuse: expect.objectContaining({
reason: PINNED_EXECUTION_WORKSPACE_INCOMPATIBLE_ERROR_CODE,
executionWorkspaceId: "workspace-shared",
}),
},
});
});
it.each([
+28 -13
View File
@@ -398,6 +398,8 @@ export const PINNED_EXECUTION_WORKSPACE_RESTORE_FAILED_ERROR_CODE =
"pinned_execution_workspace_restore_failed";
export const PINNED_EXECUTION_WORKSPACE_UNAVAILABLE_ERROR_CODE =
"pinned_execution_workspace_unavailable";
export const PINNED_EXECUTION_WORKSPACE_INCOMPATIBLE_ERROR_CODE =
"pinned_execution_workspace_incompatible";
const EXECUTION_REVIEW_PARTICIPANT_RECOVERY_RETRY_REASON = "execution_review_participant_recovery";
const EXECUTION_REVIEW_PARTICIPANT_RECOVERY_WAKE_REASON = "execution_review_participant_recovery";
const EXECUTION_REVIEW_PARTICIPANT_RECOVERY_CAUSE = "execution_review_participant_recovery";
@@ -4355,7 +4357,7 @@ export function resolveExecutionWorkspaceReuseRequestForIssue(input: {
};
}
export function shouldReusePinnedExecutionWorkspaceForRun(input: {
export function isPinnedExecutionWorkspaceIncompatibleWithTrust(input: {
requestedShouldReuseExisting: boolean;
trustPresetKind: TrustPresetResolution["kind"];
requestedExecutionWorkspaceMode: ReturnType<typeof resolveExecutionWorkspaceMode>;
@@ -4363,10 +4365,7 @@ export function shouldReusePinnedExecutionWorkspaceForRun(input: {
}): boolean {
if (!input.requestedShouldReuseExisting) return false;
// A durable pin cannot weaken the low-trust isolation boundary. An available
// non-isolated pin is bypassed so the policy can provision a fresh isolated workspace.
// Missing or archived pins still flow through the named reuse-failure path.
return !(
return (
input.trustPresetKind === "low_trust_review"
&& input.requestedExecutionWorkspaceMode === "isolated_workspace"
&& input.existingExecutionWorkspaceMode !== null
@@ -4394,7 +4393,8 @@ export function resolveExecutionWorkspaceReuseProvisioningPolicy(input: {
type ExecutionWorkspaceReuseFailureReason =
| typeof PINNED_EXECUTION_WORKSPACE_RESTORE_FAILED_ERROR_CODE
| typeof PINNED_EXECUTION_WORKSPACE_UNAVAILABLE_ERROR_CODE;
| typeof PINNED_EXECUTION_WORKSPACE_UNAVAILABLE_ERROR_CODE
| typeof PINNED_EXECUTION_WORKSPACE_INCOMPATIBLE_ERROR_CODE;
function formatPinnedExecutionWorkspaceReuseFailure(input: {
reason: ExecutionWorkspaceReuseFailureReason;
@@ -4411,12 +4411,17 @@ function formatPinnedExecutionWorkspaceReuseFailure(input: {
: input.cause != null
? String(input.cause)
: null;
const incompatible = input.reason === PINNED_EXECUTION_WORKSPACE_INCOMPATIBLE_ERROR_CODE;
const remediation = input.reason === PINNED_EXECUTION_WORKSPACE_RESTORE_FAILED_ERROR_CODE
? "Inspect the pinned execution workspace restore/provision logs, repair or unarchive the workspace, or intentionally clear the issue's execution workspace binding before retrying."
: "Repair or unarchive the pinned execution workspace, or intentionally clear the issue's execution workspace binding before retrying.";
const message = causeMessage
? `Issue ${issueLabel} pinned execution workspace ${workspaceLabel}, but the workspace could not be restored because ${causeMessage}.`
: `Issue ${issueLabel} pinned execution workspace ${workspaceLabel}, but the workspace could not be restored.`;
: incompatible
? "Pin an isolated workspace, change the task trust policy, or intentionally clear the issue's execution workspace binding before retrying."
: "Repair or unarchive the pinned execution workspace, or intentionally clear the issue's execution workspace binding before retrying.";
const message = incompatible
? `Issue ${issueLabel} pinned execution workspace ${workspaceLabel}, but that workspace is incompatible with the run's isolation policy.`
: causeMessage
? `Issue ${issueLabel} pinned execution workspace ${workspaceLabel}, but the workspace could not be restored because ${causeMessage}.`
: `Issue ${issueLabel} pinned execution workspace ${workspaceLabel}, but the workspace could not be restored.`;
return `${message} ${remediation}`;
}
@@ -14039,7 +14044,7 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
issueExecutionWorkspacePreference: issueRef?.executionWorkspacePreference ?? null,
existingExecutionWorkspaceStatus: existingExecutionWorkspace?.status ?? null,
});
const requestedShouldReuseExisting = shouldReusePinnedExecutionWorkspaceForRun({
const pinnedExecutionWorkspaceIncompatibleWithTrust = isPinnedExecutionWorkspaceIncompatibleWithTrust({
requestedShouldReuseExisting: workspaceReuseRequest.requestedShouldReuseExisting,
trustPresetKind: trustPreset.kind,
requestedExecutionWorkspaceMode,
@@ -14047,13 +14052,14 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
? existingExecutionWorkspace?.mode ?? null
: null,
});
const reusableExistingExecutionWorkspace = requestedShouldReuseExisting
&& workspaceReuseRequest.existingExecutionWorkspaceAvailable
const requestedShouldReuseExisting = workspaceReuseRequest.requestedShouldReuseExisting;
const reusableExistingExecutionWorkspace = workspaceReuseRequest.existingExecutionWorkspaceAvailable
? existingExecutionWorkspace
: null;
if (
requestedShouldReuseExisting &&
reusableExistingExecutionWorkspace &&
!pinnedExecutionWorkspaceIncompatibleWithTrust &&
(
issueExecutionWorkspaceSettings?.mode == null ||
issueExecutionWorkspaceSettings.mode === "inherit" ||
@@ -14554,6 +14560,15 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
inferredMetadata: inferredExistingWorkspaceConfigMetadata,
nextMetadata: latestWorkspaceConfigMetadata,
});
if (pinnedExecutionWorkspaceIncompatibleWithTrust) {
throw new ExecutionWorkspaceReuseFailure({
reason: PINNED_EXECUTION_WORKSPACE_INCOMPATIBLE_ERROR_CODE,
issueRef,
runId: run.id,
executionWorkspaceId: workspaceReuseRequest.requestedExecutionWorkspaceId,
workspaceConfigFreshness,
});
}
const workspaceReuseProvisioningPolicy = resolveExecutionWorkspaceReuseProvisioningPolicy({
requestedShouldReuseExisting,
workspaceConfigFreshness,