diff --git a/hermes_cli/_scan_venv_blockers.py b/hermes_cli/_scan_venv_blockers.py index 8a0fe153c6..e25a465567 100644 --- a/hermes_cli/_scan_venv_blockers.py +++ b/hermes_cli/_scan_venv_blockers.py @@ -28,14 +28,28 @@ _SENSITIVE_LONG_FLAGS: list[str] = [ ] -def _probe_fail_json() -> str: - """Return the standard probe-failure JSON document.""" - return json.dumps({"ok": False, "blocked": False, "processes": []}) +def _probe_fail_json(diagnostic: str = "probe failed") -> str: + """Return the standard probe-failure JSON document. + + ``ok: false`` plus ``probe_failed: true`` means the detector itself could + not run — this is *not* a clear scan. Callers must treat + ``ok is not True`` / non-zero exit as probe failure, never as + ``blocked: false`` "clear" (#83149). + """ + return json.dumps( + { + "ok": False, + "probe_failed": True, + "blocked": False, + "processes": [], + "error": diagnostic, + } + ) def _emit_probe_fail(diagnostic: str) -> NoReturn: """Print one JSON to stdout, diagnostic to stderr, exit non-zero.""" - print(_probe_fail_json()) + print(_probe_fail_json(diagnostic)) print(diagnostic, file=sys.stderr) sys.exit(1) diff --git a/scripts/install.ps1 b/scripts/install.ps1 index ac78ee3071..00dd9c6577 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -2460,15 +2460,23 @@ function Install-Venv { } # Move the old venv aside before creating its replacement. A directory # rename is atomic on the same volume and does not require deleting - # files mapped as DLLs. Never fall back to deleting the live venv: - # Windows can remove unlocked files first, then fail on one locked - # file, leaving no usable interpreter and no rollback source. + # files mapped as DLLs. NEVER fall back to deleting the live venv + # (#83149): Remove-Item -Recurse can delete most of site-packages and + # then fail on one locked .pyd, leaving a gutted venv with no usable + # interpreter and no rollback source. Abort with the previous install + # intact so the user can close holders and retry. $venvBackupName = "venv.stale.{0}-{1}" -f (Get-Date -Format "yyyyMMddHHmmss"), ([Guid]::NewGuid().ToString("N")) try { Rename-Item -LiteralPath "venv" -NewName $venvBackupName -ErrorAction Stop $venvParked = $true } catch { - throw "Could not move existing venv aside without deleting it. Close running Hermes processes and retry. $($_.Exception.Message)" + $renameErr = $_.Exception.Message + throw ( + "Could not move the existing venv aside ($renameErr). " + + "A process still has the install directory open (often a non-Hermes " + + "python.exe that resolved into this venv via PATH). Close those " + + "processes and retry - the previous install was left intact." + ) } }