Skip to content

fix(update): tree-aware orphan reap + drain Desktop update trees without pre-signalling (follow-up to #82179) - #82191

Merged
teknium1 merged 1 commit into
mainfrom
fix/orphan-classifier-tree-aware
Aug 9, 2026
Merged

fix(update): tree-aware orphan reap + drain Desktop update trees without pre-signalling (follow-up to #82179)#82191
teknium1 merged 1 commit into
mainfrom
fix/orphan-classifier-tree-aware

Conversation

@teknium1

@teknium1 teknium1 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Follow-up to #82179, implementing all three asks from @helix4u's review comment (which landed as the PR was merging):

  1. Desktop teardown fix (ported from fix(desktop): drain Windows update process trees #77436, credit @4adwentures): releaseBackendLock() sent SIGTERM to the primary backend before forceKillProcessTree(). If that launcher exits before taskkill /T runs, Windows can no longer enumerate its descendants and they survive holding the venv — i.e. the owning Electron path could still create the orphan that fix(update): reap orphaned Desktop backends + stop managed runtime child trees (salvages #81327) #82179 repairs. New stopBackendTreesForUpdate() in backend-child.ts tree-kills the live root first (no pre-signal), pool teardown unchanged. Includes fix(desktop): drain Windows update process trees #77436's behavioral vitest. Per the review, fix(desktop): drain Windows update process trees #77436's _scan_venv_blockers.py changes are not taken — that area moved through fix(update): venv-blocker scan truncated cmdlines, breaking the gateway exemption and dead-ending Desktop updates #82158 (they no longer apply cleanly).

  2. Tree-aware orphan classification: _orphaned_desktop_backend_pids() refused the whole set when any holder had a live parent. But the scanner legitimately returns an orphaned serve root and its descendants — including the uv-managed interpreter worker, which re-execs with the same backend argv and whose live parent is the orphan root itself. Holders inside an accepted orphan root's tree now fold into that root (only roots are returned; taskkill /T reaps the descendants). Live-parent backends defer to the ancestry check instead of refusing outright; anything outside an orphan tree still refuses.

  3. Coverage for the mixed shapes: root + managed-runtime child, grandchild depth, non-descendant stray alongside an orphan root (still refuses), descendant exited mid-classify.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

How to Test

  • Python: pytest tests/hermes_cli/test_update_orphan_backend_reap.py tests/hermes_cli/test_update_venv_health.py -o "addopts=--timeout-method=thread" — 21 passed.
  • Desktop: npm run typecheck (3 projects), npx vitest run electron/windows-child-options.test.ts — 13 passed incl. the no-pre-signal ordering test, npx eslint clean on touched files.

E2E verification (real Windows 11 box)

  • Spawned a detached backend-shaped orphan that itself spawned children (3 python descendants). Fed the scanner-shaped mixed holder set to the classifier → [root]; _stop_process_trees reaped root and all descendants (verified via psutil).
  • The live Desktop backend on the box (parent alive) → None, refusal preserved.
  • Notably, the first live run failed with the pre-fix classifier — the real trampoline/worker chain (worker has the same serve argv + a live parent) is invisible to mocks. That live failure is exactly the case part 2 fixes.

Checklist

  • Conventional commits; no unrelated changes (fix(desktop): drain Windows update process trees #77436's stale scanner half deliberately excluded)
  • Tests added; tested on Windows 11 with real process trees
  • Cross-platform: Python guard Windows-only via _is_windows(); stopBackendTreesForUpdate only reached from the Windows shim-unlock path
  • Contributor credit: Co-Authored-By @4adwentures (noreply email — no AUTHOR_MAP entry needed)

Supersedes the desktop-teardown portion of #77436; the scanner portion is superseded by #82158.

…te trees without pre-signalling

Follow-up to #82179 addressing helix4u's review comment
(#82179 issuecomment-5229441571). Three parts:

1. Desktop teardown (salvaged from #77436, @4adwentures): the update
   hand-off's releaseBackendLock() sent SIGTERM to the primary backend
   BEFORE taskkill /T. If the launcher exits first, Windows can no longer
   enumerate its descendants and they survive holding the venv — the
   Electron path that creates the orphan #82179 then has to repair.
   New stopBackendTreesForUpdate() tree-kills the live root first, with
   the behavioral vitest from #77436. The scanner half of #77436 is
   deliberately NOT taken (superseded by #82158's full-cmdline scan).

2. Tree-aware orphan classification: _orphaned_desktop_backend_pids()
   previously refused the whole holder set when any holder had a live
   parent. But the scanner legitimately returns an orphaned serve root
   AND its descendants (the venv trampoline's uv-managed interpreter
   worker — which carries the same backend argv — plus .hermes-runtime
   children). Those have a live parent: the orphan root itself. Now
   holders inside an accepted orphan root's tree fold into that root
   (only roots are returned; taskkill /T reaps descendants), and
   live-parent backends defer to the ancestry check instead of refusing
   outright. Anything outside an orphan tree still refuses.

3. Tests for the mixed shapes: root+managed-runtime child,
   grandchild depth, non-descendant stray alongside an orphan root
   (still refuses), descendant exited mid-classify.

E2E on a real Windows box: spawned a detached backend-shaped orphan
that itself spawned children (3 python descendants); the scanner-shaped
mixed holder set classified to [root], taskkill /T reaped root and all
descendants. The live Desktop backend on the box still classified None
(refusal preserved). The first E2E attempt caught exactly the
trampoline/worker case the mocks missed — the live worker re-execs with
the same backend argv and a live parent — which is what part 2 fixes.

Co-Authored-By: 4adwentures <296413879+4adwentures@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on c982912

⚠️ Warnings

OSV vulnerability scan · View job

21 known vulnerabilities found in pinned dependencies.

How to fix:

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


debug info

CI timings

CI timings · View report · View job

Wall time 6m24s vs 5m19s (+20.4%). 18 job(s) slower, 16 faster,

  • JS & TS checks / apps/desktop / check:test:ui: +83.0s
  • Python tests / Run tests slice 8/12: +34.0s
  • Python tests / Run tests slice 7/12: -26.0s
  • Python tests / Run tests slice 2/12: +20.0s
  • Python tests / Run tests slice 11/12: +17.0s

@helix4u

helix4u commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Thanks, this addresses the Electron teardown ordering and the mixed root/managed-runtime descendant case from my review. The source-side tree kill, downstream orphan recovery, tests, and #77436 attribution all look good.

One correction to my previous comment: I was too broad in saying #82158 superseded the scanner portion of #77436. #82158 fixed truncated command lines for a gateway process whose own argv contains gateway run. #77436 also handles a separate case where a detected venv holder is a descendant of a pausable gateway but its own argv does not identify it as one. Current _scan_venv_blockers.py still checks only each holder's own command line, so that descendant can block Desktop preflight before the downstream gateway pause can stop the tree.

I do not see a blocker in the Electron or orphan-classifier changes here. For the scanner portion, either port _has_pausable_gateway_ancestor() and its fail-closed tests onto current main in this PR, or leave that portion of #77436 explicitly unsuperseded for a focused follow-up. The PR description should not say #82158 already superseded it.

@teknium1
teknium1 merged commit da3a0a8 into main Aug 9, 2026
53 checks passed
@teknium1
teknium1 deleted the fix/orphan-classifier-tree-aware branch August 9, 2026 03:01
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) comp/cli CLI entry point, hermes_cli/, setup wizard platform/windows Native Windows-specific behavior or breakage area/install-update Installer, updater, packaging, wheels, doctor sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 9, 2026
@rdxhemadri

Copy link
Copy Markdown

@teknium1 can you pls merge this #75038

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/install-update Installer, updater, packaging, wheels, doctor comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) P2 Medium — degraded but workaround exists platform/windows Native Windows-specific behavior or breakage sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants