On Windows the spawned `kilo serve` process could outlive the IDE and then
block the next IDE launch (a surviving child inherits IDE handles). The
app-close teardown also deadlocked: closeForShutdown() closed the CLI's
streams before killing it, and on Windows closing a process stream while a
reader thread is mid-read hangs until the process dies — so IDE shutdown
blocked, the JVM stayed alive, the kill-on-close job never fired, and the
CLI lingered until killed by hand (which is what unblocked restart).
- Bind the CLI tree to the IDE via a Windows Job Object with
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, so the OS terminates the tree when
the IDE exits for any reason (clean exit, halt(), crash, force-kill).
- Reorder app-close teardown to kill first, then close streams
(shutdownTree), removing the deadlock. Order is regression-tested.
- Map the Job Object kernel32 functions directly (the IDE's bundled JNA
omits them) using only the platform's core JNA; JNA is not bundled to
avoid a jnidispatch collision. Guard on JnaLoader.isLoaded() before any
Native access and release handles via try/finally ownership.
- Best-effort / Windows-only: assign() returns null off-Windows, without
JNA, on non-64-bit, or on any native failure, so callers keep the
existing process-tree kill. No CLI changes.
Adds diagnostics on the setup/app-close paths, a Windows-gated integration
test, and ordering regression tests.
Known limitation: kill-on-close also terminates `persistent=true`
background processes on Windows; full support needs a coordinated CLI
breakaway change (CREATE_BREAKAWAY_FROM_JOB + JOB_OBJECT_LIMIT_BREAKAWAY_OK).
The process-tree kill test flaked on Linux CI ("child still alive"): a
SIGKILLed orphan reparents to init and lingers as an unreaped zombie that
ProcessHandle still reports alive (onExit never fires for a non-child), so the
kill worked but was unobservable. The test now treats a zombie ('Z' in /proc)
or already-reaped process as exited, with an isAlive fallback off Linux.
Also simplify confirmKilled to wait only on the tracked parent (our real
child, reliably reaped) and report descendants best-effort, so wait=true
shutdown no longer blocks on an orphan exit it cannot observe.
Locks in the disposal terminal-state guarantee (R5): after dispose(), a late SSE onClosed/onFailure with a stale source must not resurrect the connection or schedule a reconnect. Deterministic — drives the listener directly, no sleeps.
Address review: guarding TurnOpen/TurnClose/status with revertOp dropped
the transitions entirely, so a turn that started just before a rollback
could leave the prompt idle while the server turn was still active. Record
the underlying turn/status transition in revertDeferred while the revert is
held and reconcile it when the operation releases: an aborted turn releases
to Idle, a still-active turn releases back to Busy/Retry/Offline. Failed
reverts still surface the error explicitly.
Confirm process-tree exit after SIGKILL on the non-Windows kill path, and
escalate SIGTERM to SIGKILL on the shutdown-hook (no-wait) path so a
SIGTERM-ignoring tree is not orphaned on JVM exit. Deflake the process-tree
kill test by capturing both children deterministically and add coverage for
the no-wait escalation.
Make JetBrains IDE app-close teardown non-blocking: send SIGTERM and return,
letting the JVM shutdown hook confirm exit, so quitting the IDE no longer
stalls the EDT for up to several seconds. Stop plugin-unload teardown from
waiting on the lifecycle mutex behind an in-flight download.
Add a parent-death watchdog to `kilo serve`: the VS Code extension and
JetBrains plugin pass their PID via KILO_PARENT_PID and the server exits when
that process disappears, covering hard client kills where no signal or
shutdown hook runs.
Address review feedback on inline revert progress:
- Backend revert/unrevert now use a cancellable OkHttp call with a bounded
request timeout, so coroutine cancellation aborts the in-flight request and
a stuck request can no longer lock the session indefinitely.
- Cancel now actually cancels the active rollback/redo job instead of only
relabeling the progress text; the UI stays locked on the operation until the
backend request returns, then clears or reports the final outcome.
- The revert watchdog cancels the timed-out job and releases the operation
lock via failReverting rather than leaving revertOp alive forever.
- TurnOpen/TurnClose no longer clobber an active revert operation state.
- The revert banner represents active rollback (not just redo) with inline
progress and disabled redo controls so progress never silently disappears.
- Clear the mounted hover copy overlay when reverting starts so the rollback
toolbar no longer lingers next to the progress row.
Address PR review: honor wait on the Windows tree-kill path so a
reported success is verified against actual process exit; re-enumerate
descendants before SIGKILL so children forked during the grace period
are escalated too. Rework the process-tree test to use a parent that
does not clean up its children so the assertions prove the tree kill.
Add a test for the disposal terminal-state guard.