Repository navigation
fix(util): kill spawned process groups when opencode exits - #59411
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
Looks good. I reproduced both exit paths on the base and checked them against this branch:
process.exit()with a child that ignores SIGTERM and has a grandchild, spawned through the spawner: on the base the whole group is left with PPID 1; with this change it's killed.- Terminal hangup (the
shutdown-hangfixture under a pty): on the base the child is orphaned; here the hangup becomes a SIGTERM shutdown and the child is killed. Without a terminal, behaviour is unchanged, as intended.
shutdown-deadline and cross-spawn-spawner tests pass, and packages/util type-checks. No blocking concerns.
Public API: unchanged.
Related: #50363
This was referenced Oct 28, 2026
88 tasks done
brentyates-swx
pushed a commit
to swxtchio/swx-opencode-archive
that referenced
this pull request
Nov 4, 2026
brentyates-swx
added a commit
to swxtchio/swx-opencode-archive
that referenced
this pull request
Nov 6, 2026
* docs: plan transport and lifecycle backport Implement the assigned transport and lifecycle behavior in existing fork owners. - Pick each donor in its own -x commit; omit shell-failure UI and absent CLI/MCP payload. - Disable Bun’s independent fetch deadline only for long network clients and native provider requests; preserve caller signals, domain deadlines, and the #303 watchdog. - Track detached POSIX groups only while cleanup is owed, preserving #380 escalation and release of intentional descendants; route terminal SIGHUP through the TUI lifecycle. - Add real-server and child-process regressions with removal proofs, then record donor coverage and focused checks in CHANGESET.md. * fix(tui): keep long-pending requests past Bun's fetch deadline (anomalyco#55149) Give the TUI client timeout: false, share the no-timeout fetch helper across run, mini and the TUI via @opencode/util/no-timeout-fetch, and surface existing-session shell failures with a toast and composer restore. Closes anomalyco#54974 Co-authored-by: Finn763 <finn763@users.noreply.github.com> (cherry picked from commit 52a10f5) * fix(core): disable Bun's fetch idle timeout for provider requests (anomalyco#55007) (cherry picked from commit 460c626) * fix(util): kill spawned process groups when opencode exits (anomalyco#59411) (cherry picked from commit e2bc79d) * fix(core): type Bun fetch timeout overrides * test(opencode): verify mini attach timeout wiring Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * test(core): verify abrupt owner process-group cleanup Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * test(tui): verify SDK long-call fetch policy Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * fix(tui): preserve nohup SIGHUP behavior Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * test(core): verify native fetch timeout override Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * test(core): prove native requests outlive default idle timeout Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * test(opencode): verify attached runs survive Bun idle timeout Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * docs: record transport and lifecycle backport Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * docs: clarify backport regression ownership Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * test(opencode): allow Bun 1.4.4 server startup window Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * test: shorten idle-timeout acceptance tests Run the native and attached held-response witnesses in bounded Bun 1.4.4 child processes with BUN_CONFIG_HTTP_IDLE_TIMEOUT=2 so they stay out of the multi-minute range. Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * test(tui): prove SDK long calls survive Bun idle expiry Exercise SDKProvider's external SDK and SSE paths with a held event stream, a real timeout control, and a completed event after the injected short idle expiry. Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * test(opencode): prove mini attach survives Bun idle expiry Hold the real mini attach session response in a Bun 1.4.4 child process, observe an unmodified control fetch time out first, then release the expected 404 response. Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * test(tui): make SIGHUP fixture reports truthful Keep the nohup fixture scoped to listener policy and process survival, and add a non-TTY non-nohup default-disposition witness while retaining lifecycle assertions only from the real TUI producer. Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * fix(tui): clean Worker-owned process groups on exit Acknowledge Worker group ownership and release events, kill and await remaining groups before the shutdown reply, and use the recorded PGID ledger as the bounded TUI-owner backstop when shutdown is unconfirmed. Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * docs: finalize transport exit review coverage Record the acknowledged TUI Worker group lifecycle, truthful SIGHUP behavior, and short Bun 1.4.4 behavioral witnesses in the backport ledger. Claude-Session: c368ed23-1e31-4f2c-b305-b84f21d79cc5 * fix(tui): centralize Worker process-group ownership * docs: plan transport exit review fixes 1. Make worker and parent ownership ledgers converge on each local acquire, release, and cleanup decision even when acknowledgement is rejected or delayed; validate recorded identities before parent-side signalling. 2. Add rejected/delayed acknowledgement regressions for acquire, release, cleanup announcement, and release/reacquire identity fallback. 3. Harden fixture cleanup and deadlines; add Linux-only guards; correct CHANGESET ownership and SIGHUP claims; remove only test hooks no longer needed. 4. Verify focused changed cases and then each importing test file once under checksum-verified Bun 1.4.4; commit without pushing. * fix(core): reconcile process-group acknowledgement failures Reconcile Worker and parent ownership ledgers on local acquire, release, and cleanup facts, independent of rejected or delayed acknowledgements. Validate recorded member identities before signalling and preserve released member identities across reacquisition. Bound identity-owned process fixture cleanup, gate procfs child fixtures to Linux, retain portable SDK cancellation coverage, and correct the #410 SIGHUP and ownership ledger. Claude-Session: unknown * test(opencode): gate shell output on spawn readiness Wait for the process spawn acknowledgement before the ShellTool first-output deadline and reap the child on readiness failure. Tighten process-group fixture types for package typecheck. Claude-Session: unknown * fix(core): restore cross-platform process group cleanup Claude-Session: unavailable (OpenCode session) * fix(core): unify process-group ownership decisions Claude-Session: unavailable (OpenCode session) * fix(core): publish process-group ownership updates Claude-Session: unavailable (OpenCode session) * test(opencode): cover cancellation-overlap ownership Claude-Session: unavailable (OpenCode session) * test(core): wait for process-group cleanup Claude-Session: unavailable (OpenCode session) * test(opencode): harden process-group test results Claude-Session: unavailable (OpenCode session) * test(opencode): validate cancellation result shape Claude-Session: unavailable (OpenCode session) * test(opencode): type fixture results as unknown Claude-Session: unavailable (OpenCode session) --------- Co-authored-by: Finn763 <165816600+Finn763@users.noreply.github.com> Co-authored-by: Finn763 <finn763@users.noreply.github.com> Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
opencode starts every child (MCP servers, shell tool commands, formatters, rg, git) in its own process group, and only stops those groups from scope finalizers. Several exit paths skip the finalizers:
process.exit(1)Anything still running then gets reparented to PID 1, and MCP servers that ignore stdin EOF (npx wrappers, for example) stay there forever. Part of #50363. This replaces #59398 with something much smaller.
Change
cross-spawn-spawner.tskeeps a set of live process groups and registers one synchronousexithook that SIGKILLs whatever is left. Normal teardown already had its chance to stop things gracefully; the hook can't wait. A group is dropped as soon as its leader exits, so a finished shell command's intentional background job is left alone as before, and a reused PID can't be hit.shutdown-deadline.tstreats SIGHUP like SIGTERM, but only when stdin or stdout is a terminal.nohupand the background service have no terminal, and adding a listener would otherwise override nohup's ignore.Things meant to outlive opencode don't go through this spawner, so they need no opt-out. I checked:
serve --service): rawnode:child_processinpackages/client/src/service-contender.tsspawninpackages/core/src/persistent-pty/daemon.ts--stdioserver: go through the spawner but are awaited or tied to the CLI's lifetime, so killing them on exit is rightVerification
servefrom this branch with a stdio MCP server that ignores stdin EOF and SIGTERM and has its own child:Also ran
bun typecheckinpackages/utilandpackages/cli,test/shutdown-deadline.test.tsin cli,test/effect/cross-spawn-spawner.test.ts,test/mcp.test.tsandtest/processin core, andbun run checkfrom the root.Visual
Added by the opencode bot (AI-generated).
Related: #50363