fix: retain periodic checkpoint timestamps after failed final saves
A failed final flush does not erase an earlier successful periodic checkpoint. Cover interrupted continuations and replacement sandboxes for both saved and failed prior runs. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
b8d003baff
commit
22d61006b9
|
|
@ -41,11 +41,14 @@ it("initializes storage only after access succeeds and reuses it while checking
|
|||
expect(mocks.list).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it.each(["same-sandbox", "replacement-sandbox"])("keeps the last successful save visible after an interrupted run on %s", async (sandboxKey) => {
|
||||
it.each([
|
||||
["same-sandbox", "saved"], ["replacement-sandbox", "saved"],
|
||||
["same-sandbox", "failed"], ["replacement-sandbox", "failed"],
|
||||
])("keeps the last successful checkpoint visible after an interrupted run on %s (prior state %s)", async (sandboxKey, priorState) => {
|
||||
const oldSave = {
|
||||
folderRun: { runId: "saved-run", manifest: { agentId: "agent", sandboxKey: "same-sandbox" },
|
||||
state: "saved", lastSavedAt: new Date("2026-09-08T12:00:00Z"), error: null, refreshRequested: false },
|
||||
status: "succeeded",
|
||||
state: priorState, lastSavedAt: new Date("2026-09-08T12:00:00Z"), error: null, refreshRequested: false },
|
||||
status: priorState === "failed" ? "failed" : "succeeded",
|
||||
};
|
||||
const interrupted = {
|
||||
folderRun: { runId: "interrupted-run", manifest: { agentId: "agent", sandboxKey },
|
||||
|
|
@ -60,6 +63,6 @@ it.each(["same-sandbox", "replacement-sandbox"])("keeps the last successful save
|
|||
expect(response.body).toEqual([
|
||||
expect.objectContaining({ runId: "interrupted-run", state: "failed", active: false,
|
||||
error: "Run ended before its final file save completed.", lastSavedAt: null }),
|
||||
expect.objectContaining({ runId: "saved-run", state: "saved", lastSavedAt: "2026-09-08T12:00:00.000Z" }),
|
||||
expect.objectContaining({ runId: "saved-run", state: priorState, lastSavedAt: "2026-09-08T12:00:00.000Z" }),
|
||||
]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -87,8 +87,8 @@ export function workFolderRoutes(db: Db, provider?: StorageProvider) {
|
|||
sql`${workFolderRuns.manifest}->'folders'->>${owner.scope} = ${folder.id}`))
|
||||
.orderBy(desc(workFolderRuns.updatedAt)).limit(100);
|
||||
const isActive = (status: string) => status === "running" || status === "queued";
|
||||
const latestCompletedSave = rows.filter(({ folderRun, status }) =>
|
||||
!isActive(status) && folderRun.state === "saved" && folderRun.lastSavedAt !== null)
|
||||
const latestCheckpoint = rows.filter(({ folderRun, status }) =>
|
||||
!isActive(status) && folderRun.lastSavedAt !== null)
|
||||
.sort((a, b) => b.folderRun.lastSavedAt!.getTime() - a.folderRun.lastSavedAt!.getTime())[0];
|
||||
const projectStatus = ({ folderRun: row, status }: typeof rows[number]) => {
|
||||
const active = isActive(status);
|
||||
|
|
@ -102,13 +102,13 @@ export function workFolderRoutes(db: Db, provider?: StorageProvider) {
|
|||
const row = entry.folderRun;
|
||||
if (leases.has(row.manifest.sandboxKey)) return [];
|
||||
leases.add(row.manifest.sandboxKey);
|
||||
if (!isActive(entry.status) && row.state === "saved" && row.runId !== latestCompletedSave?.folderRun.runId) return [];
|
||||
if (!isActive(entry.status) && row.state === "saved" && row.runId !== latestCheckpoint?.folderRun.runId) return [];
|
||||
return [projectStatus(entry)];
|
||||
});
|
||||
// A failed replacement or later run must not erase the last successful
|
||||
// checkpoint, including when both runs share the same physical sandbox.
|
||||
if (latestCompletedSave && !statuses.some((status) => status.runId === latestCompletedSave.folderRun.runId)) {
|
||||
statuses.push(projectStatus(latestCompletedSave));
|
||||
if (latestCheckpoint && !statuses.some((status) => status.runId === latestCheckpoint.folderRun.runId)) {
|
||||
statuses.push(projectStatus(latestCheckpoint));
|
||||
}
|
||||
res.json(statuses);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue