From e0436d3699b41e58040bd13fad605d04c21f0b1d Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 31 Aug 2026 13:02:26 -0400 Subject: [PATCH] fix: stabilize release evidence timing --- .../runtime-core/src/host-command-executor.ts | 48 +++++++++++-------- .../src/browser-actions-runner.ts | 4 +- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/packages/runtime-core/src/host-command-executor.ts b/packages/runtime-core/src/host-command-executor.ts index 99e7b51f2..0de5e15ef 100644 --- a/packages/runtime-core/src/host-command-executor.ts +++ b/packages/runtime-core/src/host-command-executor.ts @@ -87,6 +87,7 @@ export async function executeHostCommand(config: HostCommandExecutorConfig, inpu let timedOut = false let settled = false const memorySamples: HostCommandMemorySample[] = [] + const memorySampleTasks = new Set>() const child = spawn(config.command, args, { cwd, @@ -102,16 +103,20 @@ export async function executeHostCommand(config: HostCommandExecutorConfig, inpu setTimeout(() => terminateHostCommandProcessTree(child.pid, "SIGKILL"), terminationGraceMs).unref() }, timeoutMs) - const memoryTimer = setInterval(() => { + const sampleMemory = () => { if (child.pid === undefined) { return } - void sampleHostCommandProcessTreeRssBytes(child.pid).then((rssBytes) => { + const task = sampleHostCommandProcessTreeRssBytes(child.pid).then((rssBytes) => { if (rssBytes !== undefined) { memorySamples.push({ elapsedMs: Date.now() - started, rssBytes }) } }).catch(() => undefined) - }, memorySampleIntervalMs) + memorySampleTasks.add(task) + void task.finally(() => memorySampleTasks.delete(task)) + } + sampleMemory() + const memoryTimer = setInterval(sampleMemory, memorySampleIntervalMs) child.stdout?.on("data", (chunk: Buffer) => { artifactWriters?.stdout.write(chunk) @@ -145,24 +150,25 @@ export async function executeHostCommand(config: HostCommandExecutorConfig, inpu settled = true clearTimeout(timer) clearInterval(memoryTimer) - const durationMs = Date.now() - started - const result: HostCommandExecutorResult = { - command: config.command, - args, - cwd, - exitCode: exitCode ?? -1, - signal: signal ?? "", - stdout, - stderr, - durationMs, - timedOut, - outputTruncated, - failureClassification: classifyHostCommandFailure(exitCode, signal, timedOut), - commandSummary, - memorySamples, - peakRssBytes: memorySamples.reduce((peak, sample) => Math.max(peak, sample.rssBytes), 0), - } - void finalizeHostCommandArtifacts(artifactWriters, result).then((artifacts) => { + void Promise.all([...memorySampleTasks]).then(async () => { + const durationMs = Date.now() - started + const result: HostCommandExecutorResult = { + command: config.command, + args, + cwd, + exitCode: exitCode ?? -1, + signal: signal ?? "", + stdout, + stderr, + durationMs, + timedOut, + outputTruncated, + failureClassification: classifyHostCommandFailure(exitCode, signal, timedOut), + commandSummary, + memorySamples, + peakRssBytes: memorySamples.reduce((peak, sample) => Math.max(peak, sample.rssBytes), 0), + } + const artifacts = await finalizeHostCommandArtifacts(artifactWriters, result) resolveResult(artifacts ? { ...result, artifacts } : result) }).catch(reject) }) diff --git a/packages/runtime-playground/src/browser-actions-runner.ts b/packages/runtime-playground/src/browser-actions-runner.ts index a3dd144ca..f22ffe4d8 100644 --- a/packages/runtime-playground/src/browser-actions-runner.ts +++ b/packages/runtime-playground/src/browser-actions-runner.ts @@ -427,7 +427,7 @@ export async function runBrowserActionsCommand({ const serialized = serializeBrowserError("probe-error", error) errors.push(serialized) stepRecords.push(browserStepRecord(index, step, "failed", recordStartedAt, recordStartedAtMs, page.url(), { error: serialized })) - pendingError = error instanceof Error ? error : new Error(String(error)) + pendingError ??= error instanceof Error ? error : new Error(String(error)) if (isBrowserCommandLivenessError(pendingError)) { await page.close().catch(() => undefined) } @@ -515,7 +515,7 @@ export async function runBrowserActionsCommand({ } } } catch (error) { - pendingError = error instanceof Error ? error : new Error(String(error)) + pendingError ??= error instanceof Error ? error : new Error(String(error)) errors.push(serializeBrowserError("probe-error", error)) } finally { await settleBrowserNetworkTasks(networkTasks, livenessPolicy.networkSettleTimeoutMs).catch((error) => errors.push(serializeBrowserError("probe-error", error)))