@@ -653,9 +653,30 @@ def _install_via_dispatcher(version: str, pkg_spec: str, thread_id: int) -> dict
653653 total_time = (time .perf_counter () - start_time ) * 1000
654654 safe_print (f"\n 🎉 DONE total={ format_duration (total_time )} " )
655655
656- # Use _exit to bypass atexit handlers and finalizers — daemon background
657- # threads (sockets/keepalives) would otherwise block sys.exit indefinitely.
658- sys .exit (0 )
656+ # Always dump daemon log before shutdown — lets us see FS watcher state,
657+ # thread activity, and any silent errors even on a fully successful run.
658+ dump_daemon_log ("FINAL DAEMON LOG (always)" , only_on_error = False )
659+
660+ # Shutdown daemon before exit so CI runner doesn't wait for orphaned children
661+ # (FS watcher watchdog threads, socket listeners, etc.)
662+ try :
663+ from omnipkg .isolation .worker_daemon import DaemonClient
664+ safe_print (" 🛑 Shutting down daemon..." )
665+ DaemonClient ().shutdown ()
666+ time .sleep (2 ) # give it a moment to actually die
667+ safe_print (" ✅ Daemon shutdown sent" )
668+ except Exception as e :
669+ safe_print (f" ⚠️ Daemon shutdown failed: { e } " )
670+
671+ # Dump again after shutdown so we can see if it actually stopped cleanly
672+ # or if the FS watcher / socket threads are still logging after the signal.
673+ dump_daemon_log ("POST-SHUTDOWN DAEMON LOG (always)" , only_on_error = False )
674+
675+ # os._exit bypasses atexit handlers and thread finalizers entirely.
676+ # sys.exit(0) goes through Python teardown which can hang if any background
677+ # thread (DaemonClient keepalive, watchdog observer, etc.) is still running.
678+ import os
679+ os ._exit (0 )
659680
660681
661682if __name__ == "__main__" :
0 commit comments