Skip to content

fix(update): reconcile venv with uv.lock so uv run hermes stops re-resolving (#8744) - #125790

Open
Finn763 wants to merge 1 commit into
NousResearch:mainfrom
Finn763:fix/8744-uv-run-frozen
Open

Finn763 wants to merge 1 commit into
NousResearch:mainfrom
Finn763:fix/8744-uv-run-frozen

Conversation

@Finn763

@Finn763 Finn763 commented Sep 27, 2026

Copy link
Copy Markdown

Closes #8744

hermes update finished with uv pip install -e .[all], which writes
the package but does not enforce lockfile pinning. The next uv run hermes was free to re-validate against uv.lock, triggering a network
round-trip for git-pinned extras (tinker, yc-bench, atropos) and
an offline launch that died with Could not resolve host: github.com.

After every successful editable install, run
uv sync --extra all --locked so the venv is provably aligned with
the lockfile state. A following uv run hermes then validates against
an already-synced venv with no network calls. The sync is silent when
uv.lock is missing (ZIP-swap / bare checkout — uv would refuse
--locked) or when uv is not the install tool.

Fix lives in _install_python_dependencies_with_optional_fallback, the
shared function every hermes update install path routes through — one
guard, not six.

Tests:

  • New tests/hermes_cli/test_update_uv_lock_sync.py — 3 cases: sync
    runs after install with the right flags, sync targets PROJECT_ROOT and
    VIRTUAL_ENV, no sync when uv.lock is missing.
  • Adjacent (50 tests across test_update_stale_virtualenv,
    test_update_shim_fail_closed, test_update_shim_self_lock,
    test_lazy_refresh_venv_repair, test_cmd_update) all pass.
  • Mutation-verified: reverting the helper restores 2/3 RED.

Notes:

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard area/install-update Installer, updater, packaging, wheels, doctor python:uv Pull requests that update python:uv code sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Sep 27, 2026
@Enough1122

Copy link
Copy Markdown

AI code review — automated review for reference; please use your judgment.

uv sync here targets a different environment than the venv hermes just installed into, so the venv this PR means to reconcile is never reconciled.

_install_python_dependencies_with_optional_fallback runs uv pip install -e .[all] with env["VIRTUAL_ENV"] = project_venv_dir(PROJECT_ROOT), and hermes_constants.project_venv_dir documents that layout: "uv venv defaults to .venv while our installers create venv ... venv wins when both exist". uv pip honors VIRTUAL_ENV; uv sync does not. Without --active / UV_PROJECT_ENVIRONMENT it syncs the project environment <PROJECT_ROOT>/.venv and explicitly ignores a VIRTUAL_ENV that doesn't match.

Measured with the repo's own uv 0.12.3 in a throwaway project (venv/ a real venv, extras all):

step command venv/ (what hermes uses) .venv/
1 uv pip install -e .[all] (VIRTUAL_ENV=venv) editable + deps absent
2 uv pip install idna + idna absent
3 uv sync --extra all --locked, cwd=PROJECT_ROOT unchanged (idna still there) created by this call

Step 3 prints warning: VIRTUAL_ENV=venv does not match the project environment path `.venv` and will be ignored, Creating virtual environment at: .venv, exit 0.

So the uv pip install writes venv/ and the sync writes .venv/: the venv hermes runs from keeps the drift this PR exists to remove, while the lock gets pinned into an environment nothing executes. uv run then resolves that same .venv, so the "already-synced venv" is a second environment — user-installed plugins in venv/ are invisible to it, and anything in it that isn't in the lock is pruned.

The propagated VIRTUAL_ENV is precisely what uv ignores. The new test tests/hermes_cli/test_update_uv_lock_sync.py:139 asserts env.get("VIRTUAL_ENV") == venv but never that the sync targeted that venv, so it passes on the wrong-environment path. Fix: pass --active, or set UV_PROJECT_ENVIRONMENT to project_venv_dir(PROJECT_ROOT) in the sync's env.

Also: group is in scope at hermes_cli/main.py:10156 but the command hardcodes --extra all, so a termux-all caller syncs the full extra set (measured: uninstalls typing-extensions, a curated termux dep). And check=False never inspects the returncode, so a stale lock exits 1 silently, venv unchanged.

…solving (NousResearch#8744)

hermes update finished with uv pip install -e .[all], which writes the
package but does not enforce lockfile pinning. The next uv run hermes
was free to re-validate against uv.lock, triggering a network round-trip
for git-pinned extras (tinker, yc-bench, atropos) and an offline launch
that died with 'Could not resolve host: github.com'.

After every successful editable install, run
'uv sync --extra all --locked' so the venv is provably aligned with the
lockfile state. A following uv run hermes then validates against an
already-synced venv with no network calls. Falls back silently when
uv.lock is missing (ZIP-swap / bare checkout — uv would refuse --locked)
or when uv is not the install tool.
@Finn763
Finn763 force-pushed the fix/8744-uv-run-frozen branch from ecdbe3a to a50d30d Compare October 1, 2026 06:53
@Finn763

Finn763 commented Oct 1, 2026

Copy link
Copy Markdown
Author

Fixed in a50d30d — all three points, plus the missing test pin.

1. The sync now targets the venv hermes runs from. You're right that the propagated VIRTUAL_ENV is exactly what uv ignores. Confirmed locally with uv 0.9.28 in a throwaway project (venv/ a real venv):

$ VIRTUAL_ENV=<proj>/venv uv sync --extra all --locked
warning: `VIRTUAL_ENV=<proj>/venv` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
Creating virtual environment at: .venv

so the pre-fix uv pip install wrote venv/ and the sync wrote .venv/. The sync env now sets UV_PROJECT_ENVIRONMENT (and a matching VIRTUAL_ENV) to the resolved project venv — the same lever hermes_cli/managed_uv.py:891 already uses for its locked sync, so this follows in-repo precedent rather than inventing a new one. (--active also works — verified — but it errors out when no venv is active, and UV_PROJECT_ENVIRONMENT doesn't.)

Related edge the same change closes: when the caller has no project venv to point at (pip / site-packages install, where the helper pins sys.executable and drops VIRTUAL_ENV), the sync is now skipped outright instead of conjuring a fresh PROJECT_ROOT/.venv that nothing runs.

2. --extra follows the caller. Primary path syncs the requested group; the individual-extras fallback syncs only the extras that actually landed (installed_extras). A termux-all caller no longer gets the full all set synced over its curated profile, and the fallback can't drag back extras it deliberately skipped.

3. Returncode is inspected. Non-zero now logs post-install uv sync failed (rc=N); venv left as-is instead of exiting 1 silently — stdout/stderr stay attached to the console, no capture.

Tests (tests/hermes_cli/test_update_uv_lock_sync.py, 5 passed): the old test_lockfile_sync_targets_project_root_and_venv asserted VIRTUAL_ENV propagation only, which passes on the wrong-environment path — exactly your read. It's now test_lockfile_sync_targets_the_venv_hermes_runs_from and asserts UV_PROJECT_ENVIRONMENT == <project>/venv. Mutation-checked: deleting that one assignment in main.py makes it fail. Added test_lockfile_sync_uses_the_callers_extra_group (--extra termux-all, not all) and test_lockfile_sync_skipped_when_no_project_venv_exists (no sync call at all).

Local test_managed_uv.py has 7 pre-existing failures on this box (uv version / venv-layout expectations) — identical set on the unmodified tree, unrelated to this change.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/install-update Installer, updater, packaging, wheels, doctor comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists python:uv Pull requests that update python:uv code sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] uv run hermes resolves dependencies on every launch — requires internet post-update

3 participants