mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-08 21:01:54 +01:00
fix(recovery): require complete repair for migrated file allocations
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
af702fe2f7
commit
c569ab3ecb
2 files changed
+18
-1
No files matched your search
@@ -162,6 +162,23 @@ describe.skipIf(!support.supported)("finalized workspace recovery preservation",
|
||||
await migrateRemoteWorkspace({ db, ...f, apply: true, writersStopped: true });
|
||||
expect(await verifiedWorkspaceRepairReceipt(db, (await preserved(f)).run)).toBe(operationId);
|
||||
});
|
||||
it("does not treat one old copyback as repair of several declared allocations", async () => {
|
||||
const f = await fixture(true), otherLeaseId = randomUUID();
|
||||
const [lease] = await db.select().from(environmentLeases).where(eq(environmentLeases.id, f.reference.leaseId));
|
||||
await db.insert(environmentLeases).values({ ...lease, id: otherLeaseId, providerLeaseId: randomUUID() });
|
||||
await db.update(issueRecoveryActions).set({ evidence: { ...f.action.evidence,
|
||||
systemRecovery: { ...createSystemRecovery(f.runId), state: "complete" },
|
||||
workspaceRecovery: { ...(f.action.evidence.workspaceRecovery as object), status: "recovered" },
|
||||
historicalWorkspaceSource: { schema: "paperclip.historical-workspace-source.v1", runId: f.runId,
|
||||
leaseIds: [f.reference.leaseId, otherLeaseId] },
|
||||
} }).where(eq(issueRecoveryActions.id, f.action.id));
|
||||
await db.insert(workspaceOperations).values({ companyId: f.companyId, issueId: f.issueId,
|
||||
heartbeatRunId: f.runId, phase: "workspace_recovery", status: "succeeded", metadata: {
|
||||
recoveryActionId: f.action.id, leaseId: f.reference.leaseId, providerLeaseId: f.reference.providerLeaseId,
|
||||
descriptorSha256: f.reference.descriptorSha256, finalHostSha256: f.reference.finalHostSha256 } });
|
||||
await migrateRemoteWorkspace({ db, ...f, apply: true, writersStopped: true });
|
||||
expect(await verifiedWorkspaceRepairReceipt(db, (await preserved(f)).run)).toBeNull();
|
||||
});
|
||||
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();
|
||||
|
||||
@@ -108,7 +108,7 @@ export async function verifiedWorkspaceRepairReceipt(db: Db, run: Pick<typeof he
|
||||
if (receiptId) return receiptId;
|
||||
// A completed legacy copyback is still a valid receipt after its host
|
||||
// files move. Migration must not erase that independent recovery proof.
|
||||
if (reference?.state !== "finalized") return null;
|
||||
if (reference?.state !== "finalized" || allocations.length !== 1 || allocations[0].leaseId !== reference.leaseId) return null;
|
||||
}
|
||||
}
|
||||
if (typeof issueId !== "string" || (!remote && (!reference || reference.state !== "finalized" || !reference.finalHostSha256))) return null;
|
||||
|
||||
Reference in new issue
Block a user