From 5bb34ce522c44e6f0fe0a3b330dba3e9165e9a55 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Wed, 11 Nov 2026 06:56:55 -0500 Subject: [PATCH] fix: scope change-time settings to Windows --- src/artifacts/ChangeTimeBaseline.test.ts | 18 +++++++++++++++ src/filesystem/ChangeTimeBaseline.ts | 10 ++++++--- tests/acceptance/analysisSettingsMcp.test.ts | 23 ++++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/artifacts/ChangeTimeBaseline.test.ts b/src/artifacts/ChangeTimeBaseline.test.ts index beaddc79a1..1ca5f24014 100644 --- a/src/artifacts/ChangeTimeBaseline.test.ts +++ b/src/artifacts/ChangeTimeBaseline.test.ts @@ -253,3 +253,21 @@ it("uses a Windows settle override before rejecting an invalid launch seed", asy ); expect(observed.ctimeMs).toBe(1_000); }); + +it("does not consume Windows limits on other hosts and preserves cancellation during the read", async () => { + const controller = new AbortController(); + const reason = new Error("cancelled during baseline read"); + await expect( + settleChangeTimeBaseline( + async () => { + controller.abort(reason); + return statsAt(1_000); + }, + { + platform: "linux", + environment: { REA_WINDOWS_CHANGE_TIME_DRIFT_MS: "invalid" }, + signal: controller.signal, + }, + ), + ).rejects.toBe(reason); +}); diff --git a/src/filesystem/ChangeTimeBaseline.ts b/src/filesystem/ChangeTimeBaseline.ts index 9e7ccf6b4f..18cddff603 100644 --- a/src/filesystem/ChangeTimeBaseline.ts +++ b/src/filesystem/ChangeTimeBaseline.ts @@ -28,8 +28,7 @@ export interface ChangeTimeSettleSeams { readonly sleep?: ((delayMs: number) => Promise) | undefined; /** Launch environment. Omission reads `process.env`. */ readonly environment?: - | Readonly> - | undefined; + Readonly> | undefined; /** Already resolved budget. Omission parses `environment`. */ readonly limits?: WindowsChangeTimeSettleLimits | undefined; /** Cancels the wait; the last observation is not returned once aborted. */ @@ -101,10 +100,15 @@ export const settleChangeTimeBaseline = async ( read: () => Promise, seams: ChangeTimeSettleSeams = {}, ): Promise => { + throwIfAborted(seams); + if ((seams.platform ?? process.platform) !== "win32") { + const observed = await read(); + throwIfAborted(seams); + return observed; + } const limits = limitsFrom(seams); const settled = { ...seams, limits }; const sleep = seams.sleep ?? defaultSleep; - throwIfAborted(seams); let observed = await read(); for (let poll = 0; poll < limits.maxPolls; poll += 1) { if (!changeTimeMayStillDrift(observed, settled)) return observed; diff --git a/tests/acceptance/analysisSettingsMcp.test.ts b/tests/acceptance/analysisSettingsMcp.test.ts index 53f2be0b9e..dbae94f312 100644 --- a/tests/acceptance/analysisSettingsMcp.test.ts +++ b/tests/acceptance/analysisSettingsMcp.test.ts @@ -518,3 +518,26 @@ it("starts with invalid runtime seeds and recovers ASAR and capture work on the await client.close(); } }, 30000); + +it.skipIf(process.platform === "win32")( + "ignores Windows ChangeTime launch settings on other hosts", + async () => { + const root = await createTestTempDirectory("rea-non-windows-change-time-"); + const archive = join(root, "small.asar"); + await writeFile(archive, asarHeaderBytes(JSON.stringify({ files: {} }))); + const { client, call } = await connectLocalToolsMcp({ + REA_WINDOWS_CHANGE_TIME_DRIFT_MS: "invalid", + REA_WINDOWS_CHANGE_TIME_SETTLE_POLL_MS: "invalid", + REA_WINDOWS_CHANGE_TIME_SETTLE_MAX_POLLS: "invalid", + }); + try { + const opened = await call("open_binary", { path: archive }); + expect(opened.isError, JSON.stringify(opened)).not.toBe(true); + const inspected = await call("inspect_artifact", {}); + expect(inspected.isError, JSON.stringify(inspected)).not.toBe(true); + } finally { + await client.close(); + } + }, + 30000, +);