subagents, ACP: stop with SIGINT only, so claude ends its own tool commands
claude's Bash tool starts every command in a new session, so a signal to claude's process group never reaches it. claude stops those commands itself when SIGINT makes it shut down. A SIGKILL leaves them running, re-parented to init. Per the ruling on #4999, every stop is SIGINT and nothing escalates. - Subagent `interrupt` always sends SIGINT. The `force` argument is gone from the MCP tool and from `session::interrupt`. A caller that still sends `force: true` gets SIGINT; the argument is ignored. - `interrupt` used to free the session name before it signalled, so a `continue` could start a second claude on the session while the first was still exiting. The `running` map now holds a three-state `Slot` (starting, running, stopping). An interrupted claude turn is `Stopping` until the turn task's own `finish_turn` clears it. `status` says "stopping", and `start`/`continue` on that name are refused. `interrupt` still returns at once. The ACP arm is unchanged: its name is freed when `session/cancel` is sent. - ACP `Connection` drop sends its process group SIGINT, not SIGKILL, and the spawn no longer sets tokio's `kill_on_drop`, which SIGKILLed the leader after the `Drop` body. The test fixture's background grandchild resets SIGINT to default, since `&` starts it ignored. Tests: a fake claude that records SIGINT, interrupted, fails with `cancel(true)` (the old `force: true` path). An interrupted fake claude that keeps running keeps its name claimed and `continue` is refused, which fails when the slot is freed before the signal. A fake ACP agent that records SIGINT, dropped, fails with SIGKILL in `Drop`, and also with SIGINT in `Drop` but `kill_on_drop(true)` left in place. Not changed: hive-claude's idle watchdog (still SIGKILL, separate hive-claude change), the units' `KillSignal`, `machinectl kill`, and the login helper.
This commit is contained in:
parent
028c283ff9
commit
43e69d345e
7 changed files with 337 additions and 167 deletions
|
|
@ -27,7 +27,7 @@ skill's recipe for you:
|
|||
start(name, prompt_file, model?, effort?, trigger?)
|
||||
continue(name, prompt, model?, effort?)
|
||||
status(name)
|
||||
interrupt(name, force?)
|
||||
interrupt(name)
|
||||
```
|
||||
|
||||
`start` and `continue` return once the turn is under way, not once it
|
||||
|
|
@ -42,7 +42,8 @@ reports how long since that turn last produced output, so a few seconds
|
|||
means it's working and an age climbing into the minutes means it's
|
||||
wedged and worth an `interrupt`. Reach for `continue` only once you
|
||||
actually have a new instruction for it, since that spends a turn.
|
||||
`interrupt` genuinely stops a running turn (`force: true` for SIGKILL).
|
||||
`interrupt` genuinely stops a running turn, with SIGINT so claude stops
|
||||
the commands it started; `status` says `stopping` until claude has exited.
|
||||
|
||||
Model choice, prompt hygiene, splitting big batches, verify-then-report —
|
||||
everything else in this skill — applies exactly the same whether you're
|
||||
|
|
|
|||
Loading…
Reference in a new issue