- Force-exit timer now distinguishes serverClosed from managersStopped.
Was: if (closed) return → suppressed timer when manager hung after
server.close fired (the original bug).
Now: if (managersStopped) return → timer fires only when full drain
(HTTP close + all managers stopped) completes before the deadline.
- Added _safeEmit() helper that wraps this.emit() so a buggy listener
throwing during 'shutdown' or 'closed' doesn't abort the shutdown
sequence. Each failed listener is logged via the structured logger.
- Added 4 new tests covering: hung manager after HTTP close,
throwing shutdown listener, throwing closed listener, and the
fast-drain happy path that clears the timer cleanly.
- Logger validation now requires info/warn/error (was info-only)
- Force-exit timer now survives manager stop drain so a hung manager
cannot trap the process in half-shutdown
- installSignalHandlers is now actually idempotent — tracks installed
signals on coordinator and skips duplicates
Codex grade D flagged two real defects:
1. Managers were stopped before HTTP server finished draining, so in-flight
requests could fail when their backing services were already down.
2. _stopManager() promises weren't awaited, contradicting the documented
'declaration order' claim for async stop methods.
Fix: server.close callback now awaits _stopManagersInOrder() before
exiting. The 'shutdown' event fires first (so listeners can observe the
signal); the 'closed' event fires after all managers are stopped.