Repository navigation
fix(cron): external worker dies on missing dependency site-packages (ruamel) - #122685
etrisnanto wants to merge 1 commit into
Conversation
…PYTHONPATH The restart-safe external cron worker (python -m cron.scheduler) never runs the launcher bootstrap, so the PM dependency environment never reaches its sys.path; the subprocess sanitizer then strips the gateway's PYTHONPATH and the worker dies at import hermes_yaml -> ModuleNotFoundError: No module named 'ruamel' before its ownership ack, failing every dispatched cron job. pin_hermes_tree_on_pythonpath now also prepends the committed dependency environment's site-packages alongside the repo root.
The restart-safe cron worker is spawned as `sys.executable -m cron.scheduler`, a Hermes entry point that never goes through `hermes_bootstrap`. On a self-managed (PM) install its interpreter is the store Python, which owns no dependencies and whose PYTHONPATH the subprocess sanitizer has just stripped, so the worker died at its first dependency import (`No module named 'ruamel'`, #122222) before it could publish its ownership acknowledgement -- every scheduled job landed `last_status: failed`. Salvaged from #122290 (thanks @123456hyu), whose two commits pinned the dependency site-packages and leased the generation at the worker entry. Reworked onto PM's own machinery: * `pin_hermes_tree_on_pythonpath` asks `pm.environments.committed_venv` + `site_packages()` for the generation, instead of deriving one from this process's `sys.path` behind a `pyvenv.cfg` ancestor probe. The record is scoped to the active home, so the child-spawn path no longer reads the real home -- that probe tripped `tests/home_io_guard.py` when the suite runs from a self-managed install -- and the uv-launcher, first-match-only and ordering edge cases of the old derivation go with it. * `cron/worker_bootstrap.py` is now the PM call the worker actually needs: lease + `site.addsitedir` + `sys.path`/`PYTHONPATH`/`PATH` are all `pm.environments.activate_dependencies`, the same boot `hermes_bootstrap` runs, and it holds the generation's lease for the process's life. It is gated on the `HERMES_CRON_EXTERNAL_WORKER` marker the spawn site sets (consumed on read) rather than sniffing `--external-worker-file` out of `sys.argv`, so the gateway keeps its launch contract without argv identity inference. Verified live on PM's store Python against a real generation (A/B, same child): tree-only PYTHONPATH -> `ModuleNotFoundError: No module named 'ruamel'`, rc=1; pinned env -> imports, `running_from_selected_environment` True, and the worker holds a real kernel lease while it runs (released on exit). The real `-m cron.scheduler --external-worker-file ...` entry now reaches its ownership handoff instead of dying at the import. `tests/cron/`: 1376 passed. Tests: the two invariants that carry the contract -- the pin restores the committed generation (and invents nothing without one), and the boot runs only in the marked worker, once. The six construction tests that drove the old derivation are dropped with it. Duplicate-cluster credit: #122238 (@webtecnica, earliest), #122382 (@Sakujakira), #122685 (@etrisnanto), #122719 (@aglowinthefield) and #122738 (@GabboAlarcon) diagnosed and fixed this same worker-dependency failure in parallel while this branch was rebuilt. None of their diffs is contained here -- the co-author trailers below record direction and duplicate-cluster credit, not lifted code; their roles are listed in the PR body. Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com> Co-authored-by: Manuel S. <3703103+Sakujakira@users.noreply.github.com> Co-authored-by: Eko Trisnanto <4469382+etrisnanto@users.noreply.github.com> Co-authored-by: aglowinthefield <146008217+aglowinthefield@users.noreply.github.com> Co-authored-by: GabboAlarcon <40816412+GabboAlarcon@users.noreply.github.com>
This PR fixes the restart-safe external cron worker dying on
Minor:
Unverified / please confirm:
|
The restart-safe cron worker is spawned as `sys.executable -m cron.scheduler`, a Hermes entry point that never goes through `hermes_bootstrap`. On a self-managed (PM) install its interpreter is the store Python, which owns no dependencies and whose PYTHONPATH the subprocess sanitizer has just stripped, so the worker died at its first dependency import (`No module named 'ruamel'`, NousResearch#122222) before it could publish its ownership acknowledgement -- every scheduled job landed `last_status: failed`. Salvaged from NousResearch#122290 (thanks @123456hyu), whose two commits pinned the dependency site-packages and leased the generation at the worker entry. Reworked onto PM's own machinery: * `pin_hermes_tree_on_pythonpath` asks `pm.environments.committed_venv` + `site_packages()` for the generation, instead of deriving one from this process's `sys.path` behind a `pyvenv.cfg` ancestor probe. The record is scoped to the active home, so the child-spawn path no longer reads the real home -- that probe tripped `tests/home_io_guard.py` when the suite runs from a self-managed install -- and the uv-launcher, first-match-only and ordering edge cases of the old derivation go with it. * `cron/worker_bootstrap.py` is now the PM call the worker actually needs: lease + `site.addsitedir` + `sys.path`/`PYTHONPATH`/`PATH` are all `pm.environments.activate_dependencies`, the same boot `hermes_bootstrap` runs, and it holds the generation's lease for the process's life. It is gated on the `HERMES_CRON_EXTERNAL_WORKER` marker the spawn site sets (consumed on read) rather than sniffing `--external-worker-file` out of `sys.argv`, so the gateway keeps its launch contract without argv identity inference. Verified live on PM's store Python against a real generation (A/B, same child): tree-only PYTHONPATH -> `ModuleNotFoundError: No module named 'ruamel'`, rc=1; pinned env -> imports, `running_from_selected_environment` True, and the worker holds a real kernel lease while it runs (released on exit). The real `-m cron.scheduler --external-worker-file ...` entry now reaches its ownership handoff instead of dying at the import. `tests/cron/`: 1376 passed. Tests: the two invariants that carry the contract -- the pin restores the committed generation (and invents nothing without one), and the boot runs only in the marked worker, once. The six construction tests that drove the old derivation are dropped with it. Duplicate-cluster credit: NousResearch#122238 (@webtecnica, earliest), NousResearch#122382 (@Sakujakira), NousResearch#122685 (@etrisnanto), NousResearch#122719 (@aglowinthefield) and NousResearch#122738 (@GabboAlarcon) diagnosed and fixed this same worker-dependency failure in parallel while this branch was rebuilt. None of their diffs is contained here -- the co-author trailers below record direction and duplicate-cluster credit, not lifted code; their roles are listed in the PR body. Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com> Co-authored-by: Manuel S. <3703103+Sakujakira@users.noreply.github.com> Co-authored-by: Eko Trisnanto <4469382+etrisnanto@users.noreply.github.com> Co-authored-by: aglowinthefield <146008217+aglowinthefield@users.noreply.github.com> Co-authored-by: GabboAlarcon <40816412+GabboAlarcon@users.noreply.github.com> (cherry picked from commit 7c55715)
The restart-safe cron worker is spawned as `sys.executable -m cron.scheduler`, a Hermes entry point that never goes through `hermes_bootstrap`. On a self-managed (PM) install its interpreter is the store Python, which owns no dependencies and whose PYTHONPATH the subprocess sanitizer has just stripped, so the worker died at its first dependency import (`No module named 'ruamel'`, NousResearch#122222) before it could publish its ownership acknowledgement -- every scheduled job landed `last_status: failed`. Salvaged from NousResearch#122290 (thanks @123456hyu), whose two commits pinned the dependency site-packages and leased the generation at the worker entry. Reworked onto PM's own machinery: * `pin_hermes_tree_on_pythonpath` asks `pm.environments.committed_venv` + `site_packages()` for the generation, instead of deriving one from this process's `sys.path` behind a `pyvenv.cfg` ancestor probe. The record is scoped to the active home, so the child-spawn path no longer reads the real home -- that probe tripped `tests/home_io_guard.py` when the suite runs from a self-managed install -- and the uv-launcher, first-match-only and ordering edge cases of the old derivation go with it. * `cron/worker_bootstrap.py` is now the PM call the worker actually needs: lease + `site.addsitedir` + `sys.path`/`PYTHONPATH`/`PATH` are all `pm.environments.activate_dependencies`, the same boot `hermes_bootstrap` runs, and it holds the generation's lease for the process's life. It is gated on the `HERMES_CRON_EXTERNAL_WORKER` marker the spawn site sets (consumed on read) rather than sniffing `--external-worker-file` out of `sys.argv`, so the gateway keeps its launch contract without argv identity inference. Verified live on PM's store Python against a real generation (A/B, same child): tree-only PYTHONPATH -> `ModuleNotFoundError: No module named 'ruamel'`, rc=1; pinned env -> imports, `running_from_selected_environment` True, and the worker holds a real kernel lease while it runs (released on exit). The real `-m cron.scheduler --external-worker-file ...` entry now reaches its ownership handoff instead of dying at the import. `tests/cron/`: 1376 passed. Tests: the two invariants that carry the contract -- the pin restores the committed generation (and invents nothing without one), and the boot runs only in the marked worker, once. The six construction tests that drove the old derivation are dropped with it. Duplicate-cluster credit: NousResearch#122238 (@webtecnica, earliest), NousResearch#122382 (@Sakujakira), NousResearch#122685 (@etrisnanto), NousResearch#122719 (@aglowinthefield) and NousResearch#122738 (@GabboAlarcon) diagnosed and fixed this same worker-dependency failure in parallel while this branch was rebuilt. None of their diffs is contained here -- the co-author trailers below record direction and duplicate-cluster credit, not lifted code; their roles are listed in the PR body. Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com> Co-authored-by: Manuel S. <3703103+Sakujakira@users.noreply.github.com> Co-authored-by: Eko Trisnanto <4469382+etrisnanto@users.noreply.github.com> Co-authored-by: aglowinthefield <146008217+aglowinthefield@users.noreply.github.com> Co-authored-by: GabboAlarcon <40816412+GabboAlarcon@users.noreply.github.com> (cherry picked from commit 7c55715)
|
Closing as superseded by #126344, which fixes the same failure (the cron external worker dying on the missing dependency site-packages / |
The restart-safe cron worker is spawned as `sys.executable -m cron.scheduler`, a Hermes entry point that never goes through `hermes_bootstrap`. On a self-managed (PM) install its interpreter is the store Python, which owns no dependencies and whose PYTHONPATH the subprocess sanitizer has just stripped, so the worker died at its first dependency import (`No module named 'ruamel'`, NousResearch#122222) before it could publish its ownership acknowledgement -- every scheduled job landed `last_status: failed`. Salvaged from NousResearch#122290 (thanks @123456hyu), whose two commits pinned the dependency site-packages and leased the generation at the worker entry. Reworked onto PM's own machinery: * `pin_hermes_tree_on_pythonpath` asks `pm.environments.committed_venv` + `site_packages()` for the generation, instead of deriving one from this process's `sys.path` behind a `pyvenv.cfg` ancestor probe. The record is scoped to the active home, so the child-spawn path no longer reads the real home -- that probe tripped `tests/home_io_guard.py` when the suite runs from a self-managed install -- and the uv-launcher, first-match-only and ordering edge cases of the old derivation go with it. * `cron/worker_bootstrap.py` is now the PM call the worker actually needs: lease + `site.addsitedir` + `sys.path`/`PYTHONPATH`/`PATH` are all `pm.environments.activate_dependencies`, the same boot `hermes_bootstrap` runs, and it holds the generation's lease for the process's life. It is gated on the `HERMES_CRON_EXTERNAL_WORKER` marker the spawn site sets (consumed on read) rather than sniffing `--external-worker-file` out of `sys.argv`, so the gateway keeps its launch contract without argv identity inference. Verified live on PM's store Python against a real generation (A/B, same child): tree-only PYTHONPATH -> `ModuleNotFoundError: No module named 'ruamel'`, rc=1; pinned env -> imports, `running_from_selected_environment` True, and the worker holds a real kernel lease while it runs (released on exit). The real `-m cron.scheduler --external-worker-file ...` entry now reaches its ownership handoff instead of dying at the import. `tests/cron/`: 1376 passed. Tests: the two invariants that carry the contract -- the pin restores the committed generation (and invents nothing without one), and the boot runs only in the marked worker, once. The six construction tests that drove the old derivation are dropped with it. Duplicate-cluster credit: NousResearch#122238 (@webtecnica, earliest), NousResearch#122382 (@Sakujakira), NousResearch#122685 (@etrisnanto), NousResearch#122719 (@aglowinthefield) and NousResearch#122738 (@GabboAlarcon) diagnosed and fixed this same worker-dependency failure in parallel while this branch was rebuilt. None of their diffs is contained here -- the co-author trailers below record direction and duplicate-cluster credit, not lifted code; their roles are listed in the PR body. Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com> Co-authored-by: Manuel S. <3703103+Sakujakira@users.noreply.github.com> Co-authored-by: Eko Trisnanto <4469382+etrisnanto@users.noreply.github.com> Co-authored-by: aglowinthefield <146008217+aglowinthefield@users.noreply.github.com> Co-authored-by: GabboAlarcon <40816412+GabboAlarcon@users.noreply.github.com> (cherry picked from commit 7c55715)
The restart-safe cron worker is spawned as `sys.executable -m cron.scheduler`, a Hermes entry point that never goes through `hermes_bootstrap`. On a self-managed (PM) install its interpreter is the store Python, which owns no dependencies and whose PYTHONPATH the subprocess sanitizer has just stripped, so the worker died at its first dependency import (`No module named 'ruamel'`, #122222) before it could publish its ownership acknowledgement -- every scheduled job landed `last_status: failed`. Salvaged from #122290 (thanks @123456hyu), whose two commits pinned the dependency site-packages and leased the generation at the worker entry. Reworked onto PM's own machinery: * `pin_hermes_tree_on_pythonpath` asks `pm.environments.committed_venv` + `site_packages()` for the generation, instead of deriving one from this process's `sys.path` behind a `pyvenv.cfg` ancestor probe. The record is scoped to the active home, so the child-spawn path no longer reads the real home -- that probe tripped `tests/home_io_guard.py` when the suite runs from a self-managed install -- and the uv-launcher, first-match-only and ordering edge cases of the old derivation go with it. * `cron/worker_bootstrap.py` is now the PM call the worker actually needs: lease + `site.addsitedir` + `sys.path`/`PYTHONPATH`/`PATH` are all `pm.environments.activate_dependencies`, the same boot `hermes_bootstrap` runs, and it holds the generation's lease for the process's life. It is gated on the `HERMES_CRON_EXTERNAL_WORKER` marker the spawn site sets (consumed on read) rather than sniffing `--external-worker-file` out of `sys.argv`, so the gateway keeps its launch contract without argv identity inference. Verified live on PM's store Python against a real generation (A/B, same child): tree-only PYTHONPATH -> `ModuleNotFoundError: No module named 'ruamel'`, rc=1; pinned env -> imports, `running_from_selected_environment` True, and the worker holds a real kernel lease while it runs (released on exit). The real `-m cron.scheduler --external-worker-file ...` entry now reaches its ownership handoff instead of dying at the import. `tests/cron/`: 1376 passed. Tests: the two invariants that carry the contract -- the pin restores the committed generation (and invents nothing without one), and the boot runs only in the marked worker, once. The six construction tests that drove the old derivation are dropped with it. Duplicate-cluster credit: #122238 (@webtecnica, earliest), #122382 (@Sakujakira), #122685 (@etrisnanto), #122719 (@aglowinthefield) and #122738 (@GabboAlarcon) diagnosed and fixed this same worker-dependency failure in parallel while this branch was rebuilt. None of their diffs is contained here -- the co-author trailers below record direction and duplicate-cluster credit, not lifted code; their roles are listed in the PR body. Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com> Co-authored-by: Manuel S. <3703103+Sakujakira@users.noreply.github.com> Co-authored-by: Eko Trisnanto <4469382+etrisnanto@users.noreply.github.com> Co-authored-by: aglowinthefield <146008217+aglowinthefield@users.noreply.github.com> Co-authored-by: GabboAlarcon <40816412+GabboAlarcon@users.noreply.github.com>
The restart-safe cron worker is spawned as `sys.executable -m cron.scheduler`, a Hermes entry point that never goes through `hermes_bootstrap`. On a self-managed (PM) install its interpreter is the store Python, which owns no dependencies and whose PYTHONPATH the subprocess sanitizer has just stripped, so the worker died at its first dependency import (`No module named 'ruamel'`, #122222) before it could publish its ownership acknowledgement -- every scheduled job landed `last_status: failed`. Salvaged from #122290 (thanks @123456hyu), whose two commits pinned the dependency site-packages and leased the generation at the worker entry. Reworked onto PM's own machinery: * `pin_hermes_tree_on_pythonpath` asks `pm.environments.committed_venv` + `site_packages()` for the generation, instead of deriving one from this process's `sys.path` behind a `pyvenv.cfg` ancestor probe. The record is scoped to the active home, so the child-spawn path no longer reads the real home -- that probe tripped `tests/home_io_guard.py` when the suite runs from a self-managed install -- and the uv-launcher, first-match-only and ordering edge cases of the old derivation go with it. * `cron/worker_bootstrap.py` is now the PM call the worker actually needs: lease + `site.addsitedir` + `sys.path`/`PYTHONPATH`/`PATH` are all `pm.environments.activate_dependencies`, the same boot `hermes_bootstrap` runs, and it holds the generation's lease for the process's life. It is gated on the `HERMES_CRON_EXTERNAL_WORKER` marker the spawn site sets (consumed on read) rather than sniffing `--external-worker-file` out of `sys.argv`, so the gateway keeps its launch contract without argv identity inference. Verified live on PM's store Python against a real generation (A/B, same child): tree-only PYTHONPATH -> `ModuleNotFoundError: No module named 'ruamel'`, rc=1; pinned env -> imports, `running_from_selected_environment` True, and the worker holds a real kernel lease while it runs (released on exit). The real `-m cron.scheduler --external-worker-file ...` entry now reaches its ownership handoff instead of dying at the import. `tests/cron/`: 1376 passed. Tests: the two invariants that carry the contract -- the pin restores the committed generation (and invents nothing without one), and the boot runs only in the marked worker, once. The six construction tests that drove the old derivation are dropped with it. Duplicate-cluster credit: #122238 (@webtecnica, earliest), #122382 (@Sakujakira), #122685 (@etrisnanto), #122719 (@aglowinthefield) and #122738 (@GabboAlarcon) diagnosed and fixed this same worker-dependency failure in parallel while this branch was rebuilt. None of their diffs is contained here -- the co-author trailers below record direction and duplicate-cluster credit, not lifted code; their roles are listed in the PR body. Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com> Co-authored-by: Manuel S. <3703103+Sakujakira@users.noreply.github.com> Co-authored-by: Eko Trisnanto <4469382+etrisnanto@users.noreply.github.com> Co-authored-by: aglowinthefield <146008217+aglowinthefield@users.noreply.github.com> Co-authored-by: GabboAlarcon <40816412+GabboAlarcon@users.noreply.github.com>
The restart-safe cron worker is spawned as `sys.executable -m cron.scheduler`, a Hermes entry point that never goes through `hermes_bootstrap`. On a self-managed (PM) install its interpreter is the store Python, which owns no dependencies and whose PYTHONPATH the subprocess sanitizer has just stripped, so the worker died at its first dependency import (`No module named 'ruamel'`, NousResearch#122222) before it could publish its ownership acknowledgement -- every scheduled job landed `last_status: failed`. Salvaged from NousResearch#122290 (thanks @123456hyu), whose two commits pinned the dependency site-packages and leased the generation at the worker entry. Reworked onto PM's own machinery: * `pin_hermes_tree_on_pythonpath` asks `pm.environments.committed_venv` + `site_packages()` for the generation, instead of deriving one from this process's `sys.path` behind a `pyvenv.cfg` ancestor probe. The record is scoped to the active home, so the child-spawn path no longer reads the real home -- that probe tripped `tests/home_io_guard.py` when the suite runs from a self-managed install -- and the uv-launcher, first-match-only and ordering edge cases of the old derivation go with it. * `cron/worker_bootstrap.py` is now the PM call the worker actually needs: lease + `site.addsitedir` + `sys.path`/`PYTHONPATH`/`PATH` are all `pm.environments.activate_dependencies`, the same boot `hermes_bootstrap` runs, and it holds the generation's lease for the process's life. It is gated on the `HERMES_CRON_EXTERNAL_WORKER` marker the spawn site sets (consumed on read) rather than sniffing `--external-worker-file` out of `sys.argv`, so the gateway keeps its launch contract without argv identity inference. Verified live on PM's store Python against a real generation (A/B, same child): tree-only PYTHONPATH -> `ModuleNotFoundError: No module named 'ruamel'`, rc=1; pinned env -> imports, `running_from_selected_environment` True, and the worker holds a real kernel lease while it runs (released on exit). The real `-m cron.scheduler --external-worker-file ...` entry now reaches its ownership handoff instead of dying at the import. `tests/cron/`: 1376 passed. Tests: the two invariants that carry the contract -- the pin restores the committed generation (and invents nothing without one), and the boot runs only in the marked worker, once. The six construction tests that drove the old derivation are dropped with it. Duplicate-cluster credit: NousResearch#122238 (@webtecnica, earliest), NousResearch#122382 (@Sakujakira), NousResearch#122685 (@etrisnanto), NousResearch#122719 (@aglowinthefield) and NousResearch#122738 (@GabboAlarcon) diagnosed and fixed this same worker-dependency failure in parallel while this branch was rebuilt. None of their diffs is contained here -- the co-author trailers below record direction and duplicate-cluster credit, not lifted code; their roles are listed in the PR body. Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com> Co-authored-by: Manuel S. <3703103+Sakujakira@users.noreply.github.com> Co-authored-by: Eko Trisnanto <4469382+etrisnanto@users.noreply.github.com> Co-authored-by: aglowinthefield <146008217+aglowinthefield@users.noreply.github.com> Co-authored-by: GabboAlarcon <40816412+GabboAlarcon@users.noreply.github.com> (cherry picked from commit 7c55715)
The restart-safe cron worker is spawned as `sys.executable -m cron.scheduler`, a Hermes entry point that never goes through `hermes_bootstrap`. On a self-managed (PM) install its interpreter is the store Python, which owns no dependencies and whose PYTHONPATH the subprocess sanitizer has just stripped, so the worker died at its first dependency import (`No module named 'ruamel'`, NousResearch#122222) before it could publish its ownership acknowledgement -- every scheduled job landed `last_status: failed`. Salvaged from NousResearch#122290 (thanks @123456hyu), whose two commits pinned the dependency site-packages and leased the generation at the worker entry. Reworked onto PM's own machinery: * `pin_hermes_tree_on_pythonpath` asks `pm.environments.committed_venv` + `site_packages()` for the generation, instead of deriving one from this process's `sys.path` behind a `pyvenv.cfg` ancestor probe. The record is scoped to the active home, so the child-spawn path no longer reads the real home -- that probe tripped `tests/home_io_guard.py` when the suite runs from a self-managed install -- and the uv-launcher, first-match-only and ordering edge cases of the old derivation go with it. * `cron/worker_bootstrap.py` is now the PM call the worker actually needs: lease + `site.addsitedir` + `sys.path`/`PYTHONPATH`/`PATH` are all `pm.environments.activate_dependencies`, the same boot `hermes_bootstrap` runs, and it holds the generation's lease for the process's life. It is gated on the `HERMES_CRON_EXTERNAL_WORKER` marker the spawn site sets (consumed on read) rather than sniffing `--external-worker-file` out of `sys.argv`, so the gateway keeps its launch contract without argv identity inference. Verified live on PM's store Python against a real generation (A/B, same child): tree-only PYTHONPATH -> `ModuleNotFoundError: No module named 'ruamel'`, rc=1; pinned env -> imports, `running_from_selected_environment` True, and the worker holds a real kernel lease while it runs (released on exit). The real `-m cron.scheduler --external-worker-file ...` entry now reaches its ownership handoff instead of dying at the import. `tests/cron/`: 1376 passed. Tests: the two invariants that carry the contract -- the pin restores the committed generation (and invents nothing without one), and the boot runs only in the marked worker, once. The six construction tests that drove the old derivation are dropped with it. Duplicate-cluster credit: NousResearch#122238 (@webtecnica, earliest), NousResearch#122382 (@Sakujakira), NousResearch#122685 (@etrisnanto), NousResearch#122719 (@aglowinthefield) and NousResearch#122738 (@GabboAlarcon) diagnosed and fixed this same worker-dependency failure in parallel while this branch was rebuilt. None of their diffs is contained here -- the co-author trailers below record direction and duplicate-cluster credit, not lifted code; their roles are listed in the PR body. Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com> Co-authored-by: Manuel S. <3703103+Sakujakira@users.noreply.github.com> Co-authored-by: Eko Trisnanto <4469382+etrisnanto@users.noreply.github.com> Co-authored-by: aglowinthefield <146008217+aglowinthefield@users.noreply.github.com> Co-authored-by: GabboAlarcon <40816412+GabboAlarcon@users.noreply.github.com> (cherry picked from commit 7c55715)
The restart-safe cron worker is spawned as `sys.executable -m cron.scheduler`, a Hermes entry point that never goes through `hermes_bootstrap`. On a self-managed (PM) install its interpreter is the store Python, which owns no dependencies and whose PYTHONPATH the subprocess sanitizer has just stripped, so the worker died at its first dependency import (`No module named 'ruamel'`, #122222) before it could publish its ownership acknowledgement -- every scheduled job landed `last_status: failed`. Salvaged from #122290 (thanks @123456hyu), whose two commits pinned the dependency site-packages and leased the generation at the worker entry. Reworked onto PM's own machinery: * `pin_hermes_tree_on_pythonpath` asks `pm.environments.committed_venv` + `site_packages()` for the generation, instead of deriving one from this process's `sys.path` behind a `pyvenv.cfg` ancestor probe. The record is scoped to the active home, so the child-spawn path no longer reads the real home -- that probe tripped `tests/home_io_guard.py` when the suite runs from a self-managed install -- and the uv-launcher, first-match-only and ordering edge cases of the old derivation go with it. * `cron/worker_bootstrap.py` is now the PM call the worker actually needs: lease + `site.addsitedir` + `sys.path`/`PYTHONPATH`/`PATH` are all `pm.environments.activate_dependencies`, the same boot `hermes_bootstrap` runs, and it holds the generation's lease for the process's life. It is gated on the `HERMES_CRON_EXTERNAL_WORKER` marker the spawn site sets (consumed on read) rather than sniffing `--external-worker-file` out of `sys.argv`, so the gateway keeps its launch contract without argv identity inference. Verified live on PM's store Python against a real generation (A/B, same child): tree-only PYTHONPATH -> `ModuleNotFoundError: No module named 'ruamel'`, rc=1; pinned env -> imports, `running_from_selected_environment` True, and the worker holds a real kernel lease while it runs (released on exit). The real `-m cron.scheduler --external-worker-file ...` entry now reaches its ownership handoff instead of dying at the import. `tests/cron/`: 1376 passed. Tests: the two invariants that carry the contract -- the pin restores the committed generation (and invents nothing without one), and the boot runs only in the marked worker, once. The six construction tests that drove the old derivation are dropped with it. Duplicate-cluster credit: #122238 (@webtecnica, earliest), #122382 (@Sakujakira), #122685 (@etrisnanto), #122719 (@aglowinthefield) and #122738 (@GabboAlarcon) diagnosed and fixed this same worker-dependency failure in parallel while this branch was rebuilt. None of their diffs is contained here -- the co-author trailers below record direction and duplicate-cluster credit, not lifted code; their roles are listed in the PR body. Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com> Co-authored-by: Manuel S. <3703103+Sakujakira@users.noreply.github.com> Co-authored-by: Eko Trisnanto <4469382+etrisnanto@users.noreply.github.com> Co-authored-by: aglowinthefield <146008217+aglowinthefield@users.noreply.github.com> Co-authored-by: GabboAlarcon <40816412+GabboAlarcon@users.noreply.github.com> (cherry picked from commit 7c55715)
The restart-safe cron worker is spawned as `sys.executable -m cron.scheduler`, a Hermes entry point that never goes through `hermes_bootstrap`. On a self-managed (PM) install its interpreter is the store Python, which owns no dependencies and whose PYTHONPATH the subprocess sanitizer has just stripped, so the worker died at its first dependency import (`No module named 'ruamel'`, NousResearch#122222) before it could publish its ownership acknowledgement -- every scheduled job landed `last_status: failed`. Salvaged from NousResearch#122290 (thanks @123456hyu), whose two commits pinned the dependency site-packages and leased the generation at the worker entry. Reworked onto PM's own machinery: * `pin_hermes_tree_on_pythonpath` asks `pm.environments.committed_venv` + `site_packages()` for the generation, instead of deriving one from this process's `sys.path` behind a `pyvenv.cfg` ancestor probe. The record is scoped to the active home, so the child-spawn path no longer reads the real home -- that probe tripped `tests/home_io_guard.py` when the suite runs from a self-managed install -- and the uv-launcher, first-match-only and ordering edge cases of the old derivation go with it. * `cron/worker_bootstrap.py` is now the PM call the worker actually needs: lease + `site.addsitedir` + `sys.path`/`PYTHONPATH`/`PATH` are all `pm.environments.activate_dependencies`, the same boot `hermes_bootstrap` runs, and it holds the generation's lease for the process's life. It is gated on the `HERMES_CRON_EXTERNAL_WORKER` marker the spawn site sets (consumed on read) rather than sniffing `--external-worker-file` out of `sys.argv`, so the gateway keeps its launch contract without argv identity inference. Verified live on PM's store Python against a real generation (A/B, same child): tree-only PYTHONPATH -> `ModuleNotFoundError: No module named 'ruamel'`, rc=1; pinned env -> imports, `running_from_selected_environment` True, and the worker holds a real kernel lease while it runs (released on exit). The real `-m cron.scheduler --external-worker-file ...` entry now reaches its ownership handoff instead of dying at the import. `tests/cron/`: 1376 passed. Tests: the two invariants that carry the contract -- the pin restores the committed generation (and invents nothing without one), and the boot runs only in the marked worker, once. The six construction tests that drove the old derivation are dropped with it. Duplicate-cluster credit: NousResearch#122238 (@webtecnica, earliest), NousResearch#122382 (@Sakujakira), NousResearch#122685 (@etrisnanto), NousResearch#122719 (@aglowinthefield) and NousResearch#122738 (@GabboAlarcon) diagnosed and fixed this same worker-dependency failure in parallel while this branch was rebuilt. None of their diffs is contained here -- the co-author trailers below record direction and duplicate-cluster credit, not lifted code; their roles are listed in the PR body. Co-authored-by: webtecnica <75556242+webtecnica@users.noreply.github.com> Co-authored-by: Manuel S. <3703103+Sakujakira@users.noreply.github.com> Co-authored-by: Eko Trisnanto <4469382+etrisnanto@users.noreply.github.com> Co-authored-by: aglowinthefield <146008217+aglowinthefield@users.noreply.github.com> Co-authored-by: GabboAlarcon <40816412+GabboAlarcon@users.noreply.github.com> (cherry picked from commit 7c55715)
Summary
The restart-safe external cron worker (
python -m cron.scheduler) dies before its ownership acknowledgement withModuleNotFoundError: No module named 'ruamel', which fails every dispatched cron job on a managed-systemd gateway install.Root cause:
ruamel.yamletc.) never reaches itssys.path.pin_hermes_tree_on_pythonpathonly re-pinned the repo root.Observed failure:
Restart-safe cron worker dispatch failed: cron external worker exited before ownership acknowledgement (exit 1)with the traceback ending inhermes_yaml.py:10 -> from ruamel.yaml import YAML.Fix
cron/scheduler_worker_env.py:pin_hermes_tree_on_pythonpathnow also prepends the committed dependency environment's site-packages (pm.environments.selected_venv+site_packages) alongside the repo root, so the worker's import chain resolves. Resolved lazily at spawn time in the gateway process (which itself runs on the committed environment), with a try/except fallback to the previous behavior for installs without a committed environment.Test plan
import hermes_yamlwith exactly the observed traceback.hermes_bootstrap, cron.schedulersuccessfully.cronjob runof a real job now completes with statusok(waserrorbefore).