* fix(process): a kill's result stands on every backend, and a failed scope stop never counts as stopped
Follow-up to #133434, which fixed these races for local processes only.
Non-local backends (spawn_via_env) settle through _env_poller_loop, which
still wrote the session's final state without the session lock:
- It set exit_code before _finish_exited, so a kill that landed first ended
as killed with the sandbox's exit code. The exit now goes through
_finish_exited alone, and a committed kill keeps its code.
- Its backend-lost path set exited/-1/lost directly, replacing a recorded
kill. It now goes through _finish_exited(reason="lost"), so the kill stands
and the completion is still published by its owner.
- A log delta (or a log-rotation reset) fetched before a kill was applied
after it, so the session's output no longer matched the kill's snapshot.
Both are now dropped once the session has exited, decided under the lock.
A failed systemd scope stop could still count as a successful stop:
- A recovered PID that died between get() and _signal_kill ignored the stop
result and returned a plain already_exited. It now reports
scope_stop_failed.
- kill_all counted already_exited as stopped even when the scope stop
failed, and only looked at _running, so a session that exited after being
picked kept a live scope that no later sweep retried. Every kill path now
stops the scope through _stop_scope, which marks a failed stop pending;
kill_all does not count a result with scope_stop_failed and also retries
finished sessions whose stop is pending.
_log_delta_command moves unchanged into tools/process_registry_env_log.py
(ProcessRegistry._log_delta_command stays as an alias) to keep
process_registry.py under its size cap.
Refs #132358. Both gaps were found by @andrexibiza in the review of #133434.
* fix(process): pruning keeps a session whose scope stop is pending
kill_all retries a failed scope stop through the finished session, but
_prune_if_needed still evicted that session by TTL (measured from process
start, so a long-running process was prunable the moment it finished) or
by capacity, leaving a possibly live scope with no retry target. Sessions
with _scope_stop_pending are now exempt from both until the stop succeeds;
capacity pruning drops the oldest prunable session instead.
Found by the independent review of #133648.