Repository navigation
Conversation
…s venv
A PM install keeps one dependency generation per install (a store interpreter plus
its own environment) while the checkout may still hold the legacy pre-PM `venv/`,
built by uv for a different CPython. `_ensure_windows_gateway_venv_imports` walked
`VIRTUAL_ENV` then `<project_root>/venv` and inserted whichever site-packages it
found at sys.path[0:1] — ahead of the generation the process booted from — so on a
cp314 store interpreter `pydantic` resolved from the cp311 tree and its compiled
core could not load:
ModuleNotFoundError: No module named 'pydantic_core._pydantic_core'
The gateway then logged "Supervised task hosted_room_worker died 5 times in rapid
succession" and hosted rooms stopped working, on every update that moved the store
Python. Reproduced deterministically by importing `hermes_bootstrap`, calling the
helper, then importing `tui_gateway.server` with the store interpreter.
Two changes:
* `_ensure_windows_gateway_venv_imports` now prefers PM's committed environment
(`pm.environments.committed_venv`) and skips any candidate whose recorded Python
does not match the running interpreter, warning when that leaves nothing. A
candidate without `pyvenv.cfg` keeps the historical behavior.
* `pm.environments.venv_python_version` also reads `version_info`, the key
`uv venv` writes for every Hermes-managed environment. Reading only `version`
returned None for all of them, which is what let a mismatched tree look
acceptable in the first place (and dated POSIX site-packages from the caller).
Tests: tests/gateway/test_windows_gateway_venv_injection.py (leftover skipped,
matching venv still injected, marker-less venv unchanged, committed generation
outranks the leftover) and tests/pm/test_venv_python_version.py. Both fail on the
parent commit and pass with the fix.
Right direction: pairing 1. [blocker] On 2. [blocker] 3. [blocker] The new tests run in no CI lane. Relations. #122427 coexists — it never touches |
What fails
On Windows, a PM install (store interpreter + committed dependency generation) that still has the legacy pre-PM
venv/in the checkout kills the gateway's hosted-room worker minutes after boot:pydanticimported fine (pure Python), but its compiled core came from a cp311 tree while the process ran the cp314 store interpreter. Hosted rooms stop working until the leftover tree is removed by hand. Seen on the update that moved the install totools/python-3.14.7whilehermes-agent\venv\(uv, 3.11) stayed behind.Root cause
gateway/run.py::_ensure_windows_gateway_venv_imports()runs before MCP discovery and injects a venv'ssite-packagesatsys.path[0:1]so detached runs see Hermes' packages. Its candidates areVIRTUAL_ENVthen<project_root>/venv, and the legacy in-tree venv is accepted whatever interpreter built it — so its site-packages lands ahead of the generationhermes_bootstrapactivation put onsys.path. Any package that exists in both trees then resolves from the old one, and its compiled extensions refuse to load.The helper meant to catch exactly this cannot see it:
pm.environments.venv_python_version()reads onlypyvenv.cfg'sversionkey, whileuv venv— every Hermes-managed environment — writesversion_info:So it returns
Nonefor all of them (on POSIX it also makessite_packages()date the tree from the caller'ssys.version_info).Reproduction (deterministic)
Store interpreter,
PYTHONPATH=<checkout>(what a detached Windows gateway gets), checkout holding a leftover 3.11venv/:Parent commit:
site-packageslistshermes-agent\venv\Lib\site-packagesfirst and the import raisesModuleNotFoundError: No module named 'pydantic_core._pydantic_core'. With this PR: only the PM generation's tree is onsys.pathand the import succeeds. Live check on the affected machine:hermes gateway restart→ zero newpydantic_core/hosted_room_worker diedentries over six minutes (before: 5 death-restarts, ~3 minutes after every boot).Fix
_ensure_windows_gateway_venv_imports()prefers PM's committed environment (pm.environments.committed_venv) overVIRTUAL_ENV/<checkout>/venv, and skips any candidate whose recorded interpreter version differs from the running one — warning, naming the path, when that leaves nothing. A candidate withoutpyvenv.cfgkeeps the historical behavior, and a legacy install with a matching venv is untouched.pm.environments.venv_python_version()also readsversion_info, so uv-built venvs are identifiable at all.Tests
tests/gateway/test_windows_gateway_venv_injection.py— leftover skipped, matching venv still injected, marker-less venv unchanged, committed generation outranks the leftover.tests/pm/test_venv_python_version.py—version_info, legacyversion, missing/unparsable marker, and (POSIX)site_packages()using the venv's own version.Both files fail on the parent commit (
3 failed, 5 passed) and pass with the fix (8 passed, 1 skipped, Windows).Full
tests/pmon the machine that hit this is unchanged by the diff:60 failed, 625 passed, 88 skipped, 102 errorsbefore and after, with an identical failure/error ID set (pre-existing gaps in that checkout's legacy 3.11 dev venv — node/worker/network-bound tests), so nothing here regresses the suite.