Skip to content

fix(update): a contended Windows venv is never mutated — failed shim quarantine refuses instead of warning (#87331) - #92617

Merged
teknium1 merged 3 commits into
mainfrom
hermes/hermes-83bfdb1e
Aug 23, 2026
Merged

teknium1 merged 3 commits into
mainfrom
hermes/hermes-83bfdb1e

Conversation

@teknium1

Copy link
Copy Markdown
Collaborator

Summary

A Windows update can no longer mutate a venv it failed to quarantine — the "warn and install anyway" path that stranded installs half-updated is now a clean refusal that defers to the next launch (#87331's remaining half; the ZIP-fallback destruction legs died in #92013).

Root cause: when hermes.exe/sibling shims could not be renamed aside (a process holds them without FILE_SHARE_DELETE), _quarantine_running_hermes_exe printed a warning and the installer ran anyway — then died partway on the same locks, leaving the venv between versions (3 field occurrences on one machine).

Changes

  • hermes_cli/main.py: _run_quarantined_install(strict_quarantine=True) — a shim whose rename failed every retry raises ShimQuarantineError BEFORE the install command runs, with successful renames rolled back. The update dependency sync (_install_python_dependencies_with_optional_fallback) is strict; post-sync repair installs keep warn-and-try (their venv is already mutated — refusing buys nothing).
  • hermes_cli/update_cmd.py: boundary handling — the error becomes a refusal: update-incomplete marker written, exit 2 (receipt net records refused), explicit no-ZIP-fallback. Covers both the git path and the sync inside the ZIP handler.
  • hermes_cli/_install_repair.py: the marker-recovery installer (_run_install_cmd) is strict unconditionally — marker survives, next launch retries after the holder exits.
  • Tests: 9 unit tests (strict refusal, rollback, non-strict preserved, strict wiring, boundary marker+exit-2, no-ZIP classification) + 4 live Windows E2E tests.

Validation

Check Result
New unit tests + existing quarantine suites (noop-restore, concurrent, console-scripts) 28/28
Sabotage run (strict wiring reverted) both fail-closed tests FAIL — tests prove the fix
Live windows-latest (wine2e, run 32610954959): real child holds hermes.exe with no FILE_SHARE_DELETE — the exact field lock shape rename really blocked (premise test) · strict path refused with 0 installer runs · sibling renames rolled back · recovery installer refused · after holder exit the same path installs — 12/12

Infographic

Fail-closed venv updates

…ne now refuses instead of warning (#87331)

The #87331 remaining half: when hermes.exe (or a sibling shim) could not
be renamed aside, the updater printed a warning and ran the installer
anyway — which died partway on the same locks and stranded the venv
between versions.

- _run_quarantined_install gains strict_quarantine: any shim whose
  rename failed every retry aborts BEFORE the install command runs
  (successful renames rolled back), raising ShimQuarantineError.
- The update dependency sync passes strict_quarantine=True. The update
  boundary turns the error into a refusal: defer via the
  update-incomplete marker, exit 2 (recorded as refused by the receipt
  net), never ZIP-fallback. Post-sync repair installs keep warn-and-try
  (their venv is already mutated; refusing buys nothing).
- The recovery installer (_install_repair._run_install_cmd) is strict
  unconditionally: marker survives, next launch retries after the
  holder exits.
- Live Windows E2E for the wine2e lane: a real child holds hermes.exe
  without FILE_SHARE_DELETE (the exact field lock shape), strict path
  refuses with zero installer invocations, releases roll back, and the
  same path proceeds once the holder exits.

Sabotage-verified: reverting the strict wiring makes both fail-closed
tests fail.
@teknium1
teknium1 force-pushed the hermes/hermes-83bfdb1e branch from 6f531d5 to 2096868 Compare August 23, 2026 01:44
@github-actions

github-actions Bot commented Aug 23, 2026 •

Copy link
Copy Markdown

૮ >ﻌ< ა ci review

ran on a5d46a2 — test(bedrock): make the botocore stub windows airtight — kil

⚠️ Warnings

CI timings · View report · View job

Wall time 4m53s vs 2m42s (+80.9%). 8 job(s) slower, 2 faster, 2 unchanged.

  • OS-specific tests / macOS-only tests: -8.0s
  • Python tests / Run tests: +6.0s
  • OSV scan / Emit review status: +6.0s
  • Check contributors / check-attribution: +6.0s
  • Python tests / e2e: -5.0s

OSV vulnerability scan · View job

7 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.

@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard platform/windows Native Windows-specific behavior or breakage area/install-update Installer, updater, packaging, wheels, doctor P2 Medium — degraded but workaround exists sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 23, 2026
…ndored-import flake

CI flake mechanism (PR #92617 red, reproduced standalone): tests plant
fake botocore modules via patch.dict; when the REAL botocore.exceptions
is first imported in an interpreter state where a fake parent is (or
was) installed, its 'from botocore.vendored import requests' resolves
against a module with no __path__ and every exception test in the worker
dies with "No module named 'botocore.vendored'" — ordering-dependent,
so green locally, red in CI workers.

Defenses (both, in depth):
- test_bedrock_adapter.py pre-imports the real botocore.exceptions at
  module scope, before any test can stub sys.modules — later imports are
  cache hits that can never re-execute the vendored import under a
  poisoned parent. Proven standalone: fake-parent repro fails without
  the pre-import, succeeds with it.
- autouse _boto_sys_modules_hygiene fixtures in all three files that
  plant fake boto* modules (adapter, integration, model-picker):
  snapshot every boto* sys.modules entry before each test, evict+restore
  after — no stub window can leak state into a later test regardless of
  worker ordering.
- importorskip targets botocore.exceptions (the module the tests
  actually need) instead of bare botocore, so a torn install skips
  instead of erroring.

148/148 across the four affected suites.
@teknium1

Copy link
Copy Markdown
Collaborator Author

Also on this branch (it redded this PR's first CI run): the bedrock test flake is now fixed at the class level — commit a5d46a2. Mechanism reproduced standalone: tests plant fake botocore modules; the real botocore.exceptions first-imported under a poisoned parent dies with No module named 'botocore.vendored', ordering-dependent per CI worker. Defenses: module-scope pre-import of the real exceptions (cache-hit immunity), autouse boto* sys.modules snapshot/restore fixtures in all three stub-planting files, and importorskip targeting botocore.exceptions. 148/148 across the four affected suites.

@teknium1
teknium1 merged commit fd76043 into main Aug 23, 2026
35 checks passed
@teknium1
teknium1 deleted the hermes/hermes-83bfdb1e branch August 23, 2026 02:30
teknium1 added a commit that referenced this pull request Aug 23, 2026
Sibling-test blast radius from #92617: the salvaged fixture's fake
_run_quarantined_install predates the strict_quarantine kwarg the
update sync now passes.
teknium1 added a commit that referenced this pull request Aug 23, 2026
Sibling-test blast radius from #92617: the salvaged fixture's fake
_run_quarantined_install predates the strict_quarantine kwarg the
update sync now passes.
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
…ndored-import flake

CI flake mechanism (PR NousResearch#92617 red, reproduced standalone): tests plant
fake botocore modules via patch.dict; when the REAL botocore.exceptions
is first imported in an interpreter state where a fake parent is (or
was) installed, its 'from botocore.vendored import requests' resolves
against a module with no __path__ and every exception test in the worker
dies with "No module named 'botocore.vendored'" — ordering-dependent,
so green locally, red in CI workers.

Defenses (both, in depth):
- test_bedrock_adapter.py pre-imports the real botocore.exceptions at
  module scope, before any test can stub sys.modules — later imports are
  cache hits that can never re-execute the vendored import under a
  poisoned parent. Proven standalone: fake-parent repro fails without
  the pre-import, succeeds with it.
- autouse _boto_sys_modules_hygiene fixtures in all three files that
  plant fake boto* modules (adapter, integration, model-picker):
  snapshot every boto* sys.modules entry before each test, evict+restore
  after — no stub window can leak state into a later test regardless of
  worker ordering.
- importorskip targets botocore.exceptions (the module the tests
  actually need) instead of bare botocore, so a torn install skips
  instead of erroring.

148/148 across the four affected suites.
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
Sibling-test blast radius from NousResearch#92617: the salvaged fixture's fake
_run_quarantined_install predates the strict_quarantine kwarg the
update sync now passes.
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 platform/windows Native Windows-specific behavior or breakage sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants