Problem
Sandbox.arun() in nohup mode always returns exit_code=0 when the process completes within wait_timeout, regardless of whether the command actually succeeded or failed.
result = await sandbox.arun(cmd="nonexistent_command_xyz", session="s", mode="nohup")
# Current: result.exit_code == 0 ← wrong, command not found should be 127
This makes it impossible for callers to distinguish between a successful command and a failed one via exit_code.
Root Cause
handle_nohup_output hardcodes exit_code=0 when success=True (process completed within timeout), with no mechanism to capture the actual exit status of the background command.
Proposed Fix
Add a capture_exit_code: bool = False parameter to arun(). When True, the nohup command is wrapped in a subshell that writes the exit code to a .rc file:
# before
nohup {cmd} < /dev/null > {out} 2>&1 & echo PIDSTART${!}PIDEND;disown
# after (capture_exit_code=True)
( nohup {cmd} < /dev/null > {out} 2>&1; echo $? > {rc} ) & echo PIDSTART${!}PIDEND;disown
After the process completes, the .rc file is read to obtain the actual exit code.
The flag defaults to False for full backward compatibility.
Acceptance Criteria
arun(cmd="nonexistent_command", mode="nohup", capture_exit_code=True) → exit_code=127
arun(cmd="exit 42", mode="nohup", capture_exit_code=True) → exit_code=42
arun(cmd="echo hello", mode="nohup", capture_exit_code=True) → exit_code=0
- Default
capture_exit_code=False behavior unchanged
- Timeout still returns
exit_code=1
Problem
Sandbox.arun()in nohup mode always returnsexit_code=0when the process completes withinwait_timeout, regardless of whether the command actually succeeded or failed.This makes it impossible for callers to distinguish between a successful command and a failed one via
exit_code.Root Cause
handle_nohup_outputhardcodesexit_code=0whensuccess=True(process completed within timeout), with no mechanism to capture the actual exit status of the background command.Proposed Fix
Add a
capture_exit_code: bool = Falseparameter toarun(). WhenTrue, the nohup command is wrapped in a subshell that writes the exit code to a.rcfile:After the process completes, the
.rcfile is read to obtain the actual exit code.The flag defaults to
Falsefor full backward compatibility.Acceptance Criteria
arun(cmd="nonexistent_command", mode="nohup", capture_exit_code=True)→exit_code=127arun(cmd="exit 42", mode="nohup", capture_exit_code=True)→exit_code=42arun(cmd="echo hello", mode="nohup", capture_exit_code=True)→exit_code=0capture_exit_code=Falsebehavior unchangedexit_code=1