# NousResearch/hermes-agent · Desktop build: electronDist path and binary issues

## Open now

- Nothing open on GitHub.

Multiple bugs and proposed fixes concern the desktop build encountering an unresolvable electronDist path, stemming from Electron binary placement mismatches between workspace-local and repo-root node_modules, unreliable downloads, and installer misdiagnosis of related errors. Members span macOS, Windows, and Linux.

## TL;DR

- 13 member(s): 0 open issue(s), 9 closed issue(s), 0 open PR(s), 3 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #47276. Verify it addresses your case.
- **Recommended:** read PR #47276 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `partially_resolved` · deciding PR(s): `pr-47276,pr-48081,pr-48091`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.11`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #46785: Bootstrap installer misdiagnoses desktop build failures as Electron download problems
- #47266: Windows install fails at desktop stage: electronDist does not exist (Electron download unreliable)
- #47917: \[Bug\] Desktop build fails after update - electronDist does not exist (cache invalidated)
- #48019: Windows desktop pack fails: electronDist points to repo-root node_modules instead of workspace-local Electron
- #48021: Desktop installer: Electron binary not downloaded, build fails on fresh macOS install
- #48059: \[Bug\]: macOS desktop build fails — \`electronDist does not exist\` because the lockfile installs \`electron\` under \`apps/desktop/node_modules\` while electron-builder expects it hoisted to the workspace root
- #48084: bug(desktop): \`electronDist\` path mismatch — electron in workspace but path points to repo root
- #56834: Desktop build fails on Linux with electron error
- #67166: Installer misdiagnoses ENOSPC as a blocked Electron download, then burns more disk retrying

## Prior & competing PRs

- **#47276: fix(desktop): re-download Electron binary via mirror when pack fails (#47266)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `hermes_cli/main.py`, `scripts/install.ps1`, `scripts/install.sh`, `tests/hermes_cli/test_gui_command.py`
  - fix hunks:
  - `hermes_cli/main.py`

    ```diff
    @@ -5110,6 +5110,90 @@ def _purge_electron_build_cache(desktop_dir: Path) -> list[Path]:
         return removed
     
     
    +def _electron_dist_binary(project_root: Path) -> Path:
    +    """Return the path to the Electron main binary inside ``node_modules``.
    +
    +    electron-builder reads the binary from ``build.electronDist``
    +    (``node_modules/electron/dist``) since #38673, so this is the exact file
    +    whose absence makes a pack fail with "The specified electronDist does not
    +    exist". The basename differs per OS (the platform Electron is named for the
    +    host the build runs on).
    +    """
    +    dist = project_root / "node_modules" / "electron" / "dist"
    +    if sys.platform == "darwin":
    +        return dist / "Electron.app" / "Contents" / "MacOS" / "Electron"
    +    if sys.platform == "win32":
    +        return dist / "electron.exe"
    +    return dist / "electron"
    +
    +
    +def _electron_dist_ok(project_root: Path) -> bool:
    +    """True when ``node_modules/electron/dist`` holds a usable Electron binary.
    +
    +    A directory that exists but is missing the binary (a partial extraction from
    +    a corrupt cached zip, or an interrupted postinstall) counts as NOT ok, since
    +    that is exactly the shape that makes electron-builder throw on the pinned
    +    electronDist.
    +    """
    +    try:
    +        return _electron_dist_binary(project_root).exists()
    +    except OSError:
    +        return False
    +
    +
    +def _redownload_electron_dist(
    +    project_root: Path,
    +    env: dict,
    +    *,
    +    mirror: Optional[str] = None,
    +) -> bool:
    +    """(Re)populate ``node_modules/electron/dist`` via electron's own downloader.
    +
    +    Since #38673 the desktop build pins ``build.electronDist`` to
    +    ``node_modules/electron/dist``, so electron-builder reads the Electron binary
    +    straight from there and never downloads it during ``npm run pack``. That dist
    +    tree is produced by the ``electron`` package's postinstall (``install.js``)
    +    during ``npm ci``. When that download is blocked or throttled (GitHub's
    +    release host is unreachable in some regions — #47266), the dist is missing
    +    and re-running ``pack`` only re-throws "The specified electronDist does not
    +    exist". The mirror fallback therefore has to drive *this* downloader, not
    +    another ``pack``.
    +
    +    No-op (returns True) when the dist binary is already present, so an unrelated
    +    build failure doesn't trigger a needless ~200 MB re-download. Otherwise drops
    +    any partial dist + version marker (electron's install.js short-circuits when
    +    ``path.txt`` already matches) and runs the downloader once, optionally via a
    +    mirror. Best-effort: never raises. Returns True iff the dist binary exists
    +    afterward.
    +    """
    +    if _electron_dist_ok(project_root):
    +        return True
    +
    +    electron_dir = project_root / "node_modules" / "electron"
    +    installer = electron_dir / "install.js"
    +    if not installer.is_file():
    +        return False
    +    node = shutil.which("node")
    +    if not node:
    +        return False
    +
    +    dist_dir = electron_dir / "dist"
    +    shutil.rmtree(dist_dir, ignore_errors=True)
    +    try:
    +        (electron_dir / "path.txt").unlink()
    +    except OSError:
    +        pass
    +
    +    dl_env = dict(env)
    +    if mirror:
    +        dl_env["ELECTRON_MIRROR"] = mirror
    +    try:
    +        subprocess.run([node, str(installer)], cwd=str(electron_dir), env=dl_env, check=False)
    +    except OSError:
    +        return False
    +    return _electron_dist_ok(project_root)
    +
    +
     def _stop_desktop_processes_locking_build(desktop_dir: Path) -> list[int]:
         """Terminate any running desktop app executing from this build's ``release``
         dir so a rebuild can replace its (otherwise locked) executable.
    ```

    ```diff
    @@ -5364,8 +5448,18 @@ def cmd_gui(args: argparse.Namespace):
                     # failure was something else, the clean re-download is harmless
                     # and the retry fails the same way.
                     purged = _purge_electron_build_cache(desktop_dir)
    -                if purged:
    -                    print("  ⚠ Desktop build failed; cleared cached Electron download and retrying once...")
    +                # electronDist is pinned to node_modules/electron/dist (#38673):
    +                # electron-builder reads the Electron binary from there and `pack`
    +                # never downloads it, so purging the cache + re-running pack can't
    +                # by itself repopulate a missing/partial dist. When the dist is
    +                # actually gone, re-run electron's own downloader so the retry has
    +                # a binary to read. Gated on the dist check so an unrelated build
    +                # failure (tsc/vite) doesn't trigger a pointless ~200 MB refetch.
    +                restored = False
    +                if not _electron_dist_ok(PROJECT_ROOT):
    +                    restored = _redownload_electron_dist(PROJECT_ROOT, env)
    +                if purged or restored:
    +                    print("  ⚠ Desktop build failed; refreshed the Electron download and retrying once...")
                         for p in purged:
                             print(f"    - {p}")
                         # The purge can't remove a win-unpacked tree whose Hermes.exe
    ```

    ```diff
    @@ -5383,12 +5477,25 @@ def cmd_gui(args: argparse.Namespace):
                     # trade-off we only make AFTER the canonical GitHub download has
                     # failed, and we never override a user-pinned ELECTRON_MIRROR.
                     print("  ⚠ Desktop build still failing; the Electron download from "
    -                      "GitHub looks blocked. Retrying once via a public mirror "
    +                      "GitHub looks blocked. Re-downloading via a public mirror "
                           "(npmmirror.com)... (set ELECTRON_MIRROR to use another mirror)")
    +                mirror = "https://npmmirror.com/mirrors/electron/"
                     mirror_env = dict(env)
    -                mirror_env["ELECTRON_MIRROR"] = "https://npmmirror.com/mirrors/electron/"
    -                _stop_desktop_processes_locking_build(desktop_dir)
    -                build_result = subprocess.run([npm, "run", build_script], cwd=desktop_dir, env=mirror_env, check=False)
    +                mirror_env["ELECTRON_MIRROR"] = mirror
    +                # electronDist is pinned (#38673), so `npm run pack` never
    +                # downloads Electron — the mirror only helps if it drives
    +                # electron's own downloader. Re-fetch the binary through the
    +                # mirror first; otherwise the retry just re-reads the same missing
    +                # dist and re-throws "electronDist does not exist" (#47266).
    +                have_dist = _electron_dist_ok(PROJECT_ROOT)
    +                if not have_dist:
    +                    have_dist = _redownload_electron_dist(PROJECT_ROOT, env, mirror=mirror)
    +                if have_dist:
    +                    _stop_desktop_processes_locking_build(desktop_dir)
    +                    build_result = subprocess.run([npm, "run", build_script], cwd=desktop_dir, env=mirror_env, check=False)
    +                else:
    +                    print("  ✗ Could not re-download Electron from the mirror "
    +                          "(node_modules/electron/dist still missing)")
                 if build_result.returncode != 0:
                     print("✗ Desktop GUI build failed")
                     print(f"  Run manually:  cd apps/desktop && npm run {build_script}")
    ```
  - `scripts/install.ps1`

    ```diff
    @@ -2161,6 +2161,66 @@ function Clear-ElectronBuildCache {
         return $removed
     }
     
    +# True when node_modules\electron\dist holds a usable Electron binary.
    +# electron-builder reads the binary from build.electronDist
    +# (node_modules\electron\dist) since #38673, so this is the exact file whose
    +# absence makes a pack fail with "The specified electronDist does not exist". A
    +# dist dir that exists but is missing electron.exe (partial extraction / aborted
    +# postinstall) is NOT ok.
    +function Test-ElectronDist {
    +    param([string]$InstallDir)
    +    $distExe = Join-Path $InstallDir 'node_modules\electron\dist\electron.exe'
    +    return (Test-Path -LiteralPath $distExe)
    +}
    +
    +# (Re)populate node_modules\electron\dist via electron's own downloader.
    +#
    +# Since #38673 the desktop build pins build.electronDist to
    +# node_modules\electron\dist, so electron-builder reads the Electron binary
    +# straight from there and never downloads it during `npm run pack`. That dist
    +# tree is produced by the electron package's postinstall (install.js) during
    +# `npm ci`. When that download is blocked/throttled (GitHub's release host is
    +# unreachable in some regions - #47266), dist is missing and re-running pack only
    +# re-throws "The specified electronDist does not exist". The mirror fallback
    +# therefore has to drive THIS downloader, not another pack.
    +#
    +# No-op (returns $true) when the dist binary is already present. Otherwise drops a
    +# partial dist + version marker (electron's install.js short-circuits when
    +# path.txt already matches) and runs the downloader once, optionally via a
    +# mirror. Best-effort: never throws. Returns $true iff the dist binary exists
    +# afterward.
    +function Restore-ElectronDist {
    +    param([string]$InstallDir, [string]$Mirror)
    +    if (Test-ElectronDist -InstallDir $InstallDir) { return $true }
    +
    +    $electronDir = Join-Path $InstallDir 'node_modules\electron'
    +    $distExe = Join-Path $electronDir 'dist\electron.exe'
    +    $installer = Join-Path $electronDir 'install.js'
    +    if (-not (Test-Path -LiteralPath $installer)) { return $false }
    +    $node = Get-Command node -ErrorAction SilentlyContinue
    +    if (-not $node) { return $false }
    +
    +    $distDir = Join-Path $electronDir 'dist'
    +    if (Test-Path -LiteralPath $distDir) {
    +        Remove-Item -LiteralPath $distDir -Recurse -Force -ErrorAction SilentlyContinue
    +    }
    +    Remove-Item -LiteralPath (Join-Path $electronDir 'path.txt') -Force -ErrorAction SilentlyContinue
    +
    +    $prevMirror = $env:ELECTRON_MIRROR
    +    if ($Mirror) { $env:ELECTRON_MIRROR = $Mirror }
    +    try {
    +        # Out-Host so the downloader's progress shows on the console WITHOUT
    +        # leaking into this function's return value (PowerShell returns every
    +        # object left on the output stream, so a bare pipe here would make the
    +        # boolean below ambiguous).
    +        & $node.Source $installer 2>&1 | ForEach-Object { "$_" } | Out-Host
    +    } catch {
    +    } finally {
    +        $env:ELECTRON_MIRROR = $prevMirror
    +    }
    +    return (Test-Path -LiteralPath $distExe)
    +}
    +
     function Install-Desktop {
         # Build apps/desktop into a launchable Hermes.exe. Only called from
         # Stage-Desktop, which is itself only included in the manifest when
    ```

    ```diff
    @@ -2310,8 +2370,19 @@ function Install-Desktop {
                 # once; @electron/get re-downloads with its own SHASUM check. Without
                 # this a corrupt download hard-fails the whole installer.
                 $purged = @(Clear-ElectronBuildCache -DesktopDir $desktopDir)
    -            if ($purged.Count -gt 0) {
    -                Write-Warn "Desktop build failed - cleared cached Electron download, retrying once:"
    +            # electronDist is pinned to node_modules\electron\dist (#38673):
    +            # electron-builder reads the Electron binary from there and `pack`
    +            # never downloads it, so purging the cache + re-running pack can't by
    +            # itself repopulate a missing/partial dist. When the dist is actually
    +            # gone, re-run electron's own downloader so the retry has a binary to
    +            # read. Gated on the dist check so an unrelated build failure
    +            # (tsc/vite) doesn't trigger a pointless ~200MB refetch.
    +            $restored = $false
    +            if (-not (Test-ElectronDist -InstallDir $InstallDir)) {
    +                $restored = Restore-ElectronDist -InstallDir $InstallDir
    +            }
    +            if ($purged.Count -gt 0 -or $restored) {
    +                Write-Warn "Desktop build failed - refreshed the Electron download, retrying once:"
                     foreach ($p in $purged) { Write-Info "  - $p" }
                     & $npmExe run pack 2>&1 | ForEach-Object { "$_" } | Tee-Object -FilePath $buildLog
                     $code = $LASTEXITCODE
    ```

    ```diff
    @@ -2326,14 +2397,23 @@ function Install-Desktop {
             # trade-off we only make AFTER the canonical GitHub download has failed,
             # and we never override a user-pinned ELECTRON_MIRROR.
             if ($code -ne 0 -and -not $env:ELECTRON_MIRROR) {
    -            $prevMirror = $env:ELECTRON_MIRROR
    -            $env:ELECTRON_MIRROR = "https://npmmirror.com/mirrors/electron/"
    +            $mirror = "https://npmmirror.com/mirrors/electron/"
                 Write-Warn "Desktop build still failing - the Electron download from GitHub looks blocked."
    -            Write-Warn "Retrying once via a public Electron mirror ($($env:ELECTRON_MIRROR)):"
    +            Write-Warn "Re-downloading Electron via a public mirror ($mirror), then rebuilding:"
                 Write-Info "  (set ELECTRON_MIRROR yourself to use a different/trusted mirror)"
    -            & $npmExe run pack 2>&1 | ForEach-Object { "$_" } | Tee-Object -FilePath $buildLog
    -            $code = $LASTEXITCODE
    -            $env:ELECTRON_MIRROR = $prevMirror
    +            # electronDist is pinned (#38673), so `npm run pack` never downloads
    +            # Electron - the mirror only helps if it drives electron's own
    +            # downloader. Re-fetch the binary through the mirror first; otherwise
    +            # the retry just re-reads the same missing dist and re-throws
    +            # "The specified electronDist does not exist" (#47266).
    +            $haveDist = Test-ElectronDist -InstallDir $InstallDir
    +            if (-not $haveDist) { $haveDist = Restore-ElectronDist -InstallDir $InstallDir -Mirror $mirror }
    +            if ($haveDist) {
    +                & $npmExe run pack 2>&1 | ForEach-Object { "$_" } | Tee-Object -FilePath $buildLog
    +                $code = $LASTEXITCODE
    +            } else {
    +                Write-Warn "Could not re-download Electron from the mirror (node_modules\electron\dist still missing)"
    +            }
             }
             $ErrorActionPreference = $prevEAP
             if ($code -ne 0) {
    ```
  - `scripts/install.sh`

    ```diff
    @@ -2407,6 +2407,58 @@ _desktop_pack() {
     # failed, and we never override a user-pinned ELECTRON_MIRROR.
     DESKTOP_ELECTRON_FALLBACK_MIRROR="https://npmmirror.com/mirrors/electron/"
     
    +# True (returns 0) when node_modules/electron/dist holds a usable Electron
    +# binary. electron-builder reads the binary from build.electronDist
    +# (node_modules/electron/dist) since #38673, so this is the exact file whose
    +# absence makes a pack fail with "The specified electronDist does not exist". A
    +# dist dir that exists but is missing the binary (partial extraction / aborted
    +# postinstall) is NOT ok. $1 = the workspace root holding node_modules.
    +_electron_dist_ok() {
    +    local install_dir="$1"
    +    local electron_dir="$install_dir/node_modules/electron"
    +    if [ "$OS" = "macos" ]; then
    +        [ -e "$electron_dir/dist/Electron.app/Contents/MacOS/Electron" ]
    +    else
    +        [ -e "$electron_dir/dist/electron" ]
    +    fi
    +}
    +
    +# (Re)populate node_modules/electron/dist via electron's own downloader.
    +#
    +# Since #38673 the desktop build pins build.electronDist to
    +# node_modules/electron/dist, so electron-builder reads the Electron binary
    +# straight from there and never downloads it during `npm run pack`. That dist
    +# tree is produced by the electron package's postinstall (install.js) during
    +# `npm ci`. When that download is blocked/throttled (GitHub's release host is
    +# unreachable in some regions - #47266), dist is missing and re-running pack only
    +# re-throws "The specified electronDist does not exist". The mirror fallback
    +# therefore has to drive THIS downloader, not another pack.
    +#
    +# No-op (returns 0) when the dist binary is already present. Otherwise drops a
    +# partial dist + version marker (electron's install.js short-circuits when
    +# path.txt already matches) and runs the downloader once. $1 = the workspace root
    +# holding node_modules; optional $2 = an ELECTRON_MIRROR base URL. Best-effort:
    +# returns 0 iff the dist binary exists afterward.
    +_restore_electron_dist() {
    +    local install_dir="$1"
    +    local mirror="${2:-}"
    +    local electron_dir="$install_dir/node_modules/electron"
    +    _electron_dist_ok "$install_dir" && return 0
    +
    +    [ -f "$electron_dir/install.js" ] || return 1
    +    command -v node >/dev/null 2>&1 || return 1
    +
    +    rm -rf "$electron_dir/dist" 2>/dev/null || true
    +    rm -f "$electron_dir/path.txt" 2>/dev/null || true
    +
    +    if [ -n "$mirror" ]; then
    +        ( cd "$electron_dir" && ELECTRON_MIRROR="$mirror" node install.js ) || true
    +    else
    +        ( cd "$electron_dir" && node install.js ) || true
    +    fi
    +    _electron_dist_ok "$install_dir"
    +}
    +
     # Build apps/desktop into a launchable native app. Mirrors install.ps1's
     # Install-Desktop: a root-level npm install so the apps/* workspace resolves
     # the desktop's own deps (Electron ~150MB), then `npm run pack`
    ```

    ```diff
    @@ -2479,8 +2531,19 @@ install_desktop() {
             # (b) Corrupt cached Electron zip is the most common self-healable cause.
             local purged
             purged="$(clear_electron_build_cache "$desktop_dir")"
    -        if [ -n "$purged" ]; then
    -            log_warn "Desktop build failed; cleared cached Electron download and retrying once..."
    +        # electronDist is pinned to node_modules/electron/dist (#38673):
    +        # electron-builder reads the binary from there and `pack` never downloads
    +        # it, so purging the cache + re-running pack can't by itself repopulate a
    +        # missing/partial dist. When the dist is actually gone, re-run electron's
    +        # own downloader so the retry has a binary to read. Gated on the dist
    +        # check so an unrelated build failure (tsc/vite) doesn't trigger a
    +        # pointless ~200MB refetch.
    +        local restored=false
    +        if ! _electron_dist_ok "$INSTALL_DIR"; then
    +            if _restore_electron_dist "$INSTALL_DIR"; then restored=true; fi
    +        fi
    +        if [ -n "$purged" ] || [ "$restored" = true ]; then
    +            log_warn "Desktop build failed; refreshed the Electron download and retrying once..."
                 if _desktop_pack "$desktop_dir"; then
                     pack_ok=true
                 fi
    ```

    ```diff
    @@ -2488,14 +2551,26 @@ install_desktop() {
         fi
     
         # (c) Still failing and the user hasn't pinned their own mirror: the GitHub
    -    #     release host is likely blocked/throttled. Retry once via a public
    -    #     Electron mirror (@electron/get still SHASUM-verifies the download).
    +    #     release host is likely blocked/throttled. Re-download the Electron
    +    #     binary via a public mirror, then retry. The mirror MUST drive
    +    #     electron's own downloader — `npm run pack` reads the pinned electronDist
    +    #     and never downloads, so a mirror passed only to pack is a no-op (#47266).
         if [ "$pack_ok" = false ] && [ -z "${ELECTRON_MIRROR:-}" ]; then
             log_warn "Desktop build still failing — the Electron download from GitHub looks blocked."
    -        log_warn "Retrying once via a public Electron mirror ($DESKTOP_ELECTRON_FALLBACK_MIRROR)..."
    +        log_warn "Re-downloading Electron via a public mirror ($DESKTOP_ELECTRON_FALLBACK_MIRROR), then rebuilding..."
             log_warn "  (set ELECTRON_MIRROR yourself to use a different/trusted mirror)"
    -        if _desktop_pack "$desktop_dir" "$DESKTOP_ELECTRON_FALLBACK_MIRROR"; then
    -            pack_ok=true
    +        local have_dist=false
    +        if _electron_dist_ok "$INSTALL_DIR"; then
    +            have_dist=true
    +        elif _restore_electron_dist "$INSTALL_DIR" "$DESKTOP_ELECTRON_FALLBACK_MIRROR"; then
    +            have_dist=true
    +        fi
    +        if [ "$have_dist" = true ]; then
    +            if _desktop_pack "$desktop_dir" "$DESKTOP_ELECTRON_FALLBACK_MIRROR"; then
    +                pack_ok=true
    +            fi
    +        else
    +            log_warn "Could not re-download Electron from the mirror (node_modules/electron/dist still missing)"
             fi
         fi
     
    ```
  - `tests/hermes_cli/test_gui_command.py`

    ```diff
    @@ -498,9 +498,10 @@ def test_gui_retries_pack_once_after_purging_build_cache(tmp_path, monkeypatch):
         assert mock_run.call_args_list[2].args[0] == [str(packaged_exe)]
     
     
    -def test_gui_falls_back_to_mirror_when_purge_finds_nothing(tmp_path, monkeypatch, capsys):
    -    """Purge clears nothing (not a cache problem) → fall back to an Electron
    -    mirror once before failing, so a GitHub-blocked download self-heals."""
    +def test_gui_redownloads_electron_via_mirror_then_repacks(tmp_path, monkeypatch, capsys):
    +    """Purge clears nothing and the pinned electronDist (#38673) is missing →
    +    the mirror fallback must drive electron's own downloader (NOT another pack,
    +    which never downloads Electron) and only then retry pack (#47266)."""
         root = _make_desktop_tree(tmp_path)
         monkeypatch.setattr(cli_main, "PROJECT_ROOT", root)
         _make_packaged_executable(root, monkeypatch, platform="linux")
    ```

    ```diff
    @@ -512,21 +513,59 @@ def test_gui_falls_back_to_mirror_when_purge_finds_nothing(tmp_path, monkeypatch
         with patch("hermes_cli.main.shutil.which", return_value="/usr/bin/npm"), \
              patch("hermes_cli.main._run_npm_install_deterministic", return_value=install_ok), \
              patch("hermes_cli.main._desktop_macos_relaunchable_fixup"), \
    -         patch("hermes_cli.main._purge_electron_build_cache", return_value=[]) as mock_purge, \
    +         patch("hermes_cli.main._purge_electron_build_cache", return_value=[]), \
    +         patch("hermes_cli.main._electron_dist_ok", return_value=False), \
    +         patch("hermes_cli.main._redownload_electron_dist", side_effect=[False, True]) as mock_dl, \
              patch("hermes_cli.main.subprocess.run", side_effect=[pack_fail, pack_fail]) as mock_run, \
              pytest.raises(SystemExit) as exc:
             cli_main.cmd_gui(_ns())
     
         assert exc.value.code == 1
    -    mock_purge.assert_called_once()
    -    # pack(fail) → purge(nothing) → pack via mirror(fail) = 2 subprocess.run calls
    +    # initial pack + mirror pack = 2 npm calls. The first-retry pack is skipped
    +    # because the canonical-source re-download (no mirror) failed, so there was
    +    # never a binary to build against.
         assert mock_run.call_count == 2
    -    # The retry runs the same build but with ELECTRON_MIRROR injected.
    +    # First re-download attempt is canonical (no mirror); the second drives the
    +    # public mirror.
    +    assert mock_dl.call_args_list[0].kwargs.get("mirror") is None
    +    assert mock_dl.call_args_list[1].kwargs["mirror"]
    +    # Only the mirror-driven pack carries ELECTRON_MIRROR.
         assert "ELECTRON_MIRROR" not in (mock_run.call_args_list[0].kwargs.get("env") or {})
         assert mock_run.call_args_list[1].kwargs["env"]["ELECTRON_MIRROR"]
         assert "Desktop GUI build failed" in capsys.readouterr().out
     
     
    +def test_gui_skips_pack_when_electron_redownload_unrecoverable(tmp_path, monkeypatch, capsys):
    +    """When the Electron binary can't be fetched at all (mirror also blocked),
    +    skip the pointless final pack — it would just re-throw the same missing
    +    electronDist — and fail with a clear message instead."""
    +    root = _make_desktop_tree(tmp_path)
    +    monkeypatch.setattr(cli_main, "PROJECT_ROOT", root)
    +    _make_packaged_executable(root, monkeypatch, platform="linux")
    +    monkeypatch.delenv("ELECTRON_MIRROR", raising=False)
    +
    +    install_ok = subprocess.CompletedProcess(["npm", "ci"], 0)
    +    pack_fail = subprocess.CompletedProcess(["npm", "run", "pack"], 1)
    +
    +    with patch("hermes_cli.main.shutil.which", return_value="/usr/bin/npm"), \
    +         patch("hermes_cli.main._run_npm_install_deterministic", return_value=install_ok), \
    +         patch("hermes_cli.main._desktop_macos_relaunchable_fixup"), \
    +         patch("hermes_cli.main._purge_electron_build_cache", return_value=[]), \
    +         patch("hermes_cli.main._electron_dist_ok", return_value=False), \
    +         patch("hermes_cli.main._redownload_electron_dist", return_value=False), \
    +         patch("hermes_cli.main.subprocess.run", side_effect=[pack_fail]) as mock_run, \
    +         pytest.raises(SystemExit) as exc:
    +        cli_main.cmd_gui(_ns())
    +
    +    assert exc.value.code == 1
    +    # Only the initial pack ran; both retries were skipped because no binary
    +    # could be produced.
    +    assert mock_run.call_count == 1
    +    out = capsys.readouterr().out
    +    assert "Could not re-download Electron from the mirror" in out
    +    assert "Desktop GUI build failed" in out
    +
    +
     def test_gui_does_not_override_user_electron_mirror(tmp_path, monkeypatch, capsys):
         """A user-pinned ELECTRON_MIRROR is respected: no extra mirror fallback
         attempt (and we never swap in our default mirror)."""
    ```

    ```diff
    @@ -553,6 +592,108 @@ def test_gui_does_not_override_user_electron_mirror(tmp_path, monkeypatch, capsy
         assert "Desktop GUI build failed" in capsys.readouterr().out
     
     
    +# ── electronDist (re)download helper tests (#47266) ───────────────────
    +
    +
    [redacted:email](
    +    "platform,rel",
    +    [
    +        ("linux", "dist/electron"),
    +        ("win32", "dist/electron.exe"),
    +        ("darwin", "dist/Electron.app/Contents/MacOS/Electron"),
    +    ],
    +)
    +def test_electron_dist_ok_per_platform(tmp_path, monkeypatch, platform, rel):
    +    monkeypatch.setattr(cli_main.sys, "platform", platform)
    +    electron = tmp_path / "node_modules" / "electron"
    +    # A dist dir that exists but lacks the binary is NOT ok (partial extraction).
    +    (electron / "dist").mkdir(parents=True)
    +    assert cli_main._electron_dist_ok(tmp_path) is False
    +
    +    binp = electron / rel
    +    binp.parent.mkdir(parents=True, exist_ok=True)
    +    binp.write_text("", encoding="utf-8")
    +    assert cli_main._electron_dist_ok(tmp_path) is True
    +
    +
    +def test_redownload_electron_dist_noop_when_present(tmp_path, monkeypatch):
    +    """Already-healthy dist → no download, so an unrelated build failure can't
    +    trigger a needless ~200 MB refetch."""
    +    monkeypatch.setattr(cli_main.sys, "platform", "linux")
    +    binp = tmp_path / "node_modules" / "electron" / "dist" / "electron"
    +    binp.parent.mkdir(parents=True)
    +    binp.write_text("", encoding="utf-8")
    +
    +    with patch("hermes_cli.main.subprocess.run") as mock_run:
    +        assert cli_main._redownload_electron_dist(tmp_path, {}) is True
    +    mock_run.assert_not_called()
    +
    +
    +def test_redownload_electron_dist_missing_installer(tmp_path, monkeypatch):
    +    """No electron/install.js (deps never installed) → nothing to run."""
    +    monkeypatch.setattr(cli_main.sys, "platform", "linux")
    +    (tmp_path / "node_modules" / "electron").mkdir(parents=True)
    +
    +    with patch("hermes_cli.main.shutil.which", return_value="/usr/bin/node"), \
    +         patch("hermes_cli.main.subprocess.run") as mock_run:
    +        assert cli_main._redownload_electron_dist(tmp_path, {}) is False
    +    mock_run.assert_not_called()
    +
    +
    +def test_redownload_electron_dist_runs_installer_with_mirror(tmp_path, monkeypatch):
    +    """Missing dist → wipe any partial dist + version marker, run electron's own
    +    install.js with ELECTRON_MIRROR injected, and report success on the binary."""
    +    monkeypatch.setattr(cli_main.sys, "platform", "linux")
    +    electron = tmp_path / "node_modules" / "electron"
    +    electron.mkdir(parents=True)
    +    (electron / "install.js").write_text("// stub", encoding="utf-8")
    +    # A stale partial dist + version marker that MUST be cleared first, otherwise
    +    # electron's install.js short-circuits on path.txt and never re-downloads.
    +    (electron / "dist").mkdir()
    +    (electron / "dist" / "leftover").write_text("junk", encoding="utf-8")
    +    (electron / "path.txt").write_text("electron", encoding="utf-8")
    +
    +    captured = {}
    +
    +    def fake_run(cmd, **kwargs):
    +        captured["cmd"] = cmd
    +        captured["env"] = kwargs.get("env")
    +        captured["cwd"] = kwargs.get("cwd")
    +        # simulate electron's install.js producing the dist binary
    +        binp = electron / "dist" / "electron"
    +        binp.parent.mkdir(parents=True, exist_ok=True)
    +        binp.write_text("", encoding="utf-8")
    +        return subprocess.CompletedProcess(cmd, 0)
    +
    +    with patch("hermes_cli.main.shutil.which", return_value="/usr/bin/node"), \
    +         patch("hermes_cli.main.subprocess.run", side_effect=fake_run):
    +        ok = cli_main._redownload_electron_dist(
    +            tmp_path, {"PATH": "/x"}, mirror="https://mirror.example/electron/"
    +        )
    +
    +    assert ok is True
    +    assert captured["cmd"] == ["/usr/bin/node", str(electron / "install.js")]
    +    assert captured["cwd"] == str(electron)
    +    assert captured["env"]["ELECTRON_MIRROR"] == "https://mirror.example/electron/"
    +    # The partial dir + marker were dropped before the re-download.
    +    assert not (electron / "dist" / "leftover").exists()
    +    assert not (electron / "path.txt").exists()
    +
    +
    +def test_redownload_electron_dist_returns_false_when_download_fails(tmp_path, monkeypatch):
    +    """install.js ran but produced no binary (still blocked) → False, so the
    +    caller skips a doomed pack."""
    +    monkeypatch.setattr(cli_main.sys, "platform", "linux")
    +    electron = tmp_path / "node_modules" / "electron"
    +    electron.mkdir(parents=True)
    +    (electron / "install.js").write_text("// stub", encoding="utf-8")
    +
    +    with patch("hermes_cli.main.shutil.which", return_value="/usr/bin/node"), \
    +         patch("hermes_cli.main.subprocess.run",
    +               return_value=subprocess.CompletedProcess(["node"], 1)):
    +        assert cli_main._redownload_electron_dist(tmp_path, {}) is False
    +
    +
     class _FakeProc:
         """Minimal psutil.Process stand-in for the lock-breaker tests."""
     
    ```
- **#48081: fix(desktop): resolve electronDist to the actual electron install location**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `apps/desktop/package.json`, `apps/desktop/scripts/patch-electron-builder-mac-binary.cjs`, `hermes_cli/main.py`, `scripts/install.ps1`, `scripts/install.sh`, `scripts/release.py`, `tests/hermes_cli/test_gui_command.py`, `tests/test_desktop_electron_pin.py`
  - fix hunks:
  - `apps/desktop/package.json`

    ```diff
    @@ -135,7 +135,7 @@
       },
       "build": {
         "electronVersion": "40.10.2",
    -    "electronDist": "../../node_modules/electron/dist",
    +    "electronDist": "node_modules/electron/dist",
         "appId": "com.nousresearch.hermes",
         "productName": "Hermes",
         "executableName": "Hermes",
    ```
  - `apps/desktop/scripts/patch-electron-builder-mac-binary.cjs`

    ```diff
    @@ -24,6 +24,11 @@ const replacement = `    // ${marker}: electron-builder 26.8.x can sometimes cop
         if (!fs.existsSync(bundledElectronBinary)) {
             const candidates = [
                 path.join(packager.info.framework.distMacOsAppName, "Contents", "MacOS", electronBranding.productName),
    +            // npm may nest the workspace-only electron devDep under
    +            // apps/desktop/node_modules (process.cwd() during pack), or hoist
    +            // it to the repo root. Try the workspace-local install first, then
    +            // the root hoist, so the fallback works under either layout.
    +            path.join(process.cwd(), "node_modules", "electron", "dist", "Electron.app", "Contents", "MacOS", electronBranding.productName),
                 path.join(process.cwd(), "..", "..", "node_modules", "electron", "dist", "Electron.app", "Contents", "MacOS", electronBranding.productName),
             ];
             const sourceBinary = candidates.find(candidate => fs.existsSync(candidate));
    ```
  - `hermes_cli/main.py`

    ```diff
    @@ -5110,16 +5110,33 @@ def _purge_electron_build_cache(desktop_dir: Path) -> list[Path]:
         return removed
     
     
    +def _electron_dir(project_root: Path) -> Path:
    +    """Return the Electron package directory the desktop workspace installs.
    +
    +    npm may keep workspace-only dev dependencies under
    +    ``apps/desktop/node_modules`` instead of hoisting them to the repo root.
    +    Which layout you get depends on the npm version and what else is installed,
    +    so a build path that assumes one or the other breaks intermittently across
    +    machines. ``apps/desktop/package.json`` points electron-builder's
    +    ``electronDist`` at ``node_modules/electron/dist`` relative to the desktop
    +    project, so prefer the workspace-local package and fall back to the root
    +    hoist when that's where npm landed it.
    +    """
    +    desktop_local = project_root / "apps" / "desktop" / "node_modules" / "electron"
    +    if desktop_local.exists():
    +        return desktop_local
    +    return project_root / "node_modules" / "electron"
    +
    +
     def _electron_dist_binary(project_root: Path) -> Path:
    -    """Return the path to the Electron main binary inside ``node_modules``.
    +    """Return the path to the Electron main binary inside the installed package.
     
    -    electron-builder reads the binary from ``build.electronDist``
    -    (``node_modules/electron/dist``) since #38673, so this is the exact file
    -    whose absence makes a pack fail with "The specified electronDist does not
    -    exist". The basename differs per OS (the platform Electron is named for the
    -    host the build runs on).
    +    electron-builder reads the binary from ``build.electronDist`` since #38673,
    +    so this is the exact file whose absence makes a pack fail with "The
    +    specified electronDist does not exist". The basename differs per OS (the
    +    platform Electron is named for the host the build runs on).
         """
    -    dist = project_root / "node_modules" / "electron" / "dist"
    +    dist = _electron_dir(project_root) / "dist"
         if sys.platform == "darwin":
             return dist / "Electron.app" / "Contents" / "MacOS" / "Electron"
         if sys.platform == "win32":
    ```

    ```diff
    @@ -5169,7 +5186,7 @@ def _redownload_electron_dist(
         if _electron_dist_ok(project_root):
             return True
     
    -    electron_dir = project_root / "node_modules" / "electron"
    +    electron_dir = _electron_dir(project_root)
         installer = electron_dir / "install.js"
         if not installer.is_file():
             return False
    ```

    ```diff
    @@ -5387,8 +5404,8 @@ def cmd_gui(args: argparse.Namespace):
                     print("  Pre-build first:  cd apps/desktop && npm run build")
                     print("  Or drop --skip-build to install dependencies and build automatically.")
                     sys.exit(1)
    -            if not (PROJECT_ROOT / "node_modules" / "electron" / "package.json").exists():
    -                print("✗ --skip-build --source requires existing workspace dependencies.")
    +            if not (_electron_dir(PROJECT_ROOT) / "package.json").exists():
    +                print("✗ --skip-build --source requires existing desktop workspace dependencies.")
                     print(f"  Install first:  cd {PROJECT_ROOT} && npm ci")
                     print("  Or drop --skip-build to install dependencies and build automatically.")
                     sys.exit(1)
    ```
  - `scripts/install.ps1`

    ```diff
    @@ -2161,28 +2161,41 @@ function Clear-ElectronBuildCache {
         return $removed
     }
     
    -# True when node_modules\electron\dist holds a usable Electron binary.
    -# electron-builder reads the binary from build.electronDist
    -# (node_modules\electron\dist) since #38673, so this is the exact file whose
    -# absence makes a pack fail with "The specified electronDist does not exist". A
    -# dist dir that exists but is missing electron.exe (partial extraction / aborted
    -# postinstall) is NOT ok.
    +# Return the Electron package directory the desktop workspace installs. npm may
    +# nest workspace-only dev dependencies under apps\desktop\node_modules instead
    +# of hoisting them to the repo root; which layout you get depends on the npm
    +# version and what else is installed. apps\desktop\package.json points
    +# electron-builder's electronDist there, so prefer the workspace-local package
    +# and fall back to the root hoist.
    +function Get-ElectronDir {
    +    param([string]$InstallDir)
    +    $desktopLocal = Join-Path $InstallDir 'apps\desktop\node_modules\electron'
    +    if (Test-Path -LiteralPath $desktopLocal) { return $desktopLocal }
    +    return (Join-Path $InstallDir 'node_modules\electron')
    +}
    +
    +# True when the desktop workspace electronDist holds a usable Electron binary.
    +# electron-builder reads the binary from build.electronDist since #38673, so
    +# this is the exact file whose absence makes a pack fail with "The specified
    +# electronDist does not exist". A dist dir that exists but is missing
    +# electron.exe (partial extraction / aborted postinstall) is NOT ok.
     function Test-ElectronDist {
         param([string]$InstallDir)
    -    $distExe = Join-Path $InstallDir 'node_modules\electron\dist\electron.exe'
    +    $electronDir = Get-ElectronDir -InstallDir $InstallDir
    +    $distExe = Join-Path $electronDir 'dist\electron.exe'
         return (Test-Path -LiteralPath $distExe)
     }
     
    -# (Re)populate node_modules\electron\dist via electron's own downloader.
    +# (Re)populate the desktop Electron dist via electron's own downloader.
     #
    -# Since #38673 the desktop build pins build.electronDist to
    -# node_modules\electron\dist, so electron-builder reads the Electron binary
    -# straight from there and never downloads it during `npm run pack`. That dist
    -# tree is produced by the electron package's postinstall (install.js) during
    -# `npm ci`. When that download is blocked/throttled (GitHub's release host is
    -# unreachable in some regions - #47266), dist is missing and re-running pack only
    -# re-throws "The specified electronDist does not exist". The mirror fallback
    -# therefore has to drive THIS downloader, not another pack.
    +# Since #38673 the desktop build pins build.electronDist, so electron-builder
    +# reads the Electron binary straight from there and never downloads it during
    +# `npm run pack`. That dist tree is produced by the electron package's
    +# postinstall (install.js) during `npm ci`. When that download is
    +# blocked/throttled (GitHub's release host is unreachable in some regions -
    +# #47266), dist is missing and re-running pack only re-throws "The specified
    +# electronDist does not exist". The mirror fallback therefore has to drive THIS
    +# downloader, not another pack.
     #
     # No-op (returns $true) when the dist binary is already present. Otherwise drops a
     # partial dist + version marker (electron's install.js short-circuits when
    ```

    ```diff
    @@ -2193,7 +2206,7 @@ function Restore-ElectronDist {
         param([string]$InstallDir, [string]$Mirror)
         if (Test-ElectronDist -InstallDir $InstallDir) { return $true }
     
    -    $electronDir = Join-Path $InstallDir 'node_modules\electron'
    +    $electronDir = Get-ElectronDir -InstallDir $InstallDir
         $distExe = Join-Path $electronDir 'dist\electron.exe'
         $installer = Join-Path $electronDir 'install.js'
         if (-not (Test-Path -LiteralPath $installer)) { return $false }
    ```
  - `scripts/install.sh`

    ```diff
    @@ -2407,15 +2407,31 @@ _desktop_pack() {
     # failed, and we never override a user-pinned ELECTRON_MIRROR.
     DESKTOP_ELECTRON_FALLBACK_MIRROR="https://npmmirror.com/mirrors/electron/"
     
    -# True (returns 0) when node_modules/electron/dist holds a usable Electron
    -# binary. electron-builder reads the binary from build.electronDist
    -# (node_modules/electron/dist) since #38673, so this is the exact file whose
    -# absence makes a pack fail with "The specified electronDist does not exist". A
    -# dist dir that exists but is missing the binary (partial extraction / aborted
    -# postinstall) is NOT ok. $1 = the workspace root holding node_modules.
    +# Return the Electron package directory the desktop workspace installs. npm may
    +# nest workspace-only dev dependencies under apps/desktop/node_modules instead
    +# of hoisting them to the repo root; which layout you get depends on the npm
    +# version and what else is installed. apps/desktop/package.json points
    +# electron-builder's electronDist there, so prefer the workspace-local package
    +# and fall back to the root hoist. $1 = the workspace root holding node_modules.
    +_electron_dir() {
    +    local install_dir="$1"
    +    if [ -d "$install_dir/apps/desktop/node_modules/electron" ]; then
    +        printf '%s\n' "$install_dir/apps/desktop/node_modules/electron"
    +    else
    +        printf '%s\n' "$install_dir/node_modules/electron"
    +    fi
    +}
    +
    +# True (returns 0) when the desktop workspace electronDist holds a usable
    +# Electron binary. electron-builder reads the binary from build.electronDist
    +# since #38673, so this is the exact file whose absence makes a pack fail with
    +# "The specified electronDist does not exist". A dist dir that exists but is
    +# missing the binary (partial extraction / aborted postinstall) is NOT ok.
    +# $1 = the workspace root holding node_modules.
     _electron_dist_ok() {
         local install_dir="$1"
    -    local electron_dir="$install_dir/node_modules/electron"
    +    local electron_dir
    +    electron_dir="$(_electron_dir "$install_dir")"
         if [ "$OS" = "macos" ]; then
             [ -e "$electron_dir/dist/Electron.app/Contents/MacOS/Electron" ]
         else
    ```

    ```diff
    @@ -2423,16 +2439,16 @@ _electron_dist_ok() {
         fi
     }
     
    -# (Re)populate node_modules/electron/dist via electron's own downloader.
    +# (Re)populate the desktop Electron dist via electron's own downloader.
     #
    -# Since #38673 the desktop build pins build.electronDist to
    -# node_modules/electron/dist, so electron-builder reads the Electron binary
    -# straight from there and never downloads it during `npm run pack`. That dist
    -# tree is produced by the electron package's postinstall (install.js) during
    -# `npm ci`. When that download is blocked/throttled (GitHub's release host is
    -# unreachable in some regions - #47266), dist is missing and re-running pack only
    -# re-throws "The specified electronDist does not exist". The mirror fallback
    -# therefore has to drive THIS downloader, not another pack.
    +# Since #38673 the desktop build pins build.electronDist, so electron-builder
    +# reads the Electron binary straight from there and never downloads it during
    +# `npm run pack`. That dist tree is produced by the electron package's
    +# postinstall (install.js) during `npm ci`. When that download is
    +# blocked/throttled (GitHub's release host is unreachable in some regions -
    +# #47266), dist is missing and re-running pack only re-throws "The specified
    +# electronDist does not exist". The mirror fallback therefore has to drive THIS
    +# downloader, not another pack.
     #
     # No-op (returns 0) when the dist binary is already present. Otherwise drops a
     # partial dist + version marker (electron's install.js short-circuits when
    ```

    ```diff
    @@ -2442,7 +2458,8 @@ _electron_dist_ok() {
     _restore_electron_dist() {
         local install_dir="$1"
         local mirror="${2:-}"
    -    local electron_dir="$install_dir/node_modules/electron"
    +    local electron_dir
    +    electron_dir="$(_electron_dir "$install_dir")"
         _electron_dist_ok "$install_dir" && return 0
     
         [ -f "$electron_dir/install.js" ] || return 1
    ```
  - `scripts/release.py`

    ```diff
    @@ -45,6 +45,9 @@
     
     # Auto-extracted from noreply emails + manual overrides
     AUTHOR_MAP = {
    +    "[redacted:email]": "sitkarev",
    +    "[redacted:email]": "omegazheng",
    +    "[redacted:email]": "james47kjv",
         "[redacted:email]": "1960697431",
         "[redacted:email]": "definitelynotguru",
         "[redacted:email]": "ChasLui",
    ```
  - `tests/hermes_cli/test_gui_command.py`

    ```diff
    @@ -616,6 +616,33 @@ def test_electron_dist_ok_per_platform(tmp_path, monkeypatch, platform, rel):
         assert cli_main._electron_dist_ok(tmp_path) is True
     
     
    +def test_electron_dir_prefers_workspace_local_package(tmp_path):
    +    """npm may nest electron under apps/desktop; resolve there over the root hoist."""
    +    root_electron = tmp_path / "node_modules" / "electron"
    +    local_electron = tmp_path / "apps" / "desktop" / "node_modules" / "electron"
    +    root_electron.mkdir(parents=True)
    +    local_electron.mkdir(parents=True)
    +
    +    assert cli_main._electron_dir(tmp_path) == local_electron
    +
    +
    +def test_electron_dir_falls_back_to_root_hoist(tmp_path):
    +    """When npm hoists electron to the repo root, resolve there."""
    +    root_electron = tmp_path / "node_modules" / "electron"
    +    root_electron.mkdir(parents=True)
    +
    +    assert cli_main._electron_dir(tmp_path) == root_electron
    +
    +
    +def test_electron_dist_ok_finds_workspace_local_binary(tmp_path, monkeypatch):
    +    """A nested apps/desktop electron with a valid binary counts as ok."""
    +    monkeypatch.setattr(cli_main.sys, "platform", "linux")
    +    binp = tmp_path / "apps" / "desktop" / "node_modules" / "electron" / "dist" / "electron"
    +    binp.parent.mkdir(parents=True)
    +    binp.write_text("", encoding="utf-8")
    +    assert cli_main._electron_dist_ok(tmp_path) is True
    +
    +
     def test_redownload_electron_dist_noop_when_present(tmp_path, monkeypatch):
         """Already-healthy dist → no download, so an unrelated build failure can't
         trigger a needless ~200 MB refetch."""
    ```
  - `tests/test_desktop_electron_pin.py`

    ```diff
    @@ -94,3 +94,42 @@ def test_lockfile_resolves_the_pinned_electron():
             f"but the pin is {spec!r}; run `npm install --package-lock-only` so "
             "`npm ci` stays consistent."
         )
    +
    +
    +def test_electron_dist_matches_lockfile_install_location():
    +    """build.electronDist must point at where the lockfile installs Electron.
    +
    +    electron-builder copies the unpacked Electron from ``build.electronDist``
    +    (resolved relative to ``apps/desktop``). npm workspace hoisting is not
    +    deterministic across machines/npm versions: it may nest Electron under
    +    ``apps/desktop/node_modules/electron`` or hoist it to the repo root. If
    +    electronDist points at one location while the lockfile installs at the
    +    other, packaging fails with ``The specified electronDist does not exist`` —
    +    the "Building desktop app" failure reported after the June lockfile
    +    regeneration floated Electron and reshuffled the hoist. Lock the two
    +    together so a hoist change (root <-> nested) can't silently break the path
    +    again.
    +    """
    +    if not ROOT_LOCK.is_file():
    +        pytest.skip("root package-lock.json not present")
    +    electron_dist = _desktop_pkg().get("build", {}).get("electronDist")
    +    assert electron_dist, "build.electronDist is missing"
    +
    +    lock = json.loads(ROOT_LOCK.read_text(encoding="utf-8"))
    +    electron_paths = [
    +        path
    +        for path in lock.get("packages", {})
    +        if path.endswith("node_modules/electron")
    +    ]
    +    assert electron_paths, "no electron entry found in package-lock.json"
    +
    +    desktop_dir = REPO_ROOT / "apps" / "desktop"
    +    # electronDist is resolved relative to the apps/desktop project dir.
    +    configured = (desktop_dir / electron_dist).resolve()
    +    # Where the lockfile actually places Electron's unpacked dist.
    +    installed = {(REPO_ROOT / p / "dist").resolve() for p in electron_paths}
    +    assert configured in installed, (
    +        f"build.electronDist={electron_dist!r} resolves to {configured}, but the "
    +        f"lockfile installs Electron at {sorted(str(p) for p in installed)}. "
    +        "electron-builder will fail with 'electronDist does not exist'."
    +    )
    ```
- **#48091: fix(desktop): resolve electronDist dynamically + self-heal blocked installs (supersedes #48081/#48082)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `apps/desktop/package.json`, `apps/desktop/scripts/run-electron-builder.cjs`, `hermes_cli/main.py`, `scripts/install.ps1`, `scripts/install.sh`, `tests/hermes_cli/test_gui_command.py`, `tests/test_desktop_electron_pin.py`
  - fix hunks:
  - `apps/desktop/package.json`

    ```diff
    @@ -21,7 +21,7 @@
         "build": "node scripts/assert-root-install.cjs && node scripts/write-build-stamp.cjs && node scripts/stage-native-deps.cjs && tsc -b && vite build && npm run postbuild",
         "postbuild": "node scripts/assert-dist-built.cjs",
         "prebuilder": "node scripts/patch-electron-builder-mac-binary.cjs",
    -    "builder": "cross-env NODE_OPTIONS=--max-old-space-size=16384 electron-builder",
    +    "builder": "cross-env NODE_OPTIONS=--max-old-space-size=16384 node scripts/run-electron-builder.cjs",
         "pack": "npm run build && npm run builder -- --dir",
         "dist": "npm run build && npm run builder",
         "dist:mac": "npm run build && npm run builder -- --mac",
    ```

    ```diff
    @@ -135,7 +135,6 @@
       },
       "build": {
         "electronVersion": "40.10.2",
    -    "electronDist": "node_modules/electron/dist",
         "appId": "com.nousresearch.hermes",
         "productName": "Hermes",
         "executableName": "Hermes",
    ```
  - `apps/desktop/scripts/run-electron-builder.cjs`

    ```diff
    @@ -0,0 +1,57 @@
    +"use strict"
    +
    +// Resolve electronDist at runtime (#38673, #47917): electron-builder 26.8.x can
    +// re-unpack a broken Electron.app; reusing the installed dist dodges that.
    +// npm workspace hoisting is non-deterministic — require.resolve finds electron
    +// wherever it landed. Dist present → -c.electronDist=<abs>/dist; absent → let
    +// electron-builder fetch via @electron/get (electronVersion + ELECTRON_MIRROR).
    +
    +const fs = require("node:fs")
    +const path = require("node:path")
    +const { spawnSync } = require("node:child_process")
    +
    +function electronDistDir() {
    +  try {
    +    return path.join(path.dirname(require.resolve("electron/package.json")), "dist")
    +  } catch {
    +    return null
    +  }
    +}
    +
    +function distBinary(dist) {
    +  if (process.platform === "darwin") {
    +    return path.join(dist, "Electron.app", "Contents", "MacOS", "Electron")
    +  }
    +  if (process.platform === "win32") {
    +    return path.join(dist, "electron.exe")
    +  }
    +  return path.join(dist, "electron")
    +}
    +
    +function electronBuilderCli() {
    +  const pkgJson = require.resolve("electron-builder/package.json")
    +  const bin = require(pkgJson).bin
    +  const rel = typeof bin === "string" ? bin : bin["electron-builder"]
    +  return path.join(path.dirname(pkgJson), rel)
    +}
    +
    +const dist = electronDistDir()
    +const args = []
    +if (dist && fs.existsSync(distBinary(dist))) {
    +  args.push(`-c.electronDist=${dist}`)
    +} else {
    +  console.warn(
    +    "[run-electron-builder] no local electron dist; electron-builder will fetch " +
    +      "via @electron/get (electronVersion + ELECTRON_MIRROR)."
    +  )
    +}
    +args.push(...process.argv.slice(2))
    +
    +const result = spawnSync(process.execPath, [electronBuilderCli(), ...args], {
    +  stdio: "inherit",
    +})
    +if (result.error) {
    +  console.error(`[run-electron-builder] spawn failed: ${result.error.message}`)
    +  process.exit(1)
    +}
    +process.exit(result.status == null ? 1 : result.status)
    ```
  - `hermes_cli/main.py`

    ```diff
    @@ -5110,6 +5110,11 @@ def _purge_electron_build_cache(desktop_dir: Path) -> list[Path]:
         return removed
     
     
    +# Last-resort Electron mirror after GitHub download fails (#47266). Only used
    +# when the user hasn't pinned ELECTRON_MIRROR.
    +_ELECTRON_FALLBACK_MIRROR = "https://npmmirror.com/mirrors/electron/"
    +
    +
     def _electron_dir(project_root: Path) -> Path:
         """Return the Electron package directory the desktop workspace installs.
     
    ```

    ```diff
    @@ -5158,31 +5163,23 @@ def _electron_dist_ok(project_root: Path) -> bool:
             return False
     
     
    +def _electron_pkg_staged_missing_dist(project_root: Path) -> bool:
    +    """electron staged (package.json + install.js) but dist missing — blocked postinstall."""
    +    electron_dir = _electron_dir(project_root)
    +    return (
    +        (electron_dir / "package.json").is_file()
    +        and (electron_dir / "install.js").is_file()
    +        and not _electron_dist_ok(project_root)
    +    )
    +
    +
     def _redownload_electron_dist(
         project_root: Path,
         env: dict,
         *,
         mirror: Optional[str] = None,
     ) -> bool:
    -    """(Re)populate ``node_modules/electron/dist`` via electron's own downloader.
    -
    -    Since #38673 the desktop build pins ``build.electronDist`` to
    -    ``node_modules/electron/dist``, so electron-builder reads the Electron binary
    -    straight from there and never downloads it during ``npm run pack``. That dist
    -    tree is produced by the ``electron`` package's postinstall (``install.js``)
    -    during ``npm ci``. When that download is blocked or throttled (GitHub's
    -    release host is unreachable in some regions — #47266), the dist is missing
    -    and re-running ``pack`` only re-throws "The specified electronDist does not
    -    exist". The mirror fallback therefore has to drive *this* downloader, not
    -    another ``pack``.
    -
    -    No-op (returns True) when the dist binary is already present, so an unrelated
    -    build failure doesn't trigger a needless ~200 MB re-download. Otherwise drops
    -    any partial dist + version marker (electron's install.js short-circuits when
    -    ``path.txt`` already matches) and runs the downloader once, optionally via a
    -    mirror. Best-effort: never raises. Returns True iff the dist binary exists
    -    afterward.
    -    """
    +    """Best-effort: run electron's install.js to populate dist/ (optional mirror)."""
         if _electron_dist_ok(project_root):
             return True
     
    ```

    ```diff
    @@ -5211,6 +5208,15 @@ def _redownload_electron_dist(
         return _electron_dist_ok(project_root)
     
     
    +def _try_redownload_electron_dist(project_root: Path, env: dict) -> bool:
    +    """Canonical download, then fallback mirror unless the user pinned one."""
    +    if _redownload_electron_dist(project_root, env):
    +        return True
    +    if env.get("ELECTRON_MIRROR"):
    +        return False
    +    return _redownload_electron_dist(project_root, env, mirror=_ELECTRON_FALLBACK_MIRROR)
    +
    +
     def _stop_desktop_processes_locking_build(desktop_dir: Path) -> list[int]:
         """Terminate any running desktop app executing from this build's ``release``
         dir so a rebuild can replace its (otherwise locked) executable.
    ```

    ```diff
    @@ -5433,9 +5439,18 @@ def cmd_gui(args: argparse.Namespace):
                 nixos_env = _nixos_build_env()
                 install_result = _run_npm_install_deterministic(npm, PROJECT_ROOT, capture_output=False, env=nixos_env)
                 if install_result.returncode != 0:
    -                print("✗ Desktop dependency install failed")
    -                print(f"  Run manually:  cd {PROJECT_ROOT} && npm ci")
    -                sys.exit(install_result.returncode or 1)
    +                if not _electron_pkg_staged_missing_dist(PROJECT_ROOT):
    +                    print("✗ Desktop dependency install failed")
    +                    print(f"  Run manually:  cd {PROJECT_ROOT} && npm ci")
    +                    sys.exit(install_result.returncode or 1)
    +                repaired = _try_redownload_electron_dist(PROJECT_ROOT, env)
    +                if repaired:
    +                    print("  ⚠ Dependency install failed with a missing Electron dist; "
    +                          "repopulated it and continuing.")
    +                else:
    +                    print("  ⚠ Dependency install failed with a missing Electron dist; "
    +                          "continuing to the build so electron-builder can attempt "
    +                          "the Electron fetch itself.")
     
                 build_label = "source build" if source_mode else "packaged app"
                 print(f"→ Building desktop {build_label}...")
    ```

    ```diff
    @@ -5451,31 +5466,15 @@ def cmd_gui(args: argparse.Namespace):
                         print(f"  ⚠ Stopped running desktop app to free the build output (pid {', '.join(map(str, stopped))})")
                 build_result = subprocess.run([npm, "run", build_script], cwd=desktop_dir, env=env, check=False)
                 if build_result.returncode != 0 and not source_mode:
    -                # A corrupt cached Electron zip makes `pack` fail with an ENOENT
    -                # on the final `electron` -> `Hermes` rename: unpack-electron
    -                # extracted a partial tree (missing the 193 MB binary) from the
    -                # bad zip. We do NOT try to prove the zip is corrupt ourselves —
    -                # stdlib zipfile silently tolerates the prepended/concatenated
    -                # junk that is the most common corruption (a partial download
    -                # resumed into the same file), so a `testzip()` gate would pass
    -                # and never self-heal. Instead, on any packaged-build failure we
    -                # purge the version's cached zip + the half-written unpacked dir
    -                # and retry once: @electron/get re-downloads with its own SHASUM
    -                # verification, which is the real source of truth. If the
    -                # failure was something else, the clean re-download is harmless
    -                # and the retry fails the same way.
    -                purged = _purge_electron_build_cache(desktop_dir)
    -                # electronDist is pinned to node_modules/electron/dist (#38673):
    -                # electron-builder reads the Electron binary from there and `pack`
    -                # never downloads it, so purging the cache + re-running pack can't
    -                # by itself repopulate a missing/partial dist. When the dist is
    -                # actually gone, re-run electron's own downloader so the retry has
    -                # a binary to read. Gated on the dist check so an unrelated build
    -                # failure (tsc/vite) doesn't trigger a pointless ~200 MB refetch.
    +                # Corrupt cached Electron zip → partial unpack → ENOENT on rename.
    +                # stdlib zipfile won't catch the common concat-junk case, so purge
    +                # and retry once; @electron/get SHASUM is the real gate.
    +                purged: list[Path] = []
                     restored = False
                     if not _electron_dist_ok(PROJECT_ROOT):
    +                    purged = _purge_electron_build_cache(desktop_dir)
                         restored = _redownload_electron_dist(PROJECT_ROOT, env)
    -                if purged or restored:
    +                if restored:
                         print("  ⚠ Desktop build failed; refreshed the Electron download and retrying once...")
                         for p in purged:
                             print(f"    - {p}")
    ```

    ```diff
    @@ -5484,35 +5483,16 @@ def cmd_gui(args: argparse.Namespace):
                         _stop_desktop_processes_locking_build(desktop_dir)
                         build_result = subprocess.run([npm, "run", build_script], cwd=desktop_dir, env=env, check=False)
                 if build_result.returncode != 0 and not source_mode and not env.get("ELECTRON_MIRROR"):
    -                # Still failing and the user hasn't pinned a mirror: GitHub's
    -                # Electron release host is likely blocked/throttled (the repeating
    -                # "retrying" download log). Retry once via npmmirror.com — the
    -                # de-facto Electron community mirror (Alibaba). @electron/get
    -                # SHASUM-checks the download, but the SHASUMS come from the same
    -                # mirror, so that guards against a corrupt/partial download, NOT
    -                # a compromised mirror: reaching for it is an explicit trust
    -                # trade-off we only make AFTER the canonical GitHub download has
    -                # failed, and we never override a user-pinned ELECTRON_MIRROR.
                     print("  ⚠ Desktop build still failing; the Electron download from "
                           "GitHub looks blocked. Re-downloading via a public mirror "
                           "(npmmirror.com)... (set ELECTRON_MIRROR to use another mirror)")
    -                mirror = "https://npmmirror.com/mirrors/electron/"
    +                mirror = _ELECTRON_FALLBACK_MIRROR
                     mirror_env = dict(env)
                     mirror_env["ELECTRON_MIRROR"] = mirror
    -                # electronDist is pinned (#38673), so `npm run pack` never
    -                # downloads Electron — the mirror only helps if it drives
    -                # electron's own downloader. Re-fetch the binary through the
    -                # mirror first; otherwise the retry just re-reads the same missing
    -                # dist and re-throws "electronDist does not exist" (#47266).
    -                have_dist = _electron_dist_ok(PROJECT_ROOT)
    -                if not have_dist:
    -                    have_dist = _redownload_electron_dist(PROJECT_ROOT, env, mirror=mirror)
    -                if have_dist:
    -                    _stop_desktop_processes_locking_build(desktop_dir)
    -                    build_result = subprocess.run([npm, "run", build_script], cwd=desktop_dir, env=mirror_env, check=False)
    -                else:
    -                    print("  ✗ Could not re-download Electron from the mirror "
    -                          "(node_modules/electron/dist still missing)")
    +                if not _electron_dist_ok(PROJECT_ROOT):
    +                    _redownload_electron_dist(PROJECT_ROOT, env, mirror=mirror)
    +                _stop_desktop_processes_locking_build(desktop_dir)
    +                build_result = subprocess.run([npm, "run", build_script], cwd=desktop_dir, env=mirror_env, check=False)
                 if build_result.returncode != 0:
                     print("✗ Desktop GUI build failed")
                     print(f"  Run manually:  cd apps/desktop && npm run {build_script}")
    ```
  - `scripts/install.ps1`

    ```diff
    @@ -2161,12 +2161,10 @@ function Clear-ElectronBuildCache {
         return $removed
     }
     
    -# Return the Electron package directory the desktop workspace installs. npm may
    -# nest workspace-only dev dependencies under apps\desktop\node_modules instead
    -# of hoisting them to the repo root; which layout you get depends on the npm
    -# version and what else is installed. apps\desktop\package.json points
    -# electron-builder's electronDist there, so prefer the workspace-local package
    -# and fall back to the root hoist.
    +# Last-resort Electron mirror after GitHub download fails (#47266).
    +$script:DesktopElectronFallbackMirror = "https://npmmirror.com/mirrors/electron/"
    +
    +# Electron package dir — workspace-local nest first, then root hoist.
     function Get-ElectronDir {
         param([string]$InstallDir)
         $desktopLocal = Join-Path $InstallDir 'apps\desktop\node_modules\electron'
    ```

    ```diff
    @@ -2174,11 +2172,7 @@ function Get-ElectronDir {
         return (Join-Path $InstallDir 'node_modules\electron')
     }
     
    -# True when the desktop workspace electronDist holds a usable Electron binary.
    -# electron-builder reads the binary from build.electronDist since #38673, so
    -# this is the exact file whose absence makes a pack fail with "The specified
    -# electronDist does not exist". A dist dir that exists but is missing
    -# electron.exe (partial extraction / aborted postinstall) is NOT ok.
    +# True when dist/ holds a usable Electron binary (#38673 / run-electron-builder.cjs).
     function Test-ElectronDist {
         param([string]$InstallDir)
         $electronDir = Get-ElectronDir -InstallDir $InstallDir
    ```

    ```diff
    @@ -2186,22 +2180,7 @@ function Test-ElectronDist {
         return (Test-Path -LiteralPath $distExe)
     }
     
    -# (Re)populate the desktop Electron dist via electron's own downloader.
    -#
    -# Since #38673 the desktop build pins build.electronDist, so electron-builder
    -# reads the Electron binary straight from there and never downloads it during
    -# `npm run pack`. That dist tree is produced by the electron package's
    -# postinstall (install.js) during `npm ci`. When that download is
    -# blocked/throttled (GitHub's release host is unreachable in some regions -
    -# #47266), dist is missing and re-running pack only re-throws "The specified
    -# electronDist does not exist". The mirror fallback therefore has to drive THIS
    -# downloader, not another pack.
    -#
    -# No-op (returns $true) when the dist binary is already present. Otherwise drops a
    -# partial dist + version marker (electron's install.js short-circuits when
    -# path.txt already matches) and runs the downloader once, optionally via a
    -# mirror. Best-effort: never throws. Returns $true iff the dist binary exists
    -# afterward.
    +# Best-effort: run electron/install.js to populate dist/ (optional mirror).
     function Restore-ElectronDist {
         param([string]$InstallDir, [string]$Mirror)
         if (Test-ElectronDist -InstallDir $InstallDir) { return $true }
    ```

    ```diff
    @@ -2234,6 +2213,23 @@ function Restore-ElectronDist {
         return (Test-Path -LiteralPath $distExe)
     }
     
    +function Test-ElectronPkgStagedMissingDist {
    +    param([string]$InstallDir)
    +    $electronDir = Get-ElectronDir -InstallDir $InstallDir
    +    return (
    +        (Test-Path -LiteralPath (Join-Path $electronDir 'package.json')) -and
    +        (Test-Path -LiteralPath (Join-Path $electronDir 'install.js')) -and
    +        (-not (Test-ElectronDist -InstallDir $InstallDir))
    +    )
    +}
    +
    +function Try-RestoreElectronDist {
    +    param([string]$InstallDir)
    +    if (Restore-ElectronDist -InstallDir $InstallDir) { return $true }
    +    if ($env:ELECTRON_MIRROR) { return $false }
    +    return Restore-ElectronDist -InstallDir $InstallDir -Mirror $script:DesktopElectronFallbackMirror
    +}
    +
     function Install-Desktop {
         # Build apps/desktop into a launchable Hermes.exe. Only called from
         # Stage-Desktop, which is itself only included in the manifest when
    ```

    ```diff
    @@ -2329,10 +2325,16 @@ function Install-Desktop {
             }
             $ErrorActionPreference = $prevEAP
             if ($code -ne 0) {
    -            Show-NpmCertHint ($npmOut -join "`n") | Out-Null
    -            throw "desktop workspace npm install failed (exit $code) -- see lines above for cause"
    +            if (Test-ElectronPkgStagedMissingDist -InstallDir $InstallDir) {
    +                Write-Warn "Desktop dependency install failed with a missing Electron dist; attempting self-heal..."
    +                Try-RestoreElectronDist -InstallDir $InstallDir | Out-Null
    +            } else {
    +                Show-NpmCertHint ($npmOut -join "`n") | Out-Null
    +                throw "desktop workspace npm install failed (exit $code) -- see lines above for cause"
    +            }
    +        } else {
    +            Write-Success "Desktop workspace dependencies installed"
             }
    -        Write-Success "Desktop workspace dependencies installed"
         } catch {
             if ($prevEAP) { $ErrorActionPreference = $prevEAP }
             Pop-Location
    ```

    ```diff
    @@ -2375,57 +2377,34 @@ function Install-Desktop {
             & $npmExe run pack 2>&1 | ForEach-Object { "$_" } | Tee-Object -FilePath $buildLog
             $code = $LASTEXITCODE
             if ($code -ne 0) {
    -            # A corrupt cached Electron zip makes `pack` fail with an opaque
    -            # ENOENT on the final `electron` -> `Hermes` rename: app-builder's
    -            # unpack-electron extracted a partial tree (missing the binary) from
    -            # the bad zip, and re-running reuses the poisoned cache forever.
    -            # Purge the cached download + any stale unpacked output and retry
    -            # once; @electron/get re-downloads with its own SHASUM check. Without
    -            # this a corrupt download hard-fails the whole installer.
    -            $purged = @(Clear-ElectronBuildCache -DesktopDir $desktopDir)
    -            # electronDist is pinned to node_modules\electron\dist (#38673):
    -            # electron-builder reads the Electron binary from there and `pack`
    -            # never downloads it, so purging the cache + re-running pack can't by
    -            # itself repopulate a missing/partial dist. When the dist is actually
    -            # gone, re-run electron's own downloader so the retry has a binary to
    -            # read. Gated on the dist check so an unrelated build failure
    -            # (tsc/vite) doesn't trigger a pointless ~200MB refetch.
    +            $purged = @()
                 $restored = $false
                 if (-not (Test-ElectronDist -InstallDir $InstallDir)) {
    +                $purged = @(Clear-ElectronBuildCache -DesktopDir $desktopDir)
                     $restored = Restore-ElectronDist -InstallDir $InstallDir
                 }
    -            if ($purged.Count -gt 0 -or $restored) {
    +            if ($restored) {
                     Write-Warn "Desktop build failed - refreshed the Electron download, retrying once:"
                     foreach ($p in $purged) { Write-Info "  - $p" }
                     & $npmExe run pack 2>&1 | ForEach-Object { "$_" } | Tee-Object -FilePath $buildLog
                     $code = $LASTEXITCODE
                 }
             }
    -        # Still failing and the user hasn't pinned their own mirror: GitHub's
    -        # Electron release host is likely blocked/throttled (the repeating
    -        # "retrying" log). Retry once via npmmirror.com — the de-facto Electron
    -        # community mirror (Alibaba). @electron/get SHASUM-checks the download,
    -        # but the SHASUMS come from the same mirror, so that guards against a
    -        # corrupt/partial download, NOT a compromised mirror: an explicit trust
    -        # trade-off we only make AFTER the canonical GitHub download has failed,
    -        # and we never override a user-pinned ELECTRON_MIRROR.
             if ($code -ne 0 -and -not $env:ELECTRON_MIRROR) {
    -            $mirror = "https://npmmirror.com/mirrors/electron/"
    +            $mirror = $script:DesktopElectronFallbackMirror
                 Write-Warn "Desktop build still failing - the Electron download from GitHub looks blocked."
                 Write-Warn "Re-downloading Electron via a public mirror ($mirror), then rebuilding:"
                 Write-Info "  (set ELECTRON_MIRROR yourself to use a different/trusted mirror)"
    -            # electronDist is pinned (#38673), so `npm run pack` never downloads
    -            # Electron - the mirror only helps if it drives electron's own
    -            # downloader. Re-fetch the binary through the mirror first; otherwise
    -            # the retry just re-reads the same missing dist and re-throws
    -            # "The specified electronDist does not exist" (#47266).
    -            $haveDist = Test-ElectronDist -InstallDir $InstallDir
    -            if (-not $haveDist) { $haveDist = Restore-ElectronDist -InstallDir $InstallDir -Mirror $mirror }
    -            if ($haveDist) {
    +            if (-not (Test-ElectronDist -InstallDir $InstallDir)) {
    +                Restore-ElectronDist -InstallDir $InstallDir -Mirror $mirror | Out-Null
    +            }
    +            $prevMirror = $env:ELECTRON_MIRROR
    +            $env:ELECTRON_MIRROR = $mirror
    +            try {
                     & $npmExe run pack 2>&1 | ForEach-Object { "$_" } | Tee-Object -FilePath $buildLog
                     $code = $LASTEXITCODE
    -            } else {
    -                Write-Warn "Could not re-download Electron from the mirror (node_modules\electron\dist still missing)"
    +            } finally {
    +                $env:ELECTRON_MIRROR = $prevMirror
                 }
             }
             $ErrorActionPreference = $prevEAP
    ```
  - `scripts/install.sh`

    ```diff
    @@ -2398,21 +2398,10 @@ _desktop_pack() {
         fi
     }
     
    -# Public Electron mirror used as a last-resort fallback when GitHub's release
    -# host is blocked/throttled (the repeating "retrying" symptom). npmmirror.com is
    -# the de-facto Electron community mirror (Alibaba). @electron/get SHASUM-checks
    -# the download, but the SHASUMS come from the same mirror — that guards against a
    -# corrupt/partial download, NOT a compromised mirror. Reaching for it is an
    -# explicit trust trade-off we only make AFTER the canonical GitHub download has
    -# failed, and we never override a user-pinned ELECTRON_MIRROR.
    +# Last-resort Electron mirror after GitHub download fails (#47266).
     DESKTOP_ELECTRON_FALLBACK_MIRROR="https://npmmirror.com/mirrors/electron/"
     
    -# Return the Electron package directory the desktop workspace installs. npm may
    -# nest workspace-only dev dependencies under apps/desktop/node_modules instead
    -# of hoisting them to the repo root; which layout you get depends on the npm
    -# version and what else is installed. apps/desktop/package.json points
    -# electron-builder's electronDist there, so prefer the workspace-local package
    -# and fall back to the root hoist. $1 = the workspace root holding node_modules.
    +# Electron package dir — workspace-local nest first, then root hoist.
     _electron_dir() {
         local install_dir="$1"
         if [ -d "$install_dir/apps/desktop/node_modules/electron" ]; then
    ```

    ```diff
    @@ -2422,12 +2411,7 @@ _electron_dir() {
         fi
     }
     
    -# True (returns 0) when the desktop workspace electronDist holds a usable
    -# Electron binary. electron-builder reads the binary from build.electronDist
    -# since #38673, so this is the exact file whose absence makes a pack fail with
    -# "The specified electronDist does not exist". A dist dir that exists but is
    -# missing the binary (partial extraction / aborted postinstall) is NOT ok.
    -# $1 = the workspace root holding node_modules.
    +# True when dist/ holds a usable Electron binary (#38673 / run-electron-builder.cjs).
     _electron_dist_ok() {
         local install_dir="$1"
         local electron_dir
    ```

    ```diff
    @@ -2439,22 +2423,7 @@ _electron_dist_ok() {
         fi
     }
     
    -# (Re)populate the desktop Electron dist via electron's own downloader.
    -#
    -# Since #38673 the desktop build pins build.electronDist, so electron-builder
    -# reads the Electron binary straight from there and never downloads it during
    -# `npm run pack`. That dist tree is produced by the electron package's
    -# postinstall (install.js) during `npm ci`. When that download is
    -# blocked/throttled (GitHub's release host is unreachable in some regions -
    -# #47266), dist is missing and re-running pack only re-throws "The specified
    -# electronDist does not exist". The mirror fallback therefore has to drive THIS
    -# downloader, not another pack.
    -#
    -# No-op (returns 0) when the dist binary is already present. Otherwise drops a
    -# partial dist + version marker (electron's install.js short-circuits when
    -# path.txt already matches) and runs the downloader once. $1 = the workspace root
    -# holding node_modules; optional $2 = an ELECTRON_MIRROR base URL. Best-effort:
    -# returns 0 iff the dist binary exists afterward.
    +# Best-effort: run electron/install.js to populate dist/ (optional mirror).
     _restore_electron_dist() {
         local install_dir="$1"
         local mirror="${2:-}"
    ```

    ```diff
    @@ -2476,6 +2445,19 @@ _restore_electron_dist() {
         _electron_dist_ok "$install_dir"
     }
     
    +_electron_pkg_staged_missing_dist() {
    +    local install_dir="$1"
    +    local electron_dir
    +    electron_dir="$(_electron_dir "$install_dir")"
    +    [ -f "$electron_dir/package.json" ] && [ -f "$electron_dir/install.js" ] && ! _electron_dist_ok "$install_dir"
    +}
    +
    +_restore_electron_dist_with_fallback() {
    +    local install_dir="$1"
    +    _restore_electron_dist "$install_dir" \
    +        || { [ -z "${ELECTRON_MIRROR:-}" ] && _restore_electron_dist "$install_dir" "$DESKTOP_ELECTRON_FALLBACK_MIRROR"; }
    +}
    +
     # Build apps/desktop into a launchable native app. Mirrors install.ps1's
     # Install-Desktop: a root-level npm install so the apps/* workspace resolves
     # the desktop's own deps (Electron ~150MB), then `npm run pack`
    ```

    ```diff
    @@ -2517,7 +2499,12 @@ install_desktop() {
         #    `tsc -b` failing with no obvious cause. Fall back to `npm install`
         #    only if `npm ci` is unavailable or the lockfile is out of sync.
         log_info "Installing desktop workspace dependencies (includes Electron ~150MB, 1-3min)..."
    -    ( cd "$INSTALL_DIR" && npm ci ) || ( cd "$INSTALL_DIR" && npm install ) || {
    +    if ( cd "$INSTALL_DIR" && npm ci ) || ( cd "$INSTALL_DIR" && npm install ); then
    +        log_success "Desktop workspace dependencies installed"
    +    elif _electron_pkg_staged_missing_dist "$INSTALL_DIR"; then
    +        log_warn "Desktop dependency install failed with a missing Electron dist; attempting self-heal..."
    +        _restore_electron_dist_with_fallback "$INSTALL_DIR" || true
    +    else
             log_error "Desktop workspace npm install failed"
             # Common cause: a previous 'sudo npm'/'sudo npx' left root-owned files in
             # ~/.npm, so this non-root install can't write the shared cache. npm hides
    ```

    ```diff
    @@ -2530,8 +2517,7 @@ install_desktop() {
             log_info "Then re-run this installer, or build manually:"
             log_info "  cd \"$INSTALL_DIR\" && npm ci && cd apps/desktop && npm run pack"
             return 1
    -    }
    -    log_success "Desktop workspace dependencies installed"
    +    fi
     
         # 2. Build, with up to three escalating attempts so a transient/blocked
         #    Electron download self-heals instead of failing the whole install:
    ```

    ```diff
    @@ -2545,21 +2531,13 @@ install_desktop() {
         if _desktop_pack "$desktop_dir"; then
             pack_ok=true
         else
    -        # (b) Corrupt cached Electron zip is the most common self-healable cause.
    -        local purged
    -        purged="$(clear_electron_build_cache "$desktop_dir")"
    -        # electronDist is pinned to node_modules/electron/dist (#38673):
    -        # electron-builder reads the binary from there and `pack` never downloads
    -        # it, so purging the cache + re-running pack can't by itself repopulate a
    -        # missing/partial dist. When the dist is actually gone, re-run electron's
    -        # own downloader so the retry has a binary to read. Gated on the dist
    -        # check so an unrelated build failure (tsc/vite) doesn't trigger a
    -        # pointless ~200MB refetch.
    +        local purged=""
             local restored=false
             if ! _electron_dist_ok "$INSTALL_DIR"; then
    +            purged="$(clear_electron_build_cache "$desktop_dir")"
                 if _restore_electron_dist "$INSTALL_DIR"; then restored=true; fi
             fi
    -        if [ -n "$purged" ] || [ "$restored" = true ]; then
    +        if [ "$restored" = true ]; then
                 log_warn "Desktop build failed; refreshed the Electron download and retrying once..."
                 if _desktop_pack "$desktop_dir"; then
                     pack_ok=true
    ```

    ```diff
    @@ -2567,27 +2545,14 @@ install_desktop() {
             fi
         fi
     
    -    # (c) Still failing and the user hasn't pinned their own mirror: the GitHub
    -    #     release host is likely blocked/throttled. Re-download the Electron
    -    #     binary via a public mirror, then retry. The mirror MUST drive
    -    #     electron's own downloader — `npm run pack` reads the pinned electronDist
    -    #     and never downloads, so a mirror passed only to pack is a no-op (#47266).
    +    # (c) GitHub blocked → mirror fallback (#47266).
         if [ "$pack_ok" = false ] && [ -z "${ELECTRON_MIRROR:-}" ]; then
             log_warn "Desktop build still failing — the Electron download from GitHub looks blocked."
             log_warn "Re-downloading Electron via a public mirror ($DESKTOP_ELECTRON_FALLBACK_MIRROR), then rebuilding..."
             log_warn "  (set ELECTRON_MIRROR yourself to use a different/trusted mirror)"
    -        local have_dist=false
    -        if _electron_dist_ok "$INSTALL_DIR"; then
    -            have_dist=true
    -        elif _restore_electron_dist "$INSTALL_DIR" "$DESKTOP_ELECTRON_FALLBACK_MIRROR"; then
    -            have_dist=true
    -        fi
    -        if [ "$have_dist" = true ]; then
    -            if _desktop_pack "$desktop_dir" "$DESKTOP_ELECTRON_FALLBACK_MIRROR"; then
    -                pack_ok=true
    -            fi
    -        else
    -            log_warn "Could not re-download Electron from the mirror (node_modules/electron/dist still missing)"
    +        _electron_dist_ok "$INSTALL_DIR" || _restore_electron_dist "$INSTALL_DIR" "$DESKTOP_ELECTRON_FALLBACK_MIRROR" || true
    +        if _desktop_pack "$desktop_dir" "$DESKTOP_ELECTRON_FALLBACK_MIRROR"; then
    +            pack_ok=true
             fi
         fi
     
    ```
  - `tests/hermes_cli/test_gui_command.py`

    ```diff
    @@ -485,13 +485,15 @@ def test_gui_retries_pack_once_after_purging_build_cache(tmp_path, monkeypatch):
              patch("hermes_cli.main._desktop_linux_sandbox_fixup", return_value=True), \
              patch("hermes_cli.main._write_desktop_build_stamp"), \
              patch("hermes_cli.main._purge_electron_build_cache", return_value=[Path("/c/electron.zip")]) as mock_purge, \
    +         patch("hermes_cli.main._electron_dist_ok", return_value=False), \
    +         patch("hermes_cli.main._redownload_electron_dist", return_value=True), \
              patch("hermes_cli.main.subprocess.run", side_effect=[pack_fail, pack_ok, launch_ok]) as mock_run, \
              pytest.raises(SystemExit) as exc:
             cli_main.cmd_gui(_ns())
     
         assert exc.value.code == 0
         mock_purge.assert_called_once()
    -    # pack(fail) → purge → pack(ok) → launch = 3 subprocess.run calls
    +    # pack(fail) → repair succeeds → pack(ok) → launch = 3 subprocess.run calls
         assert mock_run.call_count == 3
         assert mock_run.call_args_list[0].args[0] == ["/usr/bin/npm", "run", "pack"]
         assert mock_run.call_args_list[1].args[0] == ["/usr/bin/npm", "run", "pack"]
    ```

    ```diff
    @@ -535,10 +537,12 @@ def test_gui_redownloads_electron_via_mirror_then_repacks(tmp_path, monkeypatch,
         assert "Desktop GUI build failed" in capsys.readouterr().out
     
     
    -def test_gui_skips_pack_when_electron_redownload_unrecoverable(tmp_path, monkeypatch, capsys):
    -    """When the Electron binary can't be fetched at all (mirror also blocked),
    -    skip the pointless final pack — it would just re-throw the same missing
    -    electronDist — and fail with a clear message instead."""
    +def test_gui_retries_pack_under_mirror_even_when_prefetch_blocked(tmp_path, monkeypatch, capsys):
    +    """When electron's own downloader can't fetch the binary (even via the
    +    mirror), still retry pack under ELECTRON_MIRROR: the build resolves
    +    electronDist dynamically and lets electron-builder fetch Electron itself
    +    via @electron/get, which honors the mirror. That retry is no longer
    +    pointless (it was, back when electronDist was a static path)."""
         root = _make_desktop_tree(tmp_path)
         monkeypatch.setattr(cli_main, "PROJECT_ROOT", root)
         _make_packaged_executable(root, monkeypatch, platform="linux")
    ```

    ```diff
    @@ -553,17 +557,96 @@ def test_gui_skips_pack_when_electron_redownload_unrecoverable(tmp_path, monkeyp
              patch("hermes_cli.main._purge_electron_build_cache", return_value=[]), \
              patch("hermes_cli.main._electron_dist_ok", return_value=False), \
              patch("hermes_cli.main._redownload_electron_dist", return_value=False), \
    -         patch("hermes_cli.main.subprocess.run", side_effect=[pack_fail]) as mock_run, \
    +         patch("hermes_cli.main.subprocess.run", side_effect=[pack_fail, pack_fail]) as mock_run, \
              pytest.raises(SystemExit) as exc:
             cli_main.cmd_gui(_ns())
     
         assert exc.value.code == 1
    -    # Only the initial pack ran; both retries were skipped because no binary
    -    # could be produced.
    -    assert mock_run.call_count == 1
    -    out = capsys.readouterr().out
    -    assert "Could not re-download Electron from the mirror" in out
    -    assert "Desktop GUI build failed" in out
    +    # Initial pack + mirror-driven pack = 2; the mirror retry runs even though
    +    # the pre-fetch failed, so electron-builder gets a shot at downloading.
    +    assert mock_run.call_count == 2
    +    assert "ELECTRON_MIRROR" not in (mock_run.call_args_list[0].kwargs.get("env") or {})
    +    assert mock_run.call_args_list[1].kwargs["env"]["ELECTRON_MIRROR"]
    +    assert "Desktop GUI build failed" in capsys.readouterr().out
    +
    +
    +def test_gui_install_failure_self_heals_electron_and_continues(tmp_path, monkeypatch, capsys):
    +    """npm ci failing on electron's blocked binary download must NOT abort the
    +    install: with the electron package staged, repopulate its dist and continue
    +    to the build instead of sys.exit-ing before pack ever runs (#47266/#48021)."""
    +    root = _make_desktop_tree(tmp_path)
    +    monkeypatch.setattr(cli_main, "PROJECT_ROOT", root)
    +    packaged_exe = _make_packaged_executable(root, monkeypatch, platform="linux")
    +    # electron package staged on disk (postinstall download was the casualty).
    +    (root / "apps" / "desktop" / "node_modules" / "electron").mkdir(parents=True)
    +    (root / "apps" / "desktop" / "node_modules" / "electron" / "package.json").write_text("{}", encoding="utf-8")
    +    (root / "apps" / "desktop" / "node_modules" / "electron" / "install.js").write_text("", encoding="utf-8")
    +
    +    install_fail = subprocess.CompletedProcess(["npm", "ci"], 1)
    +    pack_ok = subprocess.CompletedProcess(["npm", "run", "pack"], 0)
    +    launch_ok = subprocess.CompletedProcess([str(packaged_exe)], 0)
    +
    +    with patch("hermes_cli.main.shutil.which", return_value="/usr/bin/npm"), \
    +         patch("hermes_cli.main._run_npm_install_deterministic", return_value=install_fail), \
    +         patch("hermes_cli.main._desktop_linux_sandbox_fixup", return_value=True), \
    +         patch("hermes_cli.main._write_desktop_build_stamp"), \
    +         patch("hermes_cli.main._electron_dist_ok", return_value=False), \
    +         patch("hermes_cli.main._try_redownload_electron_dist", return_value=True) as mock_dl, \
    +         patch("hermes_cli.main.subprocess.run", side_effect=[pack_ok, launch_ok]) as mock_run, \
    +         pytest.raises(SystemExit) as exc:
    +        cli_main.cmd_gui(_ns())
    +
    +    assert exc.value.code == 0
    +    mock_dl.assert_called()  # tried to repopulate the dist
    +    # pack + launch ran — the install failure did NOT abort the build.
    +    assert mock_run.call_count == 2
    +    assert "repopulated" in capsys.readouterr().out.lower()
    +
    +
    +def test_gui_install_failure_hard_fails_when_electron_not_staged(tmp_path, monkeypatch, capsys):
    +    """A dependency-install failure where electron never even staged is a genuine
    +    error (not a blocked binary download) — hard-fail with guidance, don't try to
    +    self-heal a tree that isn't there."""
    +    root = _make_desktop_tree(tmp_path)
    +    monkeypatch.setattr(cli_main, "PROJECT_ROOT", root)
    +    _make_packaged_executable(root, monkeypatch, platform="linux")
    +
    +    install_fail = subprocess.CompletedProcess(["npm", "ci"], 1)
    +
    +    with patch("hermes_cli.main.shutil.which", return_value="/usr/bin/npm"), \
    +         patch("hermes_cli.main._run_npm_install_deterministic", return_value=install_fail), \
    +         patch("hermes_cli.main.subprocess.run") as mock_run, \
    +         pytest.raises(SystemExit) as exc:
    +        cli_main.cmd_gui(_ns())
    +
    +    assert exc.value.code == 1
    +    mock_run.assert_not_called()  # build never started
    +    assert "Desktop dependency install failed" in capsys.readouterr().out
    +
    +
    +def test_gui_install_failure_hard_fails_when_electron_dist_exists(tmp_path, monkeypatch, capsys):
    +    """If npm install fails but Electron dist is already present, don't classify
    +    it as the blocked-download shape; fail fast as a generic install error."""
    +    root = _make_desktop_tree(tmp_path)
    +    monkeypatch.setattr(cli_main, "PROJECT_ROOT", root)
    +    _make_packaged_executable(root, monkeypatch, platform="linux")
    +    electron_dir = root / "apps" / "desktop" / "node_modules" / "electron"
    +    electron_dir.mkdir(parents=True)
    +    (electron_dir / "package.json").write_text("{}", encoding="utf-8")
    +    (electron_dir / "install.js").write_text("", encoding="utf-8")
    +
    +    install_fail = subprocess.CompletedProcess(["npm", "ci"], 1)
    +
    +    with patch("hermes_cli.main.shutil.which", return_value="/usr/bin/npm"), \
    +         patch("hermes_cli.main._run_npm_install_deterministic", return_value=install_fail), \
    +         patch("hermes_cli.main._electron_dist_ok", return_value=True), \
    +         patch("hermes_cli.main.subprocess.run") as mock_run, \
    +         pytest.raises(SystemExit) as exc:
    +        cli_main.cmd_gui(_ns())
    +
    +    assert exc.value.code == 1
    +    mock_run.assert_not_called()
    +    assert "Desktop dependency install failed" in capsys.readouterr().out
     
     
     def test_gui_does_not_override_user_electron_mirror(tmp_path, monkeypatch, capsys):
    ```
  - `tests/test_desktop_electron_pin.py`

    ```diff
    @@ -96,40 +96,40 @@ def test_lockfile_resolves_the_pinned_electron():
         )
     
     
    -def test_electron_dist_matches_lockfile_install_location():
    -    """build.electronDist must point at where the lockfile installs Electron.
    -
    -    electron-builder copies the unpacked Electron from ``build.electronDist``
    -    (resolved relative to ``apps/desktop``). npm workspace hoisting is not
    -    deterministic across machines/npm versions: it may nest Electron under
    -    ``apps/desktop/node_modules/electron`` or hoist it to the repo root. If
    -    electronDist points at one location while the lockfile installs at the
    -    other, packaging fails with ``The specified electronDist does not exist`` —
    -    the "Building desktop app" failure reported after the June lockfile
    -    regeneration floated Electron and reshuffled the hoist. Lock the two
    -    together so a hoist change (root <-> nested) can't silently break the path
    -    again.
    -    """
    -    if not ROOT_LOCK.is_file():
    -        pytest.skip("root package-lock.json not present")
    -    electron_dist = _desktop_pkg().get("build", {}).get("electronDist")
    -    assert electron_dist, "build.electronDist is missing"
    +DESKTOP_DIR = REPO_ROOT / "apps" / "desktop"
    +ELECTRON_BUILDER_WRAPPER = DESKTOP_DIR / "scripts" / "run-electron-builder.cjs"
     
    -    lock = json.loads(ROOT_LOCK.read_text(encoding="utf-8"))
    -    electron_paths = [
    -        path
    -        for path in lock.get("packages", {})
    -        if path.endswith("node_modules/electron")
    -    ]
    -    assert electron_paths, "no electron entry found in package-lock.json"
    -
    -    desktop_dir = REPO_ROOT / "apps" / "desktop"
    -    # electronDist is resolved relative to the apps/desktop project dir.
    -    configured = (desktop_dir / electron_dist).resolve()
    -    # Where the lockfile actually places Electron's unpacked dist.
    -    installed = {(REPO_ROOT / p / "dist").resolve() for p in electron_paths}
    -    assert configured in installed, (
    -        f"build.electronDist={electron_dist!r} resolves to {configured}, but the "
    -        f"lockfile installs Electron at {sorted(str(p) for p in installed)}. "
    -        "electron-builder will fail with 'electronDist does not exist'."
    +
    +def test_no_static_electron_dist_that_can_drift():
    +    """build.electronDist must not be a static path — hoisting is non-deterministic."""
    +    assert "electronDist" not in _desktop_pkg().get("build", {}), (
    +        "build.electronDist is hardcoded again. npm hoisting is non-deterministic, "
    +        "so a static path silently breaks packaging when the layout changes. Let "
    +        "scripts/run-electron-builder.cjs resolve it dynamically instead."
    +    )
    +
    +
    +def test_builder_script_routes_through_dynamic_resolver():
    +    """npm run builder must invoke run-electron-builder.cjs, not bare electron-builder."""
    +    builder = _desktop_pkg().get("scripts", {}).get("builder", "")
    +    assert "run-electron-builder.cjs" in builder, (
    +        f"the 'builder' script must run scripts/run-electron-builder.cjs, got "
    +        f"{builder!r}"
    +    )
    +    assert ELECTRON_BUILDER_WRAPPER.is_file(), (
    +        f"missing dynamic-resolver wrapper at {ELECTRON_BUILDER_WRAPPER}"
    +    )
    +
    +
    +def test_resolver_uses_node_module_resolution():
    +    """Wrapper must resolve electron via require.resolve and pass -c.electronDist."""
    +    src = ELECTRON_BUILDER_WRAPPER.read_text(encoding="utf-8")
    +    assert 'require.resolve("electron/package.json")' in src, (
    +        "run-electron-builder.cjs must resolve electron via "
    +        "require.resolve('electron/package.json') to stay hoist-proof."
    +    )
    +    # And it must hand the resolved dist to electron-builder as an override.
    +    assert "-c.electronDist=" in src, (
    +        "run-electron-builder.cjs must pass the resolved dist to electron-builder "
    +        "via -c.electronDist."
         )
    ```
- **#49152: fix(desktop): pre-fetch Electron dist after clean install on update (#47917)**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `#48091`, `#47917`, `#48091`

## Fix status

- Merged to main in PR #47276 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-06-17`.
- Subsystem: `apps/bootstrap-installer`
