From 6ae2dc5de7a2abd2d6b694b38b5e64d46a164ca4 Mon Sep 17 00:00:00 2001 From: Dotta Date: Thu, 10 Sep 2026 14:25:05 -0500 Subject: [PATCH] Preserve runner process start time across controller recovery Use the verified remote process marker and adopted process identity when reporting runner process metadata, rather than the creation time of a replacement controller transport. Co-Authored-By: Paperclip --- .../src/live/runnerd-codex-transport.test.ts | 3 ++- .../paperclip-runner/src/live/runnerd-codex-transport.ts | 3 ++- .../services/native-runtime/native-session-executor.test.ts | 1 + .../src/services/native-runtime/native-session-executor.ts | 6 +++++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts index 714c101067..eea716d9dd 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts @@ -3212,7 +3212,7 @@ async function verifyLiveRunnerAdoption(mismatchedCheckpoint: boolean) { adoptExistingRunner: { pid: runnerPid!, processGroupId: runnerPid, - startedAt: new Date().toISOString(), + startedAt: "2026-09-01T10:00:00.000Z", isAlive: () => { try { process.kill(runnerPid!, 0); @@ -3237,6 +3237,7 @@ async function verifyLiveRunnerAdoption(mismatchedCheckpoint: boolean) { }), ); expect(adopted.evidence().runnerPid).toBe(runnerPid); + expect(adopted.transport.processInfo?.().startedAt).toBe("2026-09-01T10:00:00.000Z"); expect(duplicateLauncher).not.toHaveBeenCalled(); expect(adopted.evidence().diagnostics).toContain( `adopted runner ${runnerPid} authenticated to its durable PRP authority`, diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport.ts index 09557e4de4..de1ff8c14c 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport.ts @@ -2515,7 +2515,8 @@ class DurablePrpCodexTransport implements CodexAppServerTransport { return { pid: this.#evidence.runnerPid, processGroupId: this.#evidence.runnerProcessGroupId, - startedAt: this.#startedAt, + // Reconnecting creates a new transport, not a new runner process. + startedAt: this.options.adoptExistingRunner?.startedAt ?? this.#handle?.startedAt ?? this.#startedAt, exited: this.#evidence.runnerExited, exitCode: this.#evidence.runnerExitCode, signal: this.#evidence.runnerSignal, diff --git a/server/src/services/native-runtime/native-session-executor.test.ts b/server/src/services/native-runtime/native-session-executor.test.ts index e24058aa17..64127ff651 100644 --- a/server/src/services/native-runtime/native-session-executor.test.ts +++ b/server/src/services/native-runtime/native-session-executor.test.ts @@ -341,6 +341,7 @@ describe("remote runner process supervision", () => { startedAt: "2026-09-06T00:00:00.000Z", }); expect(handle.child.pid).toBe(4321); + expect(handle.startedAt).toBe("2026-09-06T00:00:00.000Z"); expect(handle.child.kill("SIGKILL")).toBe(true); await vi.waitFor(() => diff --git a/server/src/services/native-runtime/native-session-executor.ts b/server/src/services/native-runtime/native-session-executor.ts index f008c331a1..e4c28c4819 100644 --- a/server/src/services/native-runtime/native-session-executor.ts +++ b/server/src/services/native-runtime/native-session-executor.ts @@ -6362,7 +6362,11 @@ export function createRemoteRunnerProcessLauncher(input: { }; } })(); - return { child, completion }; + return { + child, + completion, + get startedAt() { return launchedIdentity?.startedAt; }, + }; }; }