Skip to content

Inject login-shell PATH into spawned subprocesses#18

Merged
nickdirienzo merged 1 commit intomainfrom
fix/subprocess-launch-path
Apr 28, 2026
Merged

Inject login-shell PATH into spawned subprocesses#18
nickdirienzo merged 1 commit intomainfrom
fix/subprocess-launch-path

Conversation

@nickdirienzo
Copy link
Copy Markdown
Collaborator

Summary

  • GUI-launched Flight (Sparkle, Finder) inherits launchd's stripped PATH (/usr/bin:/bin:/usr/sbin:/sbin). ClaudeAgent spawns /usr/bin/env claude and WorktreeSetupService runs pnpm install via zsh -s, neither of which loaded the user's actual PATH — so claude silently exited 127 every turn and pnpm install couldn't find pnpm. From flight-fast-ray-9916.log: two >>> STDIN writes, zero stdout, no errors anywhere.
  • New EnvironmentService captures the user's PATH via zsh -ilc 'printf %s "$PATH"' once at app launch (using -ilc not -lc because ~/.local/bin and language-version managers typically live in .zshrc, not .zprofile). All three ShellService spawn paths and both ClaudeAgent spawn branches now set process.environment = EnvironmentService.baseEnvironment(...).
  • ClaudeAgent now drains stderr into its per-worktree log (<<< STDERR: ...) so the next time something like this happens we see the actual error. On clean non-zero exits (reason == .exit && exitCode != 0), fires a metadata-only Sentry event via a new SentryService bridge — sends only command + exit code + correlation UUID. Stderr stays on-box because CLI tools can include credentials in error output. .uncaughtSignal exits (user-pressed-stop SIGINT, our own SIGTERM teardown) are intentionally skipped.
  • Tests/FlightAppTests/EnvironmentInheritanceTests.swift pins the invariant that spawned subprocesses inherit EnvironmentService.path, keyed off runScriptStreaming (uses zsh -s, no rc-file mutation, so the assertion is exact).

Test plan

  • swift test --filter EnvironmentInheritanceTests — all 3 pass
  • ./build.sh — clean build
  • Repro: launched fresh Flight.app, opened flight/fast-ray-9916, sent a message; log went 4 → 7 lines, claude responded with a session_id and a normal assistant reply
  • Verify pnpm install resolves in a fresh worktree on a Sparkle-launched Flight (the fast-elm-2629 reproducer)

🤖 Generated with Claude Code

GUI-launched Flight (Sparkle/Finder) inherits launchd's stripped PATH
(`/usr/bin:/bin:/usr/sbin:/sbin`), so `/usr/bin/env claude` silently
exits 127 every turn and `.flight/worktree-setup` can't find `pnpm`.
The user-visible symptom was "messages die"; the actual error sat in
an undrained stderr pipe.

Capture the user's interactive login-shell PATH via `zsh -ilc` once at
app launch (interactive — `~/.local/bin` and language-version managers
typically live in `.zshrc`, not `.zprofile`), apply it to every
`Process.environment`. Drain stderr from `claude` into the per-worktree
log so future failures are visible. On clean non-zero exits, fire a
metadata-only Sentry capture (command + exit code + correlation UUID
written to the local log) — stderr stays on-box since CLI tool errors
can carry credentials.

Tests pin the invariant that subprocesses inherit `EnvironmentService.path`,
keyed off `runScriptStreaming` (uses `zsh -s`, no rc-file mutation).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@nickdirienzo nickdirienzo merged commit aed5416 into main Apr 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant