Skip to main content

chant/operations/
pause.rs

1//! Spec pause operation.
2//!
3//! Canonical implementation for pausing running work processes.
4
5use anyhow::Result;
6
7use crate::spec::{Spec, SpecStatus};
8use std::path::Path;
9
10/// Options for pausing a spec
11#[derive(Debug, Clone, Default)]
12pub struct PauseOptions {
13    /// Force stop the process without confirmation (CLI only)
14    pub force: bool,
15}
16
17/// Pause a running work process for a spec.
18///
19/// This is the canonical pause logic:
20/// - Checks for PID file and running process
21/// - Stops the process if running (respecting force flag)
22/// - Updates spec status from in_progress to paused
23/// - Cleans up PID file
24///
25/// Returns Ok(true) if a process was stopped, Ok(false) otherwise.
26pub fn pause_spec(spec: &mut Spec, spec_path: &Path, options: PauseOptions) -> Result<bool> {
27    let spec_id = &spec.id;
28
29    // Check if there's a PID file
30    let pid = crate::pid::read_pid_file(spec_id)?;
31
32    let mut process_stopped = false;
33    if let Some(pid) = pid {
34        if crate::pid::is_process_running(pid) {
35            // Process is running
36            if options.force {
37                // Stop the process
38                crate::pid::stop_process(pid)?;
39                crate::pid::remove_pid_file(spec_id)?;
40                process_stopped = true;
41            } else {
42                // MCP handler: always stop without force flag check
43                // CLI: this should have been checked earlier and returned error
44                crate::pid::stop_process(pid)?;
45                crate::pid::remove_pid_file(spec_id)?;
46                process_stopped = true;
47            }
48        } else {
49            // Process not running, clean up PID file
50            crate::pid::remove_pid_file(spec_id)?;
51        }
52    }
53
54    // Update spec status to paused if it was in_progress
55    if spec.frontmatter.status == SpecStatus::InProgress {
56        spec.set_status(SpecStatus::Paused)
57            .map_err(|e| anyhow::anyhow!("Failed to transition spec to Paused: {}", e))?;
58        spec.save(spec_path)?;
59    }
60
61    Ok(process_stopped)
62}