Skip to content

Comments

fix: reliably stop running actions and allow rerunning after stop#219

Merged
matt2e merged 2 commits intomainfrom
stop-action
Feb 19, 2026
Merged

fix: reliably stop running actions and allow rerunning after stop#219
matt2e merged 2 commits intomainfrom
stop-action

Conversation

@matt2e
Copy link
Contributor

@matt2e matt2e commented Feb 19, 2026

Summary

Fix two related issues with the stop-action functionality:

Reliable process termination

  • Use setsid() to spawn action processes in their own process group
  • Send SIGTERM to the entire process group (-pid) instead of just the shell, ensuring child processes (npm, cargo, etc.) are also terminated
  • Add SIGKILL escalation after a 5-second grace period for processes that ignore SIGTERM
  • On Windows, use /T flag with taskkill to kill the process tree
  • Track stopped state with a dedicated stopped set so the completion thread can distinguish intentional stops from crashes
  • Don't remove from the running map on stop — let the completion thread handle cleanup and emit the proper Stopped status event
  • Skip auto-commit when an action is stopped

Allow rerunning actions after stop

  • Clean up stale (stopped/failed/completed) execution state before starting a new run
  • Only treat running status entries as blocking a rerun; stopped/failed entries no longer prevent re-execution
  • Clean up backend output buffers for stale executions
  • Auto-remove stopped/failed status indicators from the UI after a brief display period
  • Fix primary action button to only show output modal when the action is actively running; otherwise trigger a new run

…ng lifecycle

Previously, clicking Stop on a running action would fail to actually stop
the underlying process and would prematurely show a 'stopped' status in the
UI while the process continued running. Four root causes were identified
and fixed:

1. Process group killing: The shell was spawned without its own process
   group, so SIGTERM only reached the shell process while child processes
   (npm, cargo, etc.) continued as orphans. Now we call setsid() via
   pre_exec to create a new session/process group, and send SIGTERM to
   the negative PID (-pgid) to kill the entire tree. A background thread
   escalates to SIGKILL after 5 seconds if the group is still alive.

2. Premature UI status: The frontend set status='stopped' immediately in
   handleStop() before the backend confirmed the process actually exited.
   Now the frontend waits for the backend's StatusChanged event to update
   the status, showing 'Stopping...' on a disabled button in the interim.

3. Running map cleanup: stop() previously removed the entry from the
   running map immediately, which meant the completion thread couldn't
   find the output buffer to move it to the completed map, losing all
   buffered output. Now stop() only sends the signal; the completion
   thread handles all cleanup.

4. Stopped vs Failed status: The completion thread only emitted Completed
   or Failed based on exit code. Killed processes exit with a signal (no
   exit code), so they were reported as Failed. Now a 'stopped' HashSet
   tracks intentional stops, and the completion thread checks it to emit
   the correct Stopped status. Auto-commit is also skipped for stopped
   actions.
…n state

Previously, after stopping a running action, clicking Run again would show
old output instead of starting a fresh execution. Three issues were fixed:

1. handleRunAction() matched any execution for the action regardless of
   status. Now it only treats status='running' as already-running; for
   stopped/failed/completed entries it clears the stale state and starts
   a new run.

2. Stopped and failed actions were never auto-removed from runningActions
   (only completed ones were). Now all terminal states (completed, failed,
   stopped) are auto-removed after a brief display delay.

3. When rerunning, old backend output buffers are cleaned up via
   clearActionExecution() before the new run starts, preventing stale
   output from appearing.

4. The primary action button only opens the output modal when the action
   is actively running; otherwise it triggers handleRunAction to start
   a fresh execution.
@matt2e matt2e merged commit 65901c2 into main Feb 19, 2026
3 checks passed
@matt2e matt2e deleted the stop-action branch February 19, 2026 19:46
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