diff --git a/doc/execution-semantics.md b/doc/execution-semantics.md index 9047d55637..5228e3a5eb 100644 --- a/doc/execution-semantics.md +++ b/doc/execution-semantics.md @@ -1767,8 +1767,13 @@ A native run can accept its result and then lose its server to a hard kill before finalization. Its result stays leased to the controller of the earlier boot, which cannot release the lease the way a graceful drain does. Startup native recovery releases that lease at once when the lease belongs to a -different boot and its recorded controller process has exited or its PID now -names another process. Only the executor's lease carries that controller +different boot in the same known kernel and PID namespace, and its recorded +controller process has exited or its PID now names another process. The +executor saves that namespace with its claim. Linux uses its boot UUID and PID +namespace; macOS uses its kernel boot-session UUID. An unreadable or unsupported +identity, a different namespace, or an older claim without this proof keeps the +original lease expiry and does not probe the recorded PID. Only the executor's +lease carries that controller identity; a finalizer or cleanup lease is never released this way and expires on its own short lease. The stored result is then finalized without another diff --git a/server/src/__tests__/heartbeat-native-restart-suspension.test.ts b/server/src/__tests__/heartbeat-native-restart-suspension.test.ts index ec813739bb..4b868efbe2 100644 --- a/server/src/__tests__/heartbeat-native-restart-suspension.test.ts +++ b/server/src/__tests__/heartbeat-native-restart-suspension.test.ts @@ -128,8 +128,12 @@ describeEmbeddedPostgres("heartbeat native recovery interrupted by graceful shut // saved when it reaches the provider session. const savedAttempts: number[] = []; const reachedProvider = async () => { - const [owner] = await f.db.select({ attempt: nativeRunFinalizations.attempt }).from(nativeRunFinalizations) + const [owner] = await f.db.select({ attempt: nativeRunFinalizations.attempt, + controllerHostHash: nativeRunFinalizations.controllerHostHash }).from(nativeRunFinalizations) .where(eq(nativeRunFinalizations.runId, f.runId)); + const actualProcess = await controllerProcess.currentControllerProcess(); + expect(actualProcess.hostHash).not.toBe("unknown"); + expect(owner.controllerHostHash).toBe(actualProcess.hostHash); savedAttempts.push(owner.attempt); throw new Error("stop at provider dispatch"); };