fix(supervisor): compat shim for COMPUTE checkpoint type (#3703)

Workloads bundled with CLI versions before v4.4.4 use a strict zod enum
for `checkpoint.type` that only allows DOCKER and KUBERNETES. When a
customer's runs are routed via the compute path, those old runners
receive `type: "COMPUTE"` on `/snapshots/since/...` and `/dequeue`
responses and fail validation - blocking silent migration of existing
deployments.

The workload never reads the field - only validates the shape. Rewriting
COMPUTE -> KUBERNETES on the way out lets older runners keep parsing
while the database and internal services keep the real value. Limited to
the two workload-facing endpoints whose response includes a checkpoint;
`/continue`, `/attempts/start`, `/attempts/complete` all return shapes
without one.

Followup to #3114.
This commit is contained in:
nicktrn
2026-05-22 15:41:30 +01:00
committed by GitHub
parent 1015876b98
commit ddad9700d7
2 changed files with 24 additions and 2 deletions
@@ -0,0 +1,6 @@
---
area: supervisor
type: fix
---
Keep older workloads working when checkpoints are produced by the compute path
+18 -2
View File
@@ -43,6 +43,18 @@ const WorkloadActionParams = z.object({
snapshotFriendlyId: z.string(),
});
// Workloads bundled into customer task images before CLI v4.4.4 use a strict
// zod enum for checkpoint type that only allows DOCKER and KUBERNETES. The
// workload never reads this field - it only validates the response shape - so
// rewriting it to a known value keeps older runners working without affecting
// the value stored in the database or seen by internal services.
function legacifyCheckpointType<T extends { checkpoint?: { type: string } | null }>(item: T): T {
if (item.checkpoint?.type === "COMPUTE") {
return { ...item, checkpoint: { ...item.checkpoint, type: "KUBERNETES" } } as T;
}
return item;
}
type WorkloadServerEvents = {
runConnected: [
{
@@ -384,7 +396,9 @@ export class WorkloadServer extends EventEmitter<WorkloadServerEvents> {
return;
}
reply.json(sinceSnapshotResponse.data satisfies WorkloadRunSnapshotsSinceResponseBody);
reply.json({
snapshots: sinceSnapshotResponse.data.snapshots.map(legacifyCheckpointType),
} satisfies WorkloadRunSnapshotsSinceResponseBody);
},
}
)
@@ -409,7 +423,9 @@ export class WorkloadServer extends EventEmitter<WorkloadServerEvents> {
return;
}
reply.json(dequeueResponse.data satisfies WorkloadDequeueFromVersionResponseBody);
reply.json(
dequeueResponse.data.map(legacifyCheckpointType) satisfies WorkloadDequeueFromVersionResponseBody
);
},
});