Repository navigation
Cron .py scripts import Hermes and its dependencies again on POSIX managed-store installs (#123044, #123440, salvage #123051) - #124973
Merged
kshitijk4poor merged 6 commits intoSep 27, 2026
Conversation
On managed-store installs the gateway's store Python carries the repo and the managed site-packages only in-process; cron .py scripts spawned with sys.executable re-resolve imports from scratch and die with ModuleNotFoundError (NousResearch#123044). Run them on the selected dependency venv's interpreter instead — its pyvenv.cfg site-packages (editable installs included) need no PYTHONPATH overlay, so children the script spawns never inherit the store's paths and import CPython-3.14 extension modules on a foreign interpreter (NousResearch#123440). Lazy installs are disabled for script children so hermes_bootstrap imports off the store-record venv cannot republish launchers. The .pth bootstrap routing in _script_argv is tightened to PYTHONPATH-carrying overlays, which the POSIX path no longer produces. (cherry picked from commit 2d6976c)
_posix_cron_python_invocation called selected_venv(repo) unguarded; the four RuntimeError cases it is documented to raise would escape through _script_argv (which runs before _run_job_script's try), crash the tick, and leave the execution row in running forever — the exact half-migrated install the test docstring already claimed was supported. Mirror the Windows bootstrap's degrade-don't-crash contract: warn and run on the caller's interpreter. Adds the raising-selection case to the POSIX invocation test (red on the previous head), a sealed-payload caveat on the e2e test's hand-written .pth, and a pointer from the Windows invocation docstring to its POSIX counterpart. (cherry picked from commit 8d5b3b5)
…ection fails the run Follow-up to the venv-interpreter change above. - The selected venv resolves Hermes from its generation's workspace snapshot, which only a dependency change (uv.lock / extras / Python / plugins, pm/packages.py::expected_stamp) rebuilds. After a code-only update, scripts imported older Hermes code than the gateway runs. A `python -c` bootstrap now puts the live checkout right after the script's directory, in-process, so nothing is inherited by the script's children (no PYTHONPATH, NousResearch#123440). - POSIX dispatch moves into _script_argv (platform branch), so _windows_cron_python_invocation is Windows-only again and the PYTHONPATH-keyed bootstrap gate goes back to its original form. - The interpreter path comes from pm.environments.venv_python. - _script_argv now runs inside _run_job_script's try. PM record reads (store manifest, facts, selection) can raise ValueError/KeyError as well as RuntimeError; before, those escaped the runner and stranded the execution row, and on POSIX the local fallback handed the script to the bare store interpreter (the NousResearch#123044 symptom). A broken selection is now a failed run with the PM error, per selected_venv's contract. This also covers the same pre-existing gap on the Windows committed_venv path. - Tests: two invariant tests replace the three change-detector tests (live checkout beats a snapshot on sys.path, venv site-packages resolve, script-dir sys.path[0], no PYTHONPATH; broken selection fails the run instead of escaping).
POSIX cron scripts now consult the managed store. Without isolation, a test run from a PM-managed checkout (the standard ~/.hermes/hermes-agent) reads the host's store and install records, trips the home I/O guard, and would run scripts on the host's dependency venv. Point HERMES_RUNTIME_DIR at an empty per-test dir for tests/cron.
Review follow-up on the live-checkout bootstrap.
- runpy.run_path runs the script under a temporary __main__ that is
swapped back once the body returns, so atexit handlers or non-daemon
threads that pickle script-defined classes failed ("not found as
__main__.Foo") where a plain `python script.py` works. The bootstrap
now installs a real __main__ module (SourceFileLoader, __cached__ as
in a plain run) and execs the compiled script in it. Tracebacks keep
one bootstrap frame instead of runpy's three.
- Under -P / PYTHONSAFEPATH there is no cwd entry to replace, so the
repo is prepended instead of overwriting a stdlib path.
- Interpreter via pm.environments.project_python (the existing
venv_python(selected_venv()) helper).
- A missing venv interpreter still falls back to the caller's Python
but now logs a warning naming it, like the Windows bootstrap fallback.
- Docstrings: the lazy-install gate's real reason (a script importing
hermes_bootstrap could complete a source update and execv itself onto
the bare store Python via sys.orig_argv); _script_argv no longer
claims "else sys.executable".
_posix_cron_script_argv fell back to sys.executable when the selected venv's interpreter was gone. On a managed-store install that is the bare store Python, so the run logged a warning and then died with the same ModuleNotFoundError as NousResearch#123044. A half-deleted venv reaches this branch because _recorded_venv only checks pyvenv.cfg. Raise instead; _run_job_script's try reports it as a failed run naming the missing interpreter, matching how a broken PM record is handled. The existing broken-selection test is parametrized over both causes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cron
.pyjob scripts can import Hermes and its dependencies again on POSIX managed-store installs, and they load the live checkout rather than a stale copy (fixes #123044 and #123440; salvages #123051 by @liuhao1024).WHO/WHEN: macOS/Linux users on 0.21.5+ managed-store (PM) installs whose cron jobs run a Python
script/prerun. WHY: the gateway runs on the bare store Python, whose repo + dependency paths exist only in-process, so a script it spawned died withModuleNotFoundError: No module named 'cron'(ormcp,ruamel,google, ...).Changes
pyvenv.cfgresolves its site-packages. NoPYTHONPATHoverlay. Every child the script spawns would inherit one, and a foreign interpreter would then load the store's compiled extensions (Cron .py scripts can't import Hermes modules or managed dependencies on macOS PM-managed installs (0.21.5) #123440).pm/packages.py::expected_stamprebuilds only when uv.lock, extras, the Python version or plugins change. After a code-only update, scripts imported older Hermes code than the gateway was running (on the test machine, 26 files intools/differed). Apython -cbootstrap now puts the live checkout right after the script's directory, in-process only. It keepspython script.pysemantics: a real__main__module that survives into atexit handlers,sys.path[0]set to the script's directory, and-Prespected.HERMES_DISABLE_LAZY_INSTALLS=1). A script importinghermes_bootstrapcould otherwise complete a source update andexecvitself onto the bare store Python viasys.orig_argv. This also reaches ahermes …command the script shells out to: lazy installs are refused there._script_argvnow runs inside_run_job_script'stry. A corrupt store manifest or a bad selection record used to escape the runner and strand the execution row, on the existing Windowscommitted_venvpath too. They now reportScript execution failed: <PM error>. The contributor's second commit made this case fall back to the caller's interpreter; that is reversed, because on this path the caller's interpreter is the bare store Python, i.e. the original bug.tests/cronnow pointsHERMES_RUNTIME_DIRat an empty directory, so script tests don't read the host's PM store.Validation
_run_job_script, real PM install (macOS)ModuleNotFoundError: No module named 'cron'cronfrom the live checkout; child and grandchildPYTHONPATHunsetpython script.pyon the real venv (spawn Pool,sys.exit(3), traceback, pickle incl. atexit, sibling import,-P, non-ASCII path)tests/cron+test_cron_windows_venv_abi+test_windows_native_supporttests/cron: same 13 failing files and counts as main on the same host (pre-existing home-guard failures from a checkout inside the real hermes home)Closes #123044. Closes #123440. Closes #123547.
Also supersedes the script-side fixes in #122742 (@nhod, earliest), #123095 (@gumclaw), #123479 (@webtecnica), #123548 (@vladneshrus-cpu), and the script half of #123525 (@Sahilvishnaliya). The restart-safe external worker (#122222 / #123400, #123738 and the worker half of #123525) is a separate path and is not changed here.