test: require explicit final checkpoints in warm qualification

Expose the existing finalization timestamp through the scoped sync API and reject periodic-only or out-of-run saves.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta 2026-09-09 00:06:05 -05:00
parent 1d6ee1a642
commit a24d331c41
6 changed files with 91 additions and 1 deletions

View File

@ -361,7 +361,10 @@ at version 2 so existing native session backups remain restorable.
Warm continuity qualification reads the full run record before selecting the
persistence contract; company run listings omit the scoped-folder manifest.
Each completed scoped turn must have its own successful final save before the
harness reads cached bytes. Legacy host-workspace fallback is used only when
harness reads cached bytes. The sync API exposes `finalCheckpointAt` from the
run manifest, distinct from periodic `lastSavedAt`. Qualification requires a
successful terminal run and finalization/save timestamps within that run; a
periodic save cannot substitute for completed finalization. Legacy host-workspace fallback is used only when
the full run record has no scoped manifest.
The three-turn fixture supplies a shell script that compares exact bytes before

View File

@ -37,6 +37,8 @@ export interface WorkFolderSyncStatus {
agentId?: string;
state: "starting" | "saved" | "saving" | "failed";
lastSavedAt: string | null;
/** Successful run finalization; absent on older servers and periodic-only saves. */
finalCheckpointAt?: string | null;
error: string | null;
refreshRequested: boolean;
active: boolean;

View File

@ -66,3 +66,21 @@ it.each([
expect.objectContaining({ runId: "saved-run", state: priorState, lastSavedAt: "2026-09-08T12:00:00.000Z" }),
]);
});
it.each([undefined, "2026-09-09T04:00:00.000Z"])(
"distinguishes periodic saves from explicit finalization (%s)",
async (finalCheckpointAt) => {
const saved = {
folderRun: { runId: "run", manifest: { agentId: "agent", sandboxKey: "sandbox", finalCheckpointAt },
state: "saved", lastSavedAt: new Date("2026-09-09T04:00:00Z"), error: null, refreshRequested: false },
status: "succeeded",
};
const query = { from: () => query, innerJoin: () => query, where: () => query,
orderBy: () => query, limit: async () => [saved] };
const app = express();
app.use("/api", workFolderRoutes({ select: () => query } as unknown as Db));
const response = await request(app).get("/api/companies/11111111-1111-4111-8111-111111111111/work-folders/task/22222222-2222-4222-8222-222222222222/sync").expect(200);
expect(response.body).toEqual([expect.objectContaining({ runId: "run", state: "saved", active: false,
lastSavedAt: "2026-09-09T04:00:00.000Z", finalCheckpointAt: finalCheckpointAt ?? null })]);
},
);

View File

@ -95,6 +95,7 @@ export function workFolderRoutes(db: Db, provider?: StorageProvider) {
const interrupted = !active && (row.state === "starting" || row.state === "saving");
return { runId: row.runId, agentId: row.manifest.agentId, state: interrupted ? "failed" : row.state,
lastSavedAt: row.lastSavedAt, error: interrupted ? row.error ?? "Run ended before its final file save completed." : row.error,
finalCheckpointAt: row.manifest.finalCheckpointAt ?? null,
refreshRequested: row.refreshRequested, active };
};
const leases = new Set<string>();

View File

@ -34,6 +34,7 @@ async function fixture() {
state: "saved",
active: false,
lastSavedAt: "2026-09-09T04:00:00Z",
finalCheckpointAt: "2026-09-09T04:00:00Z" as string | null | undefined,
};
const response = {
ok: () => true,
@ -44,6 +45,9 @@ async function fixture() {
id: "run",
companyId: "company",
agentId: "agent",
status: "succeeded",
startedAt: "2026-09-09T03:59:00Z" as string | null,
finishedAt: "2026-09-09T04:00:01Z" as string | null,
contextSnapshot: { paperclipWorkFolders: binding } as Record<
string,
unknown
@ -200,6 +204,50 @@ describe("warm workspace persistence observation", () => {
expect(input.api.request.get).not.toHaveBeenCalled();
});
it.each(["failed", "cancelled", "timed_out", "running"])(
"rejects a %s run even when its periodic checkpoint is saved",
async (status) => {
const { input, fullRun } = await fixture();
fullRun.status = status;
await expect(readWarmWorkspaceFile(input)).rejects.toThrow(
"Warm turn must succeed",
);
expect(input.api.request.get).not.toHaveBeenCalled();
},
);
it.each([
{ finalCheckpointAt: undefined },
{ finalCheckpointAt: null },
{ finalCheckpointAt: "invalid" },
{ finalCheckpointAt: "2026-09-09T03:58:59Z" },
{ finalCheckpointAt: "2026-09-09T04:00:02Z" },
{ lastSavedAt: "2026-09-09T03:59:59Z" },
{ lastSavedAt: "2026-09-09T04:00:02Z" },
])(
"rejects periodic-only or unrelated finalization evidence: %j",
async (override) => {
const { input, saved } = await fixture();
Object.assign(saved, override);
await expect(readWarmWorkspaceFile(input)).rejects.toThrow(
"explicit finalization",
);
expect(input.api.request.get).not.toHaveBeenCalled();
},
);
it.each(["startedAt", "finishedAt"] as const)(
"rejects missing run boundary %s",
async (field) => {
const { input, fullRun } = await fixture();
fullRun[field] = null;
await expect(readWarmWorkspaceFile(input)).rejects.toThrow(
"explicit finalization",
);
expect(input.api.request.get).not.toHaveBeenCalled();
},
);
it.each([
{ runId: "older-run" },
{ state: "failed" },

View File

@ -31,6 +31,9 @@ export async function readWarmWorkspaceFile(input: {
const fullRun = await api.get<
typeof run & {
contextSnapshot: Record<string, unknown> | null;
status: string;
startedAt: string | null;
finishedAt: string | null;
}
>(`/api/heartbeat-runs/${encodeURIComponent(run.id)}`);
assert.equal(fullRun.id, run.id);
@ -46,6 +49,7 @@ export async function readWarmWorkspaceFile(input: {
!Array.isArray(fullRun.contextSnapshot)),
"Invalid full run context",
);
assert.equal(fullRun.status, "succeeded", "Warm turn must succeed");
const manifest = fullRun.contextSnapshot?.paperclipWorkFolders;
if (manifest === undefined || manifest === null) {
return {
@ -71,6 +75,20 @@ export async function readWarmWorkspaceFile(input: {
saved && saved.state === "saved" && !saved.active && saved.lastSavedAt,
"The completed warm turn must have a successful final file save",
);
const startedAt = Date.parse(fullRun.startedAt ?? "");
const finishedAt = Date.parse(fullRun.finishedAt ?? "");
const finalizedAt = Date.parse(saved.finalCheckpointAt ?? "");
const savedAt = Date.parse(saved.lastSavedAt);
assert(
Number.isFinite(startedAt) &&
Number.isFinite(finishedAt) &&
Number.isFinite(finalizedAt) &&
Number.isFinite(savedAt) &&
startedAt <= finalizedAt &&
finalizedAt <= savedAt &&
savedAt <= finishedAt,
"Warm turn requires explicit finalization and save timestamps within this run",
);
const response = await api.request.get(
`${base}/content?path=${encodeURIComponent(filename)}`,
);