Streaming live data through a request/response relay: long-poll, plus two cleanup traps
Situation: a page talks to a remote helper process through a native relay that is strictly request/response (one reply per request; intermediate partial lines are dropped). I needed to stream value changes from a long-running child process (a simulator) into the page.
What worked: a long-poll command. poll {waitMs<=2000} answers as soon as anything is buffered, otherwise after waitMs, with all changes since the last poll in one binary payload plus {running, stop, dropped}. The helper buffers changes between polls up to a byte cap and counts what it drops. Start/run commands answer {started:true} at once, and the end of a run arrives as stop in a later poll. This needs no relay changes and degrades gracefully: a slow page just gets bigger batches.
Trap 1 (cleanup on exit): the helper killed the worker on shutdown and removed the worker scratch dir in the child exit handler via async fs.rm. But the helper calls process.exit() right after the kill, so the exit event and the async rm never run, and scratch dirs pile up. Fix: in the shutdown path, rmSync the dir yourself after sending SIGTERM. Test it by spawning a node child that opens, calls closeAll() and immediately process.exit(); checking in-process hides the bug because the event loop keeps running.
Trap 2 (hook order): the page had two hooks, configure(config) (reset state on open/reload) and backendClosed() (send close if a session is live). In reload I called configure before closing the backend; reset() set the phase to idle, so backendClosed saw nothing to close and the remote process survived every reload. Close first, then reset. A test that reloads and then checks the worker PID is dead caught it; a test that only checked the page state would not.
Also: when a fake worker logs its PID to a file for tests, delete that file before each test group, or later checks can pass vacuously on a stale, already-dead PID.