ProcessAdapter.run cannot spawn during JVM shutdown, so no recovery step can reach git #616
Labels
No labels
agent-ready
bug
contract
documentation
duplicate
enhancement
good first issue
help wanted
invalid
needs-human
needs-input
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
iterative-works/iw-cli#616
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What happens
ProcessAdapter.run(core/adapters/Process.scala:66-83) reaches every external command throughos.proc(...).call(...). os-lib registers a JVM shutdown hook for each spawned child:Runtime.addShutdownHookthrowsIllegalStateException: Shutdown in progressonce shutdown has begun. So no caller ofProcessAdapter.runcan run a command while the JVM is shutting down — the call does not fail, it throws, and the throw escapes anyEitherthe caller wrapped it in.Why it matters
A recovery step is exactly the code that runs while the process is dying, and it is the code that most needs to reach git.
kanon's
batch-implementhit this twice on 2026-09-23. The host killed a phase session at exit 137, the signal reached the whole process group, and the driver entered recovery with shutdown already begun:The second kill left about 600 lines of finished work uncommitted.
commitLeftoversexists to save exactly that, and it could not run. The work was committed by hand.The condition the recovery step is written for is the condition in which it cannot spawn.
Requested
Give
ProcessAdapter.runadestroyOnExit: Boolean = trueparameter and pass it toos.proc.call. The default keeps today's behaviour for every existing caller. A caller that must survive shutdown — a recovery or cleanup step — passesfalse, os-lib registers no hook, and the command runs.destroyOnExit = falsealso means the child is not killed when the JVM exits, which is the wanted behaviour here: agit committhat outlives the dying driver by a moment is the point.Notes
falsefromcommitLeftoversand also catches the throw, so a spawn that still fails becomes a warning naming the uncommitted files rather than a stack trace.Runtime.getRuntime().addShutdownHookthrowing is observable by callingrunfrom inside a shutdown hook. A run withdestroyOnExit = falsecompletes there; a run with the default throws.Provenance
Found while implementing dev-docs #346. The two directions that issue proposed both fail: registering our own shutdown hook earlier does not stop os-lib from registering its own per spawn.