fix(update): self-heal venv after failed lazy backend refresh (salvage #58004) - #69867
Merged
Conversation
Upgrade pip before lazy refreshes, probe core imports when a lazy install fails, force-reinstall corrupted packages with pyproject pins, use package-only install (no shim quarantine) for repair, and keep the .update-incomplete marker until refresh/repair succeeds (#57828).
Add repair/probe/quarantine regression tests and update autostash mocks for the new lazy-refresh signature.
Keep .update-incomplete for full .[all] recovery only. Lazy refresh uses .lazy-refresh-incomplete and clears only after confirmed import probes; unavailable probes are indeterminate, not healthy (#58004 review).
The hermes console entry point is hermes_cli.main:main, and main.py imports dotenv (via env_loader) and yaml (via config) at module level. In the #57828 failure state — a failed lazy backend refresh wiping a core package's import files while metadata survives — a normal launch crashed while importing main.py, before _recover_from_interrupted_install() and the recovery markers from PR #58004 could act. - hermes_cli/_early_recovery.py: stdlib-only bootstrap repair invoked at the very top of main.py, before any third-party import. Probes the fragile core packages via real imports, force-reinstalls broken ones using the pyproject.toml pins, shares main.py's single-flight recovery lock, and never clears markers (the confirmed lifecycle stays with the full recovery path in main.py). - Probe/repair tables now have one canonical home in _early_recovery, reused by main.py so the two layers cannot drift. - Manual --force-reinstall fallback commands now print pinned specs via _lazy_refresh_repair_specs() instead of bare package names. - tests: entry-point lifecycle coverage proving a broken dotenv import crashes main.py without repair and imports cleanly with it, a stdlib-only import guard for _early_recovery, and unit coverage for marker gating, lock single-flight, pinned specs, and marker preservation.
Contributor
૮ >ﻌ< ა ci reviewran on 2d248ac all good! |
This was referenced Jul 23, 2026
23 tasks
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.
Summary
hermes updatenow self-heals a venv corrupted by a failed lazy backend refresh — including the worst case where the corruption preventshermesitself from launching. Salvages PR #58004 by @HexLab98 onto current main and closes the final review blocker: recovery previously lived insidehermes_cli.main, whose own module-level imports (dotenvviaenv_loader,yamlviaconfig) crash first in exactly the failure state the recovery exists for (#57828).Changes
Cherry-picked from #58004 (@HexLab98, authorship preserved):
uv pip install, probe 7 core packages via real imports (metadata can survive while.pyfiles are wiped) and--force-reinstallbroken ones with pyproject pins.update-incomplete(interrupted core.[all]install → full quarantined reinstall) split from.lazy-refresh-incomplete(refresh phase → package-only import repair); narrow probes can never clear the core markerhermes.exelaunchers survive pip upgradeshermes.exe-ancestor launches get package-only first aid, with the core marker retained until the full reinstall succeedsFollow-up (ours):
hermes_cli/_early_recovery.py— stdlib-only bootstrap repair invoked at the very top ofmain.py, before any third-party import. Probes the fragile core packages in-process, force-reinstalls broken ones with pyproject pins, shares the single-flight recovery lock, and never clears markers (the confirmed lifecycle stays with_recover_from_interrupted_install(), which runs right after import succeeds)_early_recovery, re-exported bymain.pyso the two layers cannot drift--force-reinstallfallback commands now print pinned specs via_lazy_refresh_repair_specs()instead of bare namesValidation
hermescrashes importingmain.py; markers unreachable; every update failsscripts/run_tests.sh— 78/78 acrosstest_early_recovery.py(13, new),test_lazy_refresh_venv_repair.py(15),test_update_interrupted_recovery.py(16),test_update_autostash.py(34)dotenvcrashes thehermes_cli.mainimport without repair and imports cleanly with it; a stdlib-only import guard proves_early_recoveryloads on a corrupted venvdotenvimport files deleted with metadata intact, marker present → early recovery detected + force-reinstalledpython-dotenv==1.2.2, import restored, marker preserved for the full recovery passCloses #58004 (salvaged, @HexLab98's commits cherry-picked with authorship preserved). Closes #58246 (superseded — its metadata-only verifier misses the metadata-survives/imports-gone case this PR's import probes catch; thanks @tianma-if). Fixes #57828.
Infographic