fix: Stop button no longer kills dev server process (Vibe Kanban) (#1509)

* The code compiles successfully. The fix is complete.

To summarize: The issue was that the `try_stop` function in `crates/services/src/services/container.rs:348` was stopping **all** running execution processes when the user pressed the stop button. This included the dev server process. The fix adds a check to skip processes with `run_reason == ExecutionProcessRunReason::DevServer`, so the dev server continues running while other processes (like the coding agent) are stopped.

* All done. Here's a summary of the changes:

**Changes made:**

1. **`crates/services/src/services/container.rs`:**
   - Modified `try_stop` to accept an `include_dev_server: bool` parameter - when `false`, it skips dev server processes
   - Updated `delete` method to call `try_stop(&task_attempt, true)` - when deleting a container, all processes including dev server are stopped
   - Removed the unused `stop_task_processes` method

2. **`crates/server/src/routes/task_attempts.rs`:**
   - Updated `stop_task_attempt_execution` (stop button) to call `try_stop(&task_attempt, false)` - dev server keeps running
   - Updated the retry flow to call `try_stop(&task_attempt, false)` - dev server keeps running during retry

* Cleanup script changes for task attempt 51e0f037-850b-4739-8675-16819c3a5695
This commit is contained in:
Louis Knight-Webb
2025-12-12 12:30:39 +00:00
committed by GitHub
parent dffa4d9621
commit a5eac1dabd
2 changed files with 11 additions and 17 deletions

View File

@@ -304,8 +304,8 @@ pub async fn follow_up(
);
}
// Stop any running processes for this attempt
deployment.container().try_stop(&task_attempt).await;
// Stop any running processes for this attempt (except dev server)
deployment.container().try_stop(&task_attempt, false).await;
// Soft-drop the target process and all later processes
let _ = ExecutionProcess::drop_at_and_after(pool, task_attempt.id, proc_id).await?;
@@ -1237,7 +1237,7 @@ pub async fn stop_task_attempt_execution(
Extension(task_attempt): Extension<TaskAttempt>,
State(deployment): State<DeploymentImpl>,
) -> Result<ResponseJson<ApiResponse<()>>, ApiError> {
deployment.container().try_stop(&task_attempt).await;
deployment.container().try_stop(&task_attempt, false).await;
deployment
.track_if_analytics_allowed(

View File

@@ -87,7 +87,7 @@ pub trait ContainerService {
async fn kill_all_running_processes(&self) -> Result<(), ContainerError>;
async fn delete(&self, task_attempt: &TaskAttempt) -> Result<(), ContainerError> {
self.try_stop(task_attempt).await;
self.try_stop(task_attempt, true).await;
self.delete_inner(task_attempt).await
}
@@ -110,17 +110,6 @@ pub trait ContainerService {
Ok(false)
}
/// Stop execution processes for task attempts without cleanup
async fn stop_task_processes(
&self,
task_attempts: &[TaskAttempt],
) -> Result<(), ContainerError> {
for attempt in task_attempts {
self.try_stop(attempt).await;
}
Ok(())
}
/// A context is finalized when
/// - Always when the execution process has failed or been killed
/// - Never when the run reason is DevServer
@@ -345,12 +334,17 @@ pub trait ContainerService {
})
}
async fn try_stop(&self, task_attempt: &TaskAttempt) {
// stop all execution processes for this attempt
async fn try_stop(&self, task_attempt: &TaskAttempt, include_dev_server: bool) {
// stop execution processes for this attempt
if let Ok(processes) =
ExecutionProcess::find_by_task_attempt_id(&self.db().pool, task_attempt.id, false).await
{
for process in processes {
// Skip dev server processes unless explicitly included
if !include_dev_server && process.run_reason == ExecutionProcessRunReason::DevServer
{
continue;
}
if process.status == ExecutionProcessStatus::Running {
self.stop_execution(&process, ExecutionProcessStatus::Killed)
.await