fix(recovery): retain older finalized workspace reference formats

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-11-04 22:02:43 -06:00
1 parent c569ab3ecb
commit fada16ab22
2 files changed
+13 -2

No files matched your search

@@ -179,6 +179,17 @@ describe.skipIf(!support.supported)("finalized workspace recovery preservation",
await migrateRemoteWorkspace({ db, ...f, apply: true, writersStopped: true });
expect(await verifiedWorkspaceRepairReceipt(db, (await preserved(f)).run)).toBeNull();
});
it("preserves an older finalized reference with omitted optional fields", async () => {
const f = await fixture(true);
const { resourceDisposition: _disposition, ...legacy } = f.reference;
await db.update(heartbeatRuns).set({ runnerProfileJson: { nativeWorkspaceSync: {
...legacy, schema: "paperclip.native-workspace-sync/v1",
} } }).where(eq(heartbeatRuns.id, f.runId));
await migrateRemoteWorkspace({ db, ...f, apply: true, writersStopped: true });
const inspected = await inspectWorkspaceRecoverySource(db, { ...f, actionId: f.action.id });
expect(inspected.sources).toEqual([expect.objectContaining({ preservationOnly: true,
reference: { ...legacy, schema: "paperclip.native-workspace-sync/v1", resourceDisposition: null } })]);
});
it("requires the exact receipt and leaves ordinary finalized references alone", async () => {
const f = await fixture(true);
expect(await readMigratedLegacyWorkspaceSource(db, { ...f, reference: f.reference })).toBeNull();
@@ -2,7 +2,7 @@ import { isDeepStrictEqual } from "node:util";
import { and, eq, sql } from "drizzle-orm";
import { environmentLeases, executionWorkspaces, heartbeatRuns, issues, workspaceOperations, type Db } from "@paperclipai/db";
import { readRemoteWorkspaceAuthority } from "@paperclipai/shared";
import type { NativeWorkspaceSyncReference } from "./native-runtime/native-workspace-sync.js";
import { readNativeWorkspaceSyncReference, type NativeWorkspaceSyncReference } from "./native-runtime/native-workspace-sync.js";
export type LegacyWorkspaceRecoverySource = {
runId: string; issueId: string; reference: NativeWorkspaceSyncReference | null;
@@ -77,7 +77,7 @@ export async function readMigratedLegacyWorkspaceSource(db: Pick<Db, "select">,
const runMarker = run?.runnerProfileJson?.[input.reference?.state === "finalized"
? "finalizedWorkspaceRecoveryMigration" : "legacyWorkspaceRecoveryMigration"] as { workspaceId?: unknown } | undefined;
if (input.reference?.state === "finalized" && runMarker == null) return null;
if (input.reference?.state === "finalized" && !isDeepStrictEqual(input.reference, run?.runnerProfileJson?.nativeWorkspaceSync))
if (input.reference?.state === "finalized" && !isDeepStrictEqual(input.reference, readNativeWorkspaceSyncReference(run?.runnerProfileJson?.nativeWorkspaceSync)))
throw new Error("legacy_workspace_migration_receipt_invalid");
const workspaceId = input.reference?.workspaceId ?? runMarker?.workspaceId;
if (typeof workspaceId !== "string") return null;