wsb-daemon 0.248.6265

wsb's daemon: the workbench, the GitHub mirror's schedule and peer continuity, for every workspace on the machine
//! A pid arriving from a browser is a request, not an instruction. What this
//! endpoint refuses matters more than what it signals.

use super::*;
use wsb::output::Line;

fn started(seq: u64, id: u64, pid: u32, argv: &[&str]) -> crate::web::ops::OutputLine {
    crate::web::op_log::output_line_of(Line {
        seq,
        ts: "2026-08-24T10:00:00.000Z".to_string(),
        record: Record::SubprocessStarted {
            id,
            argv: argv.iter().map(|a| a.to_string()).collect(),
            cwd: Some("/w".to_string()),
            pid,
        },
    })
}

fn finished(seq: u64, id: u64) -> crate::web::ops::OutputLine {
    crate::web::op_log::output_line_of(Line {
        seq,
        ts: "2026-08-24T10:00:01.000Z".to_string(),
        record: Record::SubprocessFinished {
            id,
            exit_code: Some(0),
            signal: None,
            duration_ms: 1000,
        },
    })
}

/// The running set, folded the way the handler folds it.
fn fold(lines: &[crate::web::ops::OutputLine]) -> Vec<RunningChild> {
    let mut running: BTreeMap<u64, RunningChild> = BTreeMap::new();
    for line in lines {
        match &line.record {
            Record::SubprocessStarted { id, argv, cwd, pid } => {
                running.insert(
                    *id,
                    RunningChild {
                        id: *id,
                        pid: *pid,
                        argv: argv.clone(),
                        cwd: cwd.clone(),
                        started_at: line.ts.clone(),
                    },
                );
            }
            Record::SubprocessFinished { id, .. } => {
                running.remove(id);
            }
            _ => {}
        }
    }
    running.into_values().collect()
}

#[test]
fn test11092_a_finished_child_is_not_running() {
    // The wall of chips never went away because nothing removed them. A child
    // that reported an exit is gone, and offering a kill for it would signal
    // a pid the operating system has since given to somebody else.
    let lines = vec![
        started(1, 1, 100, &["bash", "-c", "cargo build"]),
        started(2, 2, 200, &["bash", "-c", "go test ./..."]),
        finished(3, 1),
    ];
    let running = fold(&lines);
    assert_eq!(running.len(), 1);
    assert_eq!(running[0].pid, 200);
}

#[test]
fn test11093_a_child_carries_what_identifies_it() {
    // `argv[0]` is `bash` for every one of them, which is why the chips were
    // useless. The pid and the whole command line are what tell them apart.
    let lines = vec![started(1, 1, 4242, &["bash", "-c", "cargo test --features x"])];
    let running = fold(&lines);
    assert_eq!(running[0].pid, 4242);
    assert_eq!(running[0].argv.len(), 3);
    assert_eq!(running[0].started_at.as_deref(), Some("2026-08-24T10:00:00.000Z"));
}

#[test]
fn test11094_a_pid_this_operation_never_started_is_refused() {
    // The endpoint's whole job. Without this, a page could name any pid on the
    // machine and this would signal it.
    let known: Vec<u32> = fold(&[started(1, 1, 100, &["bash"])])
        .iter()
        .map(|c| c.pid)
        .collect();
    assert!(known.contains(&100));
    assert!(!known.contains(&1), "init is not one of this run's children");
    assert!(!known.contains(&99999));
}