Skip to content

fix: settle run_command on shell exit; make Stop self-healing (fixes #3958, #3959) - #3987

Open
smgcd wants to merge 1 commit into
chatboxai:mainfrom
smgcd:chatbox/fix-3958-3959
Open

smgcd wants to merge 1 commit into
chatboxai:mainfrom
smgcd:chatbox/fix-3958-3959

Conversation

@smgcd

@smgcd smgcd commented Oct 13, 2026 •

Copy link
Copy Markdown

Fixes #3958
Fixes #3959

Two related Work-mode / Stop lockups, both reproduced on 1.23.5 (Windows).

#3958 — run_command never returns when the command leaves a long-running child

Root cause: the exec promise is settled only in child.on('close'), and Node emits close only after all stdio streams are closed. A daemon-style grandchild that inherited the tool's pipes keeps them open, so close never fires; the awaited IPC never resolves and the session lock stays engaged (Stop can no longer reach the tool call).

Changes (src/main/skills/user-exec-runner.ts):

  • settle the tool call on the shell's exit after a bounded, re-armable drain grace (2s; reset while output is still flowing), instead of waiting only for close;
  • resolve cancellation immediately as cancelled: true, so Stop is terminal even with a leaked child;
  • arm the same drain from the timeout path (previously the timeout path could also hang with no finish log);
  • add a regression test in user-exec-runner.windows.test.ts that spawns a child outliving the wrapper (ping -n 8).

#3959 — Stop can fail silently and lock the session forever

Root cause: Message.handleStop() swallowed failures (catch { return false }), every retry entry point silently aborted on that false; a single stop that never settled was de-duplicated (startGenerationStop) and serialized (serializeSessionStop) forever; and the "can I retry?" gate is derived from an in-memory generation runtime that can stay stuck in stopping with no watchdog.

Changes:

  • src/renderer/components/chat/Message.tsx: bound the stop wait (15s) and surface a toast on failure/timeout instead of a dead button;
  • src/renderer/stores/generationStopOperations.ts: a watchdog drops a wedged stop so later Stop/Retry are not queued behind a dead task;
  • src/renderer/stores/session/generation-cancellation.ts: cap how long a previous stop may hold the per-session chain;
  • src/renderer/hooks/useGenerationStop.ts: on watchdog, clear the stuck generation runtime so the lock releases;
  • src/main/menu.ts: add Reload (Ctrl+R) to the production View menu as a recovery affordance.

Testing

  • tsc --noEmit: clean
  • pnpm run build: OK
  • new regression test passes; existing native-Windows exec suite 14/14; broader renderer / generation / session suites 761 passing (the 2 failures in attachment-ownership.test.ts are pre-existing and reproduce on a clean tree — unrelated to this change).

Not included

The Windows Job Object (JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE) hardening suggested in #3958 is not implemented here (it would need native support). This PR fixes the hang and the session lock; a daemon that already left the process tree is still not force-killed by taskkill /T.

…hatboxai#3958, chatboxai#3959)

chatboxai#3958 — run_command never returns when the command leaves a daemon child that
inherits the tool's stdio pipes (Node 'close' never fires, so the awaited IPC
never resolves and the session lock stays engaged):
- settle the tool call on the shell's 'exit' after a bounded, re-armable drain
  grace, instead of waiting only for 'close'
- resolve cancellation immediately as cancelled:true (Stop is terminal)
- arm the same drain from the timeout path

chatboxai#3959 — Stop can fail silently and permanently lock the session:
- Message.handleStop no longer fails silently: bounded wait + user-facing toast
- generationStopOperations: watchdog that drops a wedged stop so later Stop/Retry
  are not queued behind a dead task
- generation-cancellation: cap how long a previous stop may hold the per-session
  chain
- useGenerationStop: on watchdog, clear the stuck generation runtime so the lock
  releases
- menu: add Reload (Ctrl+R) to the production View menu as a recovery path
@coderabbitai

coderabbitai Bot commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e76ed63f-9671-4554-951d-15c11a0950c0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@smgcd smgcd changed the title fix: settle run_command on shell exit + make Stop self-healing (refs … fix: settle run_command on shell exit; make Stop self-healing (fixes #3958, #3959) Oct 13, 2026

This branch has not been deployed

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

Labels

None yet

Projects

None yet

1 participant