0.9.5
Process.terminate() only reaches the child when the window process exits cleanly, and it does not always: killed from outside, a crash, the system shutting down. The server stayed up alone, and the symptom surfaced far from the cause — you update the app, open it again, and the window finds something answering on the port and attaches to that: the old server, serving the previous version's files. New version installed, old version running.
The server now watches the process that started it, which is the only approach that covers every way the parent can go, including the one that runs no code on the other side. And the window handles SIGTERM and SIGINT itself, because AppKit does not turn them into quit — pkill and system shutdown were leaving both processes standing.
Measured on a real bundle: killed politely the server is gone in 0.4s, killed outright in 1.2s.