Skip to content

fix(desktop): reuse existing source installs - #37471

Open
AJV20 wants to merge 5 commits into
NousResearch:mainfrom
AJV20:fix/desktop-existing-install
Open

fix(desktop): reuse existing source installs#37471
AJV20 wants to merge 5 commits into
NousResearch:mainfrom
AJV20:fix/desktop-existing-install

Conversation

@AJV20

@AJV20 AJV20 commented Jun 2, 2026

Copy link
Copy Markdown

Summary

  • Reuse a valid existing Hermes source checkout and venv even when the Desktop bootstrap marker is absent.
  • Keep CLI-first installs unclaimed by Desktop: the app uses the runtime but does not write a bootstrap-complete marker.
  • Add a unit-tested resolver helper covering working installs, missing roots/venvs, and broken venv imports.

Test Plan

  • npm run test:desktop:platforms
  • npx --no-install eslint electron/runtime-resolver.cjs electron/runtime-resolver.test.cjs

Note: linting electron/main.cjs still reports existing unrelated violations in the current base, so the focused lint command targets the new resolver files.

@AJV20
AJV20 requested a review from a team June 2, 2026 15:29
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists labels Jun 2, 2026
austinpickett
austinpickett previously approved these changes Jun 9, 2026

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hermes Agent Review — ✅ Approve

Verified locally vs origin/main and ran the new test suite (3/3 pass).

The fix is correct and well-designed. It adds a dependency-injected runtime-resolver.cjs (hasUsableActiveInstall) and inserts it into resolveHermesBackend() as a new rung — between the bootstrap-complete check and the PATH-lookup rung — that reuses an existing CLI-first Hermes source checkout + venv without claiming ownership (no bootstrap marker written). This catches the documented Finder-launch case where ~/.local/bin isn't on PATH but the canonical checkout+venv exist.

The validation order in the helper is sound: activeRoot/venvRoot present → injected deps are functions → isHermesSourceRoot(activeRoot)getVenvPython(venvRoot) exists on disk → canImportHermesCli(venvPython) succeeds. Returns true only when an importable runtime is actually present. I confirmed all injected dependencies (ACTIVE_HERMES_ROOT, VENV_ROOT, getVenvPython, fileExists, isHermesSourceRoot, rememberLog, canImportHermesCli, createActiveBackend) exist on origin/main with matching signatures, and that canImportHermesCli correctly receives the venv python path (which is what it expects). Reuses the existing HERMES_DESKTOP_IGNORE_EXISTING !== '1' testing escape hatch, consistent with neighboring rungs.

node --test apps/desktop/electron/runtime-resolver.test.cjs  =>  3 pass, 0 fail

Tests are meaningful (happy path + two distinct rejection paths + asserts the diagnostic log line on unimportable hermes_cli). No out-of-scope hunks (exactly the 4 expected files, +104/-2), no new I/O or security surface — pure decision helper.

Cluster note (not a blocker): part of the desktop "extract a testable .cjs module + wire into main.cjs + register the test" family (#39554/#38292/#38589/#42901/#39522/#40558). Textual conflicts on main.cjs / the test:desktop:platforms line are expected; no semantic overlap. Trivial rebase for whichever lands later.

Reviewed by Hermes Agent (local node --test + origin/main verification).

@AJV20
AJV20 force-pushed the fix/desktop-existing-install branch from a7403fd to 983189d Compare June 9, 2026 17:53
austinpickett
austinpickett previously approved these changes Jun 11, 2026

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

PR #37471 — fix(desktop): reuse existing source installs
Author: @AJV20 | Priority: P2
Verdict: Approve ✅

Critical

None.

Warnings

  • No escape hatch in resolveHermesBackend: the new step 4 fires on every launch where HERMES_DESKTOP_IGNORE_EXISTING=1 is not set, ACTIVE_HERMES_ROOT is a valid source root, and the venv is importable. This is the correct behavior for the stated use-case, but it means a CLI-first user who later wants the Desktop to bootstrap its own isolated install cannot easily opt out at runtime (other than setting the env var). Documenting HERMES_DESKTOP_IGNORE_EXISTING=1 in the UI or a help page would be useful.
  • canImportHermesCli is an expensive probe (spawns a subprocess to run python -c "import hermes_cli"). The new step 4 calls it on every cold-start in the CLI-first path. Consider caching the result or documenting this cost for users with slow disk/antivirus on Windows.

Suggestions

  • Minor: hasUsableActiveInstall returns false when opts is nullish, but all callers in main.cjs always pass a full object. A JSDoc @param {object} opts annotation would clarify expectations and silence linting.
  • The log message "Using existing Hermes source install at ${ACTIVE_HERMES_ROOT} without desktop bootstrap marker." is good. Consider also logging at the same level when the probe is rejected (not just the rememberLog inside hasUsableActiveInstall) so users can diagnose why the fallback didn't trigger.

Looks Good

  • Helper design is clean: hasUsableActiveInstall in runtime-resolver.cjs is fully dependency-injected — no Electron globals, no filesystem calls, pure logic. Easy to test and reason about.
  • 3 unit tests cover all relevant branches: working source checkout with venv, missing root/venv, and failed hermes_cli import with log message verification.
  • Marker write is correctly omitted: the Desktop does not claim ownership of a CLI-first install — the comment makes the intent explicit.
  • Ordering in resolveHermesBackend is correct: the new step 4 sits between the existing bootstrap-complete marker check (step 3) and the PATH-based fallback (original step 4, now step 5), so it only fires when there is no Desktop-owned install already.
  • Existing override HERMES_DESKTOP_IGNORE_EXISTING=1 provides a clean escape hatch for test environments.
  • test:desktop:platforms updated to include runtime-resolver.test.cjs.

Reviewed by Hermes Agent

@austinpickett

Copy link
Copy Markdown
Collaborator

Nice work, @AJV20! Clean helper design and the test coverage is solid.

Two things worth considering before/after merge:

  1. canImportHermesCli call cost on every cold-start: spawning a Python process on every Desktop launch to check import hermes_cli may be noticeably slow on Windows with antivirus active. If the result can be cached (e.g. persisted to a resolverCache.json in userData, invalidated on version change), that would improve launch time for CLI-first users on slow machines.

  2. User-discoverable opt-out: HERMES_DESKTOP_IGNORE_EXISTING=1 is the escape hatch for users who want Desktop to bootstrap its own isolated install. This env var isn't surfaced anywhere in the UI or docs. A one-liner in Docs/Settings under a "troubleshooting" section would save a support thread.

Neither is a blocker — the fix is correct and the tests are thorough. ✅

@AJV20

AJV20 commented Jun 12, 2026

Copy link
Copy Markdown
Author

Updated this branch to address the review suggestions.

Changes:

  • cached the successful Desktop existing-runtime hermes_cli import probe in resolverCache.json, invalidated by Desktop app version, active root, and venv Python path
  • documented HERMES_DESKTOP_IGNORE_EXISTING=1 in Desktop troubleshooting as the opt-out / force-bootstrap escape hatch
  • added resolver-cache regression coverage, including app-version and install-path invalidation

Verification:

  • node --check apps/desktop/electron/runtime-resolver.cjs
  • node --check apps/desktop/electron/main.cjs
  • node --test apps/desktop/electron/runtime-resolver.test.cjs → 8 passed
  • git diff --check

Pushed head: 1b1cd23bf

…g-install

# Conflicts:
#	apps/desktop/electron/main.cjs
#	apps/desktop/package.json
@AJV20

AJV20 commented Jun 12, 2026

Copy link
Copy Markdown
Author

Final refresh pushed after latest main moved. Head is now 8e0a13acf; GitHub reports the PR is mergeable. Re-ran node --test apps/desktop/electron/runtime-resolver.test.cjs (8 passed), npm --prefix apps/desktop run test:desktop:platforms (173 passed, 1 skipped), node --check for touched Desktop files, and git diff --check.

…install

# Conflicts:
#	apps/desktop/package.json

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the focused resolver work. The unmarked CLI-first install premise still holds on current main: after a missing marker, apps/desktop/electron/main.ts:3392-3401 falls directly to PATH resolution.

Problems

  • The PR's runtime-resolver.cjs calls canImportHermesCli(venvPython) without the source-root PYTHONPATH (runtime-resolver.cjs:36). Current main requires that environment when validating and launching source-tree runtimes (apps/desktop/electron/main.ts:3098-3102, 3350-3354), so this can reject the intended valid install.
  • The persistent positive cache at runtime-resolver.cjs:32-34 can skip the fresh runtime-health probe after an in-place dependency/runtime failure. Current main added that fresh probe specifically to prevent broken-venv boot loops (commit 0229246ab879b9968c9fcc384d8d5d0add939771).
  • Current Desktop Electron sources are TypeScript (apps/desktop/electron/main.ts), so the CJS patch needs a targeted port.

Suggested changes

  • Reintroduce the unmarked-active-runtime rung in main.ts, preserving the existing source-root-aware probe and HERMES_DESKTOP_IGNORE_EXISTING gate.
  • Rework or omit the persisted probe cache unless it preserves the current runtime-health guarantee.

Automated hermes-sweeper review.

if (isResolverCacheHit({ activeRoot, appVersion, readResolverCache, venvPython })) {
return true
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please pass the active source root through the probe environment. Current main validates this runtime with PYTHONPATH containing ACTIVE_HERMES_ROOT (main.ts:3098-3102); without it, a valid source-tree venv that does not separately install hermes_cli will be rejected.


const venvPython = getVenvPython(venvRoot)
if (!venvPython || !fileExists(venvPython)) return false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This positive cache can survive an in-place venv/dependency regression because its key contains only Desktop version and paths. Current main deliberately performs a fresh import-health probe to avoid selecting broken venvs; preserve that guarantee or extend invalidation to cover runtime changes.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 13, 2026
# Conflicts:
#	apps/desktop/electron/main.ts
#	apps/desktop/package.json
@AJV20

AJV20 commented Jul 15, 2026

Copy link
Copy Markdown
Author

Maintenance verification complete at 28a48a2 (no branch churn).
Verified the TypeScript unmarked-source rung supplies ACTIVE_HERMES_ROOT on PYTHONPATH and performs a fresh import-health probe on every check; no positive probe cache remains.
Tests: npx vitest run electron/runtime-resolver.test.ts electron/backend-probes.test.ts -> 11 passed; npm run typecheck passed.
Fresh GitHub result: mergeable=true, mergeable_state=blocked (checks/reviews).

@teknium1 teknium1 added the area/install-update Installer, updater, packaging, wheels, doctor label Jul 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/install-update Installer, updater, packaging, wheels, doctor P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants