Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ export async function completeInterruptedPlan(
// Recheck after State observation; neither a stale CF read nor an expired
// historical proof can authorize this terminal transition.
await verifyStopped();
const observedAt = new Date().toISOString();
try {
const last = await read(backend, backend.descriptor.key);
if (
Expand All @@ -166,7 +167,7 @@ export async function completeInterruptedPlan(
if (error instanceof RecoveryReplanError) throw error;
fail("state_unavailable");
}
const observedAt = new Date().toISOString();
if (Date.now() - Date.parse(observedAt) > 30_000) fail("changed");
return work(async (client) => {
const latest = await context(client);
const { existingReceipt: _before, ...before } = c;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,3 +319,31 @@ it("revalidates bindings in a new transaction after external observations", asyn
});
expect(f.recorded()).toBe(false);
});
it.each([29_000, 31_000])(
"retains the actual Runner observation time across a %sms final State read",
async (delay) => {
vi.useFakeTimers({ toFake: ["Date"] });
const observed = new Date("2026-01-01T00:00:00.000Z");
vi.setSystemTime(observed);
try {
const f = fixture();
let reads = 0;
f.read.mockImplementation(async (_backend, key) => {
if (!key.endsWith(".tflock") && ++reads === 3)
vi.setSystemTime(observed.getTime() + delay);
return null;
});
if (delay > 30_000) {
await expect(f.run()).rejects.toMatchObject({
code: "application_plan_recovery_changed",
});
expect(f.recorded()).toBe(false);
} else {
await expect(f.run()).resolves.toMatchObject({ status: "failed" });
expect(f.query.mock.calls.at(-1)?.[1][4]).toBe(observed.toISOString());
}
} finally {
vi.useRealTimers();
}
},
);
Loading