`interrupt` killed the child and trusted that to end the run. It didn't:
the default signal is SIGINT, and `claude --print` handles SIGINT by
writing its terminal `result` event and exiting **zero**. A zero exit is
`TurnEnd::Complete`, so `spawn_and_track`'s killed-turn early return —
the code that was supposed to stop the run — never fired, and the loop
went straight on to spawn the next goal turn. An interrupted run carried
on to completion; the interrupt changed nothing about the outcome.
Measured, not inferred: `claude --print --verbose --output-format
stream-json`, SIGINT'd mid-turn, exits 0 on every run.
So record the reason instead of inferring it. `interrupt` writes
`StopReason::Cancelled` — the same path `goal_reached`/`need_help`
already use — while it still holds the `running` lock, so there is no
instant in which the name has lost its cancel handle but not yet gained
its stop reason. `plan_after_turn` checks stop reasons ahead of the goal,
so the run stops whichever way the child ends up exiting. `force`'s
SIGKILL still produces a `TurnEnd::Killed`, and `status` still prefers
the kill record it already had.
`continue`'s budget reset is untouched: it clears the stop reason and
hands back a fresh allowance, which is what makes a cancel a pause an
operator can undo.
The test spawns the real continuation loop against a fake claude that
exits 0 on SIGINT, interrupts it, and asserts turn two never starts —
it reports `left: 2` without the fix.