fix(cron): scope no_agent job workdir to the script subprocess - #42274
fix(cron): scope no_agent job workdir to the script subprocess#42274itswane wants to merge 1 commit into
Conversation
## What does this PR do? A `no_agent` cron job with a configured `workdir` changed the scheduler's process-global working directory via `os.chdir()` for the duration of its script run. That mutation is not isolated to the job: `tick()` partitions due jobs into a single-thread sequential pool (workdir/profile jobs) and a parallel pool (everything else), and dispatches both fire-and-forget in the same tick. While the sequential `no_agent` job held the altered cwd, any workdir-less job running concurrently in the parallel pool — which deliberately leaves `TERMINAL_CWD` unset and relies on the scheduler's own cwd for its terminal/file/code-exec tools — resolved its relative paths against the foreign job's directory. The `os.chdir()` was also inert for its stated purpose: `_run_job_script` already passes an explicit `cwd=str(path.parent)` to `subprocess.run`, which overrides the process cwd, so the documented "script runs from the workdir" behaviour never actually took effect. The fix threads the workdir through to the subprocess as an explicit `cwd_override` instead of mutating the process. This scopes the working directory to the child process — removing the cross-job contamination — and, as a side effect, makes the documented per-job workdir behaviour work correctly. ## Related Issue N/A ## Type of Change - [x] 🐛 Bug fix (non-breaking change that fixes an issue) ## Changes Made - `cron/scheduler.py`: added an optional `cwd_override` parameter to `_run_job_script`; when set to an existing directory it becomes the subprocess `cwd`, otherwise the call falls back to the script's own directory (with a logged warning for a stale/invalid override). - `cron/scheduler.py`: removed the process-global `os.chdir()`/restore block in the `no_agent` branch of `_run_job_impl` and now pass the job's `workdir` through as `cwd_override`. - `tests/cron/test_cron_no_agent.py`: added coverage for `cwd_override` (honoured / fallback) and a regression test asserting a `no_agent`+`workdir` job runs its script from the workdir without changing the scheduler's process cwd. ## How to Test 1. Run the targeted suite: `scripts/run_tests.sh tests/cron/test_cron_no_agent.py`. 2. Confirm `test_run_job_no_agent_does_not_mutate_process_cwd` passes — it asserts the script's `pwd` equals the configured workdir while `os.getcwd()` is unchanged before and after the run. 3. Run the surrounding suites for regressions: `scripts/run_tests.sh tests/cron/`. ## Checklist ### Code - [x] I've read the Contributing Guide - [x] My commit messages follow Conventional Commits (`fix(scope):`, `feat(scope):`, etc.) - [x] I searched for existing PRs to make sure this isn't a duplicate - [x] My PR contains **only** changes related to this fix/feature (no unrelated commits) - [x] I've run `pytest tests/ -q` and all tests pass - [x] I've added tests for my changes (required for bug fixes, strongly encouraged for features) - [x] I've tested on my platform: macOS 15 (Darwin 25.5.0) ### Documentation & Housekeeping - [x] I've updated relevant documentation (README, `docs/`, docstrings) — docstring for `_run_job_script` updated - [x] I've updated `cli-config.yaml.example` if I added/changed config keys — or N/A - [x] I've updated `CONTRIBUTING.md` or `AGENTS.md` if I changed architecture or workflows — or N/A - [x] I've considered cross-platform impact (Windows, macOS) per the compatibility guide — `subprocess.run(cwd=...)` is portable; no platform-specific paths added - [x] I've updated tool descriptions/schemas if I changed tool behavior — or N/A
|
Verification review — reviewed the full diff; clean implementation. The
No issues found. LGTM. |
✅ Verification ReviewScope: Process-safety fix — replaces Assessment: Clean implementation.
No issues found. LGTM. |
|
Thanks for the focused no-agent fix. I verified the premise against current main: The proposed explicit subprocess cwd removes that mutation and gives the no-agent script the configured workdir. The existing regression coverage is appropriately targeted. Scope note: agent pre-run scripts still call the same helper without a workdir at Automated hermes-sweeper review. |
What does this PR do?
A
no_agentcron job with a configuredworkdirchanged the scheduler'sprocess-global working directory via
os.chdir()for the duration of itsscript run. That mutation is not isolated to the job:
tick()partitions duejobs into a single-thread sequential pool (workdir/profile jobs) and a parallel
pool (everything else), and dispatches both fire-and-forget in the same tick.
While the sequential
no_agentjob held the altered cwd, any workdir-less jobrunning concurrently in the parallel pool — which deliberately leaves
TERMINAL_CWDunset and relies on the scheduler's own cwd for itsterminal/file/code-exec tools — resolved its relative paths against the foreign
job's directory.
The
os.chdir()was also inert for its stated purpose:_run_job_scriptalready passes an explicit
cwd=str(path.parent)tosubprocess.run, whichoverrides the process cwd, so the documented "script runs from the workdir"
behaviour never actually took effect.
The fix threads the workdir through to the subprocess as an explicit
cwd_overrideinstead of mutating the process. This scopes the workingdirectory to the child process — removing the cross-job contamination — and, as
a side effect, makes the documented per-job workdir behaviour work correctly.
Related Issue
N/A
Type of Change
Changes Made
cron/scheduler.py: added an optionalcwd_overrideparameter to_run_job_script; when set to an existing directory it becomes thesubprocess
cwd, otherwise the call falls back to the script's own directory(with a logged warning for a stale/invalid override).
cron/scheduler.py: removed the process-globalos.chdir()/restore block inthe
no_agentbranch of_run_job_impland now pass the job'sworkdirthrough as
cwd_override.tests/cron/test_cron_no_agent.py: added coverage forcwd_override(honoured / fallback) and a regression test asserting a
no_agent+workdirjob runs its script from the workdir without changing the scheduler's process
cwd.
How to Test
scripts/run_tests.sh tests/cron/test_cron_no_agent.py.test_run_job_no_agent_does_not_mutate_process_cwdpasses — itasserts the script's
pwdequals the configured workdir whileos.getcwd()is unchanged before and after the run.scripts/run_tests.sh tests/cron/.Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — docstring for_run_job_scriptupdatedcli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/Asubprocess.run(cwd=...)is portable; no platform-specific paths added