fix(companion): Cancel pending restart when stopping a companion

A stop issued while a restart was in flight (state STOPPING,
restart_pending set) was ignored: handle_exit checked restart_pending
first and respawned the companion the user had just stopped. Clear
restart_pending in stop_process so manual stop wins.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tanmoy Sarkar 2026-06-12 23:23:18 +05:30
parent 642387dd0e
commit 68ac2e4bb2
2 changed files with 20 additions and 0 deletions

View File

@ -419,6 +419,9 @@ class CompanionManager:
if process is None:
return False, "unknown companion %s" % name
process.manual_stop = True
# A stop must win over an in-flight restart: clearing this keeps
# handle_exit from respawning a companion the user asked to stop.
process.restart_pending = False
if process.state in (State.STOPPED, State.STOPPING):
return True, "%s already %s" % (name, process.state.lower())
if process.state == State.BACKOFF:

View File

@ -420,6 +420,23 @@ def test_stop_process_unknown():
assert not ok
def test_stop_during_restart_cancels_pending_restart():
manager = make_manager("rq")
proc = manager.processes["rq"]
proc.state = State.STOPPING
proc.pid = 70
proc.restart_pending = True
with mock.patch("os.kill") as kill:
ok, _ = manager.stop_process("rq")
kill.assert_not_called()
assert ok and proc.manual_stop is True and proc.restart_pending is False
# On exit the companion now settles STOPPED instead of being respawned.
with mock.patch.object(manager, "spawn_process") as spawn:
manager.handle_exit(proc)
spawn.assert_not_called()
assert proc.state == State.STOPPED
def test_signal_number_resolves_name():
assert CompanionManager._signal_number("SIGKILL") == signal.SIGKILL
assert CompanionManager._signal_number(9) == 9