← Back to Electron and React foundation team

Skill · Version 1.0.0

Desktop process lifecycle

Manages app-owned processes and window transitions without terminating unrelated programs or depending on shell environments.

desktop-process-lifecycle

Team members using this skill

See how it helps

When is it useful?

Use when quitting leaves child processes behind or GUI launch cannot locate tools.

What does it help you do?

  • Tracks spawned processes and shuts down their owned process trees.
  • Resolves executable paths explicitly and passes arguments without shell interpolation.
  • Handles single-instance behavior, window lifecycle and sleep/wake recovery.

Try asking this

Fix an app-owned local server surviving quit, while preserving a separate server started by the user. Start both servers, quit the app and inspect process ownership. Only the app's process tree should stop, and reopening should not reuse stale state.

Detailed documentation

Read the skill’s original instructions, examples and checks in their authored language.

Details…Show less

Process & Application Lifecycle

Desktop applications must manage their entire process tree reliably across cold start, window close/reopen, OS sleep/wake, crashes, and application quit—without leaving orphaned servers, locked SQLite databases, or zombie CLI processes behind.

Child Process & Ownership Discipline

  1. Track only the processes your app spawned:
    • Record the ChildProcess handle and pid (plus process group / Job Object) of every embedded server, worker, or CLI task launched by the application.
    • Never run blanket killall node or taskkill /IM node.exe, which destroys the user's unrelated development servers or IDE processes!
  2. Clean Cross-Platform Process Tree Termination:
    • On macOS/Linux: spawn long-running tool/worker trees in their own process group or send SIGTERM to the child and its descendants, wait a bounded drain window (e.g., 3s–5s) for in-flight SQLite transactions or state checkpoints to flush, and escalate to SIGKILL only if still alive.
    • On Windows: child.kill() terminates only the immediate process, leaving grand-child processes alive. Use Windows Job Objects (JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE) or taskkill /PID <pid> /T /F on timeout so the entire process tree exits when the desktop app closes or crashes.
  3. Never interpolate shell strings:
    • Spawn processes with execFile / spawn(binaryPath, argsArray, { shell: false, windowsHide: true }) so file paths containing spaces, quotes, or Unicode characters (C:\Users\Gábor Laczkovszki\My Project) never break or inject shell commands.

GUI Environment & PATH Resolution on macOS / Windows

When a user launches a macOS .app from Finder/Dock/Spotlight, the process inherits launchd's minimal PATH (/usr/bin:/bin:/usr/sbin:/sbin)—not their interactive shell PATH where Homebrew (/opt/homebrew/bin), nvm, fnm, volta, pyenv, or ~/.local/bin live:

  • Resolve the user's login shell environment once at startup (e.g., invoking $SHELL -ilc 'env -0' with a short timeout on macOS/Linux and augmenting well-known tool paths like /opt/homebrew/bin, /usr/local/bin, ~/.local/bin) so external CLIs (git, claude, codex) are found reliably.

Window Lifecycle, Single-Instance Lock & Sleep/Wake

  • Single-instance coordination: Acquire a single-instance lock (app.requestSingleInstanceLock() in Electron or tauri-plugin-single-instance) before initializing the database or local server. When a second instance is launched (e.g., via deep link or file association), forward the URL/argv to the primary instance, focus its window, and exit the second instance immediately.
  • Explicit Window Close vs. App Quit:
    • On macOS, closing the last window (window-all-closed) conventionally keeps the app active in the Dock (activate re-opens the window), whereas Cmd+Q (before-quit) initiates full shutdown.
    • Before quitting or applying an update while a task is running, either flush/checkpoint gracefully or prompt the user if an unrecoverable operation is in flight.

Included files 1

  • SKILL.md