Skip to content

Fix login-shell probe cleanup#2124

Merged
steipete merged 4 commits into
steipete:mainfrom
anagnorisis2peripeteia:codex/fix-path-probe-timeout-descendants
Jul 14, 2026
Merged

Fix login-shell probe cleanup#2124
steipete merged 4 commits into
steipete:mainfrom
anagnorisis2peripeteia:codex/fix-path-probe-timeout-descendants

Conversation

@anagnorisis2peripeteia

@anagnorisis2peripeteia anagnorisis2peripeteia commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Defer login-shell PATH capture until Codex RPC launch, while preserving it for explicit script wrappers such as #!/usr/bin/env node.
  • Adopt raw posix_spawn PATH probes into SpawnedProcessGroup, cleaning helpers that escape the original session on timeout and normal exit.
  • Prevent cross-probe pipe inheritance with atomic close-on-exec pipes on Linux and a serialized Darwin pipe/spawn boundary using POSIX_SPAWN_CLOEXEC_DEFAULT.
  • Add regression coverage for native and script overrides, session-escaped output holders, and close-on-exec descriptors.

Root cause

The PATH runner only killed the original process group. Shell startup helpers could call setsid(), retain a probe output pipe, and survive cleanup. Moving PATH capture later avoided unnecessary work for native overrides, but treating every explicit override alike broke script launchers that depend on login-shell PATH. Pipe descriptors also had a small creation-to-fcntl inheritance window when probes overlapped.

Validation

  • make check
    • SwiftFormat: 0 files require formatting
    • SwiftLint: 0 violations in 1,428 files
  • make test through autoreview on fixup commit ffe09726c
    • 640/640 selections
    • 54/54 groups passed on first attempt
    • 0 failures, retries, or timeouts
  • Post-main-merge exact head bc40e4bf3
    • focused resolver/process suite: 80 tests in 6 suites passed
    • make check: clean
  • Linux portability follow-up keeps atomic pipe2(O_CLOEXEC) through a direct glibc/musl symbol declaration after Swift's Linux modules omitted the function declaration.
  • Autoreview: no accepted or actionable findings after the portability repair; correctness confidence 0.93.
  • Original isolated before/after runtime proof and hashed artifacts: https://gist.github.com/anagnorisis2peripeteia/1b3ab2a0f2ee613c442f26e4f72507a7

Screenshots are not applicable because this is subprocess lifecycle behavior.

Out of scope

The separately observed Codex auth.json deletion is unrelated. This patch does not read, write, refresh, or remove Codex authentication state. Validation used isolated homes and test doubles without real account, browser-cookie, or Keychain access.

Related

Authored with Claude; maintainer fixups by @steipete.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1d480a59e2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread Sources/CodexBarCore/UsageFetcher.swift Outdated
Comment on lines +960 to +963
if let override = environment["CODEX_CLI_PATH"],
FileManager.default.isExecutableFile(atPath: override)
{
return override

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve login PATH for override shims

When CODEX_CLI_PATH points at an executable Codex shim installed by npm/fnm/mise and the app or CLI starts with a minimal GUI PATH, this early return skips currentOrCapture, but CodexRPCClient later launches the override through /usr/bin/env with PathBuilder.effectivePATH built from the uncaptured cache. The override itself is executable, yet its shebang such as /usr/bin/env node can no longer find node, so explicit overrides that previously depended on the login-shell PATH fail to start. Consider capturing the login PATH for override launches without using it to locate the Codex binary, or pass the captured PATH through to the launch environment.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P1 Urgent regression or broken agent/channel workflow affecting real users now. labels Jul 13, 2026
@clawsweeper

clawsweeper Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codex review: found issues before merge. Reviewed July 13, 2026, 8:05 PM ET / July 14, 2026, 00:05 UTC.

Summary
The PR defers Codex RPC login-shell PATH capture, preserves login PATH for script overrides, adopts raw shell probes into process-group cleanup, and adds cross-platform lifecycle tests.

Reproducibility: yes. The linked isolated runtime proof and process-leak report provide a high-confidence reproduction of session-escaped helpers surviving cleanup and exhausting process capacity.

Review metrics: 2 noteworthy metrics.

  • Patch surface: 9 files, +266/-56. The change spans executable resolution, low-level process cleanup, PATH probing, and four focused test surfaces.
  • Exact-head focused validation: 80 tests in 6 suites passed. Coverage is substantial, but it does not include an unrelated concurrent Darwin subprocess during the pipe-creation window.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #821
Summary: This PR is the candidate follow-up fix for the process leak documented in the canonical issue and builds on the earlier detached-session PATH-probe change.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🦐 gold shrimp
Proof: 🦞 diamond lobster ✨ media proof bonus
Patch quality: 🦐 gold shrimp
Result: needs maintainer review before merge.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Eliminate the Darwin pipe-inheritance race and add a concurrent unrelated-subprocess regression test.

Risk before merge

  • [P1] On Darwin, another subprocess can launch between pipe creation and FD_CLOEXEC setup, inherit the probe descriptor, and then be terminated as an apparent output holder during cleanup.
  • [P1] The new cleanup reaches beyond the original process group by design, so descriptor identity must be race-free before merge; the focused tests do not exercise unrelated concurrent spawn sites.

Maintainer options:

  1. Close the Darwin inheritance window (recommended)
    Use an inheritance-safe design across concurrent subprocess launches and prove that unrelated children cannot be selected for cleanup.
  2. Land with explicit availability risk
    Accept that a rare concurrent Darwin launch may inherit the pipe and be terminated by probe cleanup.
  3. Pause broad descendant cleanup
    Defer the output-holder scanning portion if a race-free Darwin implementation cannot be bounded for this release.

Next step before merge

  • [P2] A maintainer should require a race-free Darwin cleanup design or explicitly accept the unrelated-process termination risk before merging; this is not a safe blind automation repair.

Maintainer decision needed

  • Question: Should this PR be held until the Darwin pipe inheritance window is eliminated, or is the residual risk of terminating an unrelated concurrently spawned child acceptable for this release?
  • Rationale: The implementation fixes a serious leak but broadens cleanup to processes holding probe pipes; the remaining Darwin race is outside the helper-local lock and requires an explicit safety decision if it is not repaired.
  • Likely owner: steipete — They authored the branch hardening fixups and merged the preceding PATH-probe isolation work.
  • Options:
    • Fix Darwin race before merge (recommended): Require an inheritance-safe spawn design and a concurrent unrelated-child regression test before landing the cleanup.
    • Accept residual race: Merge with explicit ownership of the rare risk that an unrelated Darwin child may inherit a probe descriptor and be terminated.
    • Narrow cleanup scope: Pause the broader output-holder termination behavior and land only the resolver and safely bounded cleanup portions.

Security
Cleared: No dependency, secret, permission, or supply-chain regression is introduced; the unrelated-process termination hazard is tracked as a functional availability finding.

Review findings

  • [P2] Eliminate the Darwin pipe inheritance window — Sources/CodexBarCore/PathEnvironment.swift:762
Review details

Best possible solution:

Make Darwin descriptor creation and inheritance safety global to the process-spawn boundary, or redesign output-holder cleanup so a transiently inherited probe descriptor can never authorize terminating an unrelated process, then add a concurrent unrelated-spawn regression test.

Do we have a high-confidence way to reproduce the issue?

Yes. The linked isolated runtime proof and process-leak report provide a high-confidence reproduction of session-escaped helpers surviving cleanup and exhausting process capacity.

Is this the best way to solve the issue?

No, not yet. Adopting probes into shared process cleanup is focused, but the Darwin pipe-creation window must be eliminated or cleanup must be unable to target unrelated descriptor holders.

Full review comments:

  • [P2] Eliminate the Darwin pipe inheritance window — Sources/CodexBarCore/PathEnvironment.swift:762
    makeCloseOnExecPipe() still calls pipe() before applying FD_CLOEXEC. shellSpawnLock only serializes this helper, so another Process or spawn site can inherit the descriptors during that window; the output-holder scan may then terminate that unrelated child. Use an inheritance-safe global spawn boundary or change cleanup so descriptor inheritance cannot authorize terminating unrelated processes, and cover the concurrent-spawn case.
    Confidence: 0.94

Overall correctness: patch is incorrect
Overall confidence: 0.93

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 315ff65f7b2a.

Label changes

Label changes:

  • add merge-risk: 🚨 availability: The Darwin descriptor race can cause cleanup to terminate an unrelated live child process after merge.
  • add rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦞 diamond lobster and patch quality is 🦐 gold shrimp.
  • add status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (linked_artifact): The PR supplies linked, hashed isolated before-and-after runtime artifacts for the subprocess leak, and prior review classified them as sufficient real behavior proof.
  • remove rating: 🦞 diamond lobster: Current PR rating is rating: 🦐 gold shrimp, so this older rating label is no longer current.
  • remove status: 👀 ready for maintainer look: Current PR status label is status: ⏳ waiting on author.

Label justifications:

  • P1: The underlying leak can exhaust the macOS process limit and make unrelated shells, build tools, and compilers fail to launch.
  • merge-risk: 🚨 availability: The Darwin descriptor race can cause cleanup to terminate an unrelated live child process after merge.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦞 diamond lobster and patch quality is 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (linked_artifact): The PR supplies linked, hashed isolated before-and-after runtime artifacts for the subprocess leak, and prior review classified them as sufficient real behavior proof.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR supplies linked, hashed isolated before-and-after runtime artifacts for the subprocess leak, and prior review classified them as sufficient real behavior proof.
Evidence reviewed

What I checked:

  • Unresolved exact-head review concern: The exact-head review identifies that Darwin creates probe pipes with pipe() before setting FD_CLOEXEC; the helper-local lock does not cover unrelated subprocess launches, so another child can inherit the descriptors and later be selected by output-holder cleanup. (Sources/CodexBarCore/PathEnvironment.swift:762, bc40e4bf3747)
  • Runtime problem is real: The linked process-leak report documents hundreds of zsh and fzf helpers accumulating until the macOS user process limit was nearly exhausted and unrelated commands failed to spawn.
  • Positive real behavior proof: The PR body links isolated before-and-after runtime artifacts with hashes, and the prior completed ClawSweeper cycle classified that linked proof as sufficient for the subprocess lifecycle behavior. (bc40e4bf3747)
  • Focused regression coverage: The branch adds resolver tests for native versus script overrides and process tests for close-on-exec descriptors and session-escaped output holders; the PR reports 80 focused tests across six suites passing on the exact head. (Tests/CodexBarTests/ShellCommandLocatorProcessTests.swift:12, bc40e4bf3747)
  • Feature provenance: Merged pull request fix: isolate interactive PATH probes from caller terminal #2074 introduced detached interactive PATH probes in merge commit b2969de; this PR is an explicit cleanup follow-up, with subsequent implementation fixups by steipete. (Sources/CodexBarCore/PathEnvironment.swift:646, b2969deeeec5)

Likely related people:

  • steipete: Authored the hardening and Linux portability fixups on this branch and merged the preceding terminal-isolation work affecting the same PATH probe implementation. (role: recent area contributor and merger; confidence: high; commits: ffe09726cecd, bc40e4bf3747, b2969deeeec5; files: Sources/CodexBarCore/PathEnvironment.swift, Sources/CodexBarCore/CodexExecutableResolver.swift, Sources/CodexBarCore/Host/Process/SpawnedProcessGroup.swift)
  • possibilities: Authored the merged detached-session PATH-probe change that this cleanup work builds upon. (role: introduced adjacent behavior; confidence: high; commits: 1466f059f289, b2969deeeec5; files: Sources/CodexBarCore/PathEnvironment.swift, Tests/CodexBarTests/ShellCommandForegroundTests.swift)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (3 earlier review cycles)
  • reviewed 2026-07-13T12:16:33.789Z sha 1d480a5 :: needs maintainer review before merge. :: none
  • reviewed 2026-07-13T23:38:00.115Z sha 9c21ad7 :: needs maintainer review before merge. :: none
  • reviewed 2026-07-13T23:43:41.928Z sha 9c21ad7 :: needs maintainer review before merge. :: none

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc40e4bf37

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +762 to +768
guard withUnsafeMutablePointer(to: &fds, {
$0.withMemoryRebound(to: Int32.self, capacity: 2) { pipe($0) == 0 }
}) else { return nil }

for fd in [fds.read, fds.write] {
let flags = fcntl(fd, F_GETFD)
guard flags >= 0, fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0 else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make Darwin probe pipes non-inheritable atomically

When another subprocess is launched outside ShellCommandLocator during the small window between pipe() and the later fcntl(FD_CLOEXEC), that child can inherit these probe descriptors because shellSpawnLock is local to this helper and does not cover other Process.run()/spawn sites. The new output-holder cleanup then scans for any process holding these pipes and can SIGTERM/SIGKILL an unrelated live child, so this still regresses concurrent app activity on Darwin despite serializing overlapping shell probes.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. and removed rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 14, 2026
@steipete steipete merged commit b41715f into steipete:main Jul 14, 2026
13 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. P1 Urgent regression or broken agent/channel workflow affecting real users now. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants