@@ -111,6 +111,7 @@ async function main(): Promise<void> {
111111 let canaryCoverageComplete = true
112112 let diagnosticsRetained = true
113113 let failed = false
114+ let signalCleanupStarted = false
114115
115116 const cleanup = ( ) : Promise < void > => {
116117 if ( cleanupPromise ) return cleanupPromise
@@ -163,6 +164,8 @@ async function main(): Promise<void> {
163164 }
164165
165166 const handleSignal = async ( signal : NodeJS . Signals ) : Promise < void > => {
167+ if ( signalCleanupStarted ) return
168+ signalCleanupStarted = true
166169 failed = true
167170 const exitCode = signal === 'SIGINT' ? 130 : 143
168171 process . exitCode = exitCode
@@ -211,7 +214,9 @@ async function main(): Promise<void> {
211214 if ( ! cleanupProcess . pid ) {
212215 throw new Error ( 'Detached E2E cleanup supervisor started without a process ID' )
213216 }
214- runLock . transfer ( cleanupProcess . pid )
217+ if ( ! runLock . transfer ( cleanupProcess . pid ) ) {
218+ throw new Error ( 'Detached E2E cleanup supervisor could not acquire run-lock ownership' )
219+ }
215220 lockTransferred = true
216221 cleanupProcess . unref ( )
217222 console . error ( `Detached E2E cleanup supervisor started as PID ${ cleanupProcess . pid } ` )
@@ -227,7 +232,8 @@ async function main(): Promise<void> {
227232 }
228233 process . exit ( exitCode )
229234 }
230- // `once` is intentional: a second signal uses the OS default force termination.
235+ // Both signal types share a synchronous single-flight guard so opposite signals cannot launch
236+ // competing supervisors while the first handler awaits child-process startup.
231237 const handleSigint = ( ) : void => void handleSignal ( 'SIGINT' )
232238 const handleSigterm = ( ) : void => void handleSignal ( 'SIGTERM' )
233239 process . once ( 'SIGINT' , handleSigint )
@@ -412,8 +418,10 @@ async function main(): Promise<void> {
412418 process . off ( 'SIGINT' , handleSigint )
413419 process . off ( 'SIGTERM' , handleSigterm )
414420 setManagedProcessGroupObserver ( null )
415- if ( cleanupSucceeded ) runLock . release ( )
416- else runLock . retain ( 'normal cleanup failed; inspect diagnostics and clean resources manually' )
421+ if ( ! signalCleanupStarted ) {
422+ if ( cleanupSucceeded ) runLock . release ( )
423+ else runLock . retain ( 'normal cleanup failed; inspect diagnostics and clean resources manually' )
424+ }
417425 console . info (
418426 diagnosticsRetained
419427 ? `E2E ${ failed ? 'failed' : 'completed' } ; diagnostics: ${ runDirectory } `
0 commit comments