Fixes the 2026-08-29 daemon zombie class: a client disconnected while its
application request was in flight, the request never observed cancellation,
and runtime_worker_finish joined the request thread with no deadline. The
disconnect path wedged forever, the generation stayed formally RUNNING, and
a dead process held the endpoint pipes and UI port for nine hours while
every new client timed out with no diagnostic.
Four changes:
- runtime: the disconnect-path reap of an in-flight application request now
waits at most RUNTIME_ABANDONED_REQUEST_JOIN_TIMEOUT_MS (30 s), then logs
daemon.application_request_unresponsive (peer pid, request token) and
fail-stops the process. The kernel releases every native claim and the
next client starts a fresh generation. Test seams expose the ceiling and
replace the terminal stop with a recordable hook.
- diagnostics: a transport connect that reaches a live process but gets no
valid answer now resolves the holder's kernel pid
(GetNamedPipeServerProcessId / SO_PEERCRED). `daemon start` classifies a
mute holder as RESERVED (no doomed competitor spawns) and names the pid
in its timeout message; `daemon status` prints "not responding (endpoint
held by pid N)" with recovery guidance instead of "not running".
- frontend: the maintenance monitor's quiet-state poll drops from 10 ms to
250 ms. Each probe revalidates the whole ancestor directory chain on
Windows; at 100 Hz that burned ~20% of a core for the life of every MCP
session (7762 CPU-seconds on one 9.5 h session). Detection latency spends
0.25 s of the 15 s activation drain budget.
- version_cohort: the healthy fresh-claim record is renamed from
"claimed_unheld" (misread as a stale-claim anomaly during the incident
diagnosis) to "claimed_fresh" with prior_holder=none.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: OhOkThisIsFine <102485413+OhOkThisIsFine@users.noreply.github.com>