Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/Skidbladnir

src/cli_install.rs

//! THE CLI DOOR — install ONE already-built executable into a bin directory and
//! then **prove the bytes landed**.
//!
//! # Why this module exists
//!
//! Skíðblaðnir already owned three install doors, and every one of them leads to a
//! *service*: [`install_product`](crate::install::install_product) renders a systemd
//! unit, [`install_prefix_payload`](crate::install::install_prefix_payload) unfolds a
//! bundle into a generation layout, and
//! [`refresh_product`](crate::install::refresh_product) is build → install → migrate →
//! restart over that layout. None of them installs a **bare command** into a bin
//! directory, because a command is not a unit: it has no `ExecStart`, no PID to probe,
//! no port to knock on.
//!
//! So the constellation's fat CLIs had no door at all. MEASURED on oden 2026-08-25:
//! `/usr/local/bin/dwarves` was `0.1.4 (2079ba109c22)`, built 2026-08-14, while the
//! schema it validates against had moved on — `RunKind::Tests` (`run = { tests = … }`)
//! landed 2026-08-25 in `ab58b12`. The installed validator therefore refused a pipeline
//! that is *correct*, and refused it with a parse error that names the file rather than
//! the eleven-day gap that actually caused it. Two lanes lost time to that
//! independently. The bug was never in the pipeline; it was that **code had not reached
//! the installed artifact.**
//!
//! # The one thing this door does that a copy does not
//!
//! It **asserts the artifact on disk**, never the exit status of the copy.
//!
//! Two independent things can go wrong and only one of them shows up in a status:
//!
//! - the bytes did not land (a copy that succeeded onto the wrong path, a truncated
//!   write, a destination someone else replaced) — [`verify_installed_binary`] re-hashes
//!   the **destination** and compares it to the artifact that was handed in;
//! - the bytes landed but are not the build you meant — [`probe_version_line`] runs the
//!   installed file and [`CliInstallRequest::expect_marker`] requires the git hash it
//!   prints to be the one expected.
//!
//! `probe_version_line` gates on **stdout**, not on the status, because a program that
//! exits 0 and says nothing is entirely ordinary — the module's own test installs one
//! (a real `#!/bin/true` program: status 0, empty stdout) and requires the probe to
//! refuse it. A probe written as `if !status.success() { bail }` passes that binary and
//! would ship a command that cannot name its own build.
//!
//! # Why the write is a rename and not a copy
//!
//! You cannot write onto a running program. MEASURED on oden 2026-08-25: with a live
//! process holding the text segment, `cp` onto its path fails `ETXTBSY` ("Text file
//! busy", `errno` 26); staging beside it and `rename(2)`-ing over it succeeds, because
//! rename unlinks the old inode and the running process keeps it alive. Since the whole
//! point of this door is to update a command that someone may be running *right now* —
//! including the very binary invoking the door — the staging file is not a nicety. It is
//! the only way in.
//!
//! The staging file is created **inside the destination directory** so the rename is
//! same-filesystem and therefore atomic. A reader of `<bin>/dwarves` sees the old bytes
//! or the new bytes, never a torn file.
//!
//! # What is reused
//!
//! - [`UpgradeSource::executable`](crate::install::UpgradeSource::executable) — refuses
//!   an absent, non-file or non-executable source. A lifecycle verb installs an artifact
//!   that already exists; it never builds one.
//! - [`crate::running::binary_sha256`] — which is `nornir-hash`, the constellation's ONE
//!   content-hash primitive. Never a hand-rolled digest.
//! - The "verbs and results as data" shape of [`crate::install::InstallOutcome`] and
//!   [`crate::install::PrefixInstallOutcome`]: [`CliInstallOutcome`] is plain serde, so a
//!   test asserts exactly what landed rather than that a function returned `Ok`.

use std::path::{Path, PathBuf};

use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};

use crate::install::UpgradeSource;

/// Mode a CLI is installed with: readable and executable by all, writable by the owner.
pub const CLI_MODE: u32 = 0o755;

/// The argv a version probe appends to the installed path. Every fat client in the
/// constellation stamps its git hash here — that is what makes a stale install
/// *visible* instead of merely old.
pub const VERSION_ARGV: &[&str] = &["--version"];

/// What to install, where, and what must be true afterwards.
///
/// There is no `Default`: a door with a defaulted destination is how a binary lands
/// somewhere nobody looks.
#[derive(Debug, Clone)]
pub struct CliInstallRequest<'a> {
    /// The command name — the file name it takes in `bin_dir` (`dwarves`, `nornir`, …).
    pub name: &'a str,
    /// The ALREADY-BUILT executable to install. Validated by
    /// [`UpgradeSource::executable`]; this door never builds.
    pub source: &'a Path,
    /// The directory the command lands in — `~/.cargo/bin`, `/usr/local/bin`, or a
    /// scratch prefix. This is the bin directory itself, not a prefix with `bin/`
    /// beneath it, because both real destinations on oden already ARE bin directories.
    pub bin_dir: &'a Path,
    /// Run `<installed> --version` after the install and keep the line.
    ///
    /// Off means the outcome carries no `version_line` — never that the probe passed.
    pub probe_version: bool,
    /// A substring the version output MUST contain — in practice the git hash of the
    /// commit that was built. `Some(_)` implies the probe: the whole reason to stamp a
    /// hash into `--version` is to assert it from the outside.
    pub expect_marker: Option<&'a str>,
}

/// The fully-inspectable result of an install — plain data, so a caller reports and a
/// test asserts the same facts.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CliInstallOutcome {
    /// The command name installed.
    pub name: String,
    /// The artifact that was handed in.
    pub source: PathBuf,
    /// Where it now is.
    pub installed: PathBuf,
    /// The digest **re-read from the installed file after the rename** — not the
    /// digest of the source, which would make this field a tautology.
    pub sha256: String,
    /// The digest of the file that occupied `installed` before, when there was one.
    /// `None` on a first install.
    pub replaced: Option<String>,
    /// Did the bytes at `installed` actually change? A reinstall of identical bytes is
    /// a legitimate no-op and says so rather than claiming an update.
    pub changed: bool,
    /// The first non-empty stdout line of `<installed> --version`, when probed.
    pub version_line: Option<String>,
    /// Mode the installed file carries.
    pub mode: u32,
}

impl CliInstallOutcome {
    /// One line for an operator: what landed, where, and the proof.
    pub fn summary(&self) -> String {
        let state = if self.changed { "installed" } else { "unchanged" };
        let ver = self
            .version_line
            .as_deref()
            .map(|v| format!(" — {v}"))
            .unwrap_or_default();
        format!(
            "{state} {} at {} (sha256 {}){ver}",
            self.name,
            self.installed.display(),
            &self.sha256[..self.sha256.len().min(12)],
        )
    }
}

/// Hash a file through the constellation's one primitive, as `anyhow`.
fn digest(path: &Path) -> Result<String> {
    crate::running::binary_sha256(path).map_err(|e| anyhow::anyhow!(e))
}

/// **THE GATE** — is the file at `installed` byte-for-byte the artifact `want_sha256`
/// describes?
///
/// Hashes the file **on disk**, right now. Not a flag set by the copy, not the copy's
/// exit status, not a length. The error names both digests and the path, because an
/// installer that says only "mismatch" sends the reader back to compute what it
/// already knew.
///
/// Returns the digest it read, so a caller stores the value it PROVED rather than the
/// value it hoped for.
pub fn verify_installed_binary(installed: &Path, want_sha256: &str) -> Result<String> {
    if !installed.exists() {
        anyhow::bail!(
            "nothing at {} after the install — the install reported no error and produced \
             no file, which is exactly the shape an exit-status check cannot see",
            installed.display()
        );
    }
    let got = digest(installed).with_context(|| {
        format!(
            "hash the installed file {} to verify it against the artifact",
            installed.display()
        )
    })?;
    if got != want_sha256 {
        anyhow::bail!(
            "{} is NOT the artifact that was installed: on disk sha256:{got}, built \
             sha256:{want_sha256}. The bytes did not land — treat this install as failed, \
             not as a warning.",
            installed.display()
        );
    }
    Ok(got)
}

/// Run `<installed> --version` and return its first non-empty **stdout** line.
///
/// Zero-shell: an argv `Command`, never `sh -c`. The exit status is deliberately NOT
/// the gate — see the module docs for the libtest binary that prints an error and exits
/// 0. Empty stdout is the failure, whatever the status was.
pub fn probe_version_line(installed: &Path) -> Result<String> {
    let out = std::process::Command::new(installed)
        .args(VERSION_ARGV)
        .output()
        .with_context(|| {
            format!(
                "run `{} --version` — the installed file exists but could not be executed",
                installed.display()
            )
        })?;

    let line = String::from_utf8_lossy(&out.stdout)
        .lines()
        .map(str::trim)
        .find(|l| !l.is_empty())
        .map(str::to_string);

    match line {
        Some(l) => Ok(l),
        None => {
            let stderr = String::from_utf8_lossy(&out.stderr);
            let stderr = stderr.lines().find(|l| !l.trim().is_empty()).unwrap_or("");
            anyhow::bail!(
                "`{} --version` printed NOTHING on stdout (exit status {}{}) — a program \
                 that cannot name its own build cannot be checked for staleness, and the \
                 status alone would have called this fine",
                installed.display(),
                out.status,
                if stderr.is_empty() {
                    String::new()
                } else {
                    format!("; stderr: {stderr}")
                },
            )
        }
    }
}

/// **Install one already-built command into a bin directory, then prove it landed.**
///
/// The sequence, and why it is this sequence:
///
/// 1. Validate the source through [`UpgradeSource::executable`] — absent, a directory,
///    or missing its execute bit are all refusals, not warnings.
/// 2. Hash the source. This is the `want` every later step is measured against.
/// 3. Record the digest of whatever already occupies the destination, so the outcome can
///    say *replaced X with Y* instead of *did something*.
/// 4. Copy to a staging file **inside `bin_dir`** and set [`CLI_MODE`] on it.
/// 5. `rename(2)` staging → destination. Atomic (same filesystem) and the only write
///    that survives the destination being a running program (`ETXTBSY`).
/// 6. [`verify_installed_binary`] — re-hash the destination and compare.
/// 7. Optionally probe `--version` and require [`CliInstallRequest::expect_marker`] in
///    the output.
///
/// A failure anywhere after step 4 removes the staging file. A failure at step 6 or 7
/// leaves the destination as it is and reports — the caller must see a hard error, not a
/// silently reverted install it never learns about.
pub fn install_cli_binary(req: &CliInstallRequest<'_>) -> Result<CliInstallOutcome> {
    let out = install_cli_binary_impl(req);
    match &out {
        Ok(o) => crate::surface_status!(
            "skidbladnir/cli_install",
            "install_cli_binary",
            true,
            "name={} dest={} sha256={} changed={}",
            o.name,
            o.installed.display(),
            o.sha256,
            o.changed,
        ),
        Err(e) => crate::surface_status!(
            "skidbladnir/cli_install",
            "install_cli_binary",
            false,
            "install cli `{}` failed: {:#}",
            req.name,
            e,
        ),
    }
    out
}

fn install_cli_binary_impl(req: &CliInstallRequest<'_>) -> Result<CliInstallOutcome> {
    if req.name.is_empty() || req.name.contains(std::path::MAIN_SEPARATOR) {
        anyhow::bail!(
            "`{}` is not a command name — it is the file name the command takes inside \
             the bin directory, so it carries no path separators",
            req.name
        );
    }

    // 1. The artifact must already exist and be runnable. Reused wholesale.
    let source = match UpgradeSource::executable(req.source)? {
        UpgradeSource::Executable(p) => p,
        // `executable()` cannot return this; matched so a future variant is a compile
        // error here rather than a silent wrong install.
        UpgradeSource::Bundle(p) => anyhow::bail!(
            "{} is a packed bundle, not a command — install it with \
             `install_prefix_payload` or `refresh_product`",
            p.display()
        ),
    };

    // 2. The digest every later assertion is measured against.
    let want = digest(&source)
        .with_context(|| format!("hash the artifact {} before installing it", source.display()))?;

    std::fs::create_dir_all(req.bin_dir)
        .with_context(|| format!("create the bin directory {}", req.bin_dir.display()))?;
    let installed = req.bin_dir.join(req.name);

    if installed.is_dir() {
        anyhow::bail!(
            "{} is a DIRECTORY — refusing to install a command over it",
            installed.display()
        );
    }

    // 3. What is being replaced, by content. A first install reports `None`, which is a
    //    different fact from "replaced identical bytes".
    let replaced = if installed.exists() {
        Some(digest(&installed).with_context(|| {
            format!(
                "hash the file already at {} — an install that cannot say what it \
                 replaced cannot be audited",
                installed.display()
            )
        })?)
    } else {
        None
    };

    // 4. Stage INSIDE the destination directory: same filesystem, so step 5 is atomic.
    let staging = req.bin_dir.join(format!(
        ".{}.new-{}",
        req.name,
        std::process::id()
    ));
    let staged = stage_then_place(&source, &staging, &installed);
    if staged.is_err() {
        let _ = std::fs::remove_file(&staging);
    }
    staged?;

    // 6. THE GATE. Hash what is actually on disk.
    let sha256 = verify_installed_binary(&installed, &want)?;

    // 7. The outside view of the build: what the program says it is.
    let version_line = if req.probe_version || req.expect_marker.is_some() {
        Some(probe_version_line(&installed)?)
    } else {
        None
    };
    if let Some(marker) = req.expect_marker {
        let line = version_line.as_deref().unwrap_or("");
        if !line.contains(marker) {
            anyhow::bail!(
                "{} reports `{line}`, which does not contain `{marker}` — the file on disk \
                 hashes correctly but identifies itself as a different build, so one of the \
                 two claims is wrong and neither may be trusted",
                installed.display()
            );
        }
    }

    let changed = replaced.as_deref() != Some(sha256.as_str());

    Ok(CliInstallOutcome {
        name: req.name.to_string(),
        source,
        installed,
        sha256,
        replaced,
        changed,
        version_line,
        mode: CLI_MODE,
    })
}

/// Copy `source` to `staging`, mark it executable, and `rename` it over `installed`.
///
/// Split out so the ETXTBSY-defeating order — write elsewhere, then rename — is one
/// reviewable unit rather than something a later edit can quietly reorder into a direct
/// copy.
fn stage_then_place(source: &Path, staging: &Path, installed: &Path) -> Result<()> {
    std::fs::copy(source, staging).with_context(|| {
        format!(
            "stage {} at {}",
            source.display(),
            staging.display()
        )
    })?;

    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        std::fs::set_permissions(staging, std::fs::Permissions::from_mode(CLI_MODE))
            .with_context(|| format!("set mode {CLI_MODE:o} on {}", staging.display()))?;
    }

    // The rename, not a copy: the destination may be executing right now (`ETXTBSY`),
    // and a reader must never observe a half-written command.
    std::fs::rename(staging, installed).with_context(|| {
        format!(
            "place {} at {} — the staging file and the destination must share a \
             filesystem for this to be atomic",
            staging.display(),
            installed.display()
        )
    })?;
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Write;

    /// A REAL executable whose `--version` output the test chooses, with no shell and no
    /// dependency on any particular program being installed: a `#!/bin/echo <line>`
    /// shebang. `execve` turns that into `/bin/echo "<line>" <path> --version`, so the
    /// chosen text really is printed by a really-executed file.
    fn version_stub(dir: &Path, name: &str, line: &str) -> PathBuf {
        let p = dir.join(name);
        let mut f = std::fs::File::create(&p).unwrap();
        writeln!(f, "#!/bin/echo {line}").unwrap();
        drop(f);
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            std::fs::set_permissions(&p, std::fs::Permissions::from_mode(0o755)).unwrap();
        }
        p
    }

    /// A REAL ELF with a digest unique to `tag`: this very test binary, plus a trailing
    /// comment. Used where the fixture must be *executable machine code* — the
    /// `ETXTBSY` test needs a process that can hold a text segment, which a shebang
    /// script cannot do (the interpreter is what runs, not the script).
    fn elf_fixture(dir: &Path, name: &str, tag: &str) -> PathBuf {
        let p = dir.join(name);
        let mut bytes = std::fs::read(std::env::current_exe().unwrap()).unwrap();
        bytes.extend_from_slice(tag.as_bytes());
        std::fs::write(&p, &bytes).unwrap();
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            std::fs::set_permissions(&p, std::fs::Permissions::from_mode(0o755)).unwrap();
        }
        p
    }

    fn req<'a>(name: &'a str, source: &'a Path, bin: &'a Path) -> CliInstallRequest<'a> {
        CliInstallRequest {
            name,
            source,
            bin_dir: bin,
            probe_version: false,
            expect_marker: None,
        }
    }

    /// A no-op in an ordinary run; a process that BLOCKS when the ETXTBSY test re-execs
    /// this binary at it. That gives the suite a genuine long-lived process holding a
    /// text segment without shelling out or depending on `/bin/bash` existing.
    #[test]
    fn victim_holds_a_text_segment_when_asked() {
        if std::env::var_os("SKID_CLI_DOOR_VICTIM").is_none() {
            return;
        }
        // Long enough that a loaded oden (five lanes building at once) cannot let the
        // victim expire mid-test and turn a real assertion into a flake. The test kills
        // it, so the number is a ceiling, not a wait.
        std::thread::sleep(std::time::Duration::from_secs(300));
    }

    /// ★ RED — the gate must FAIL when the bytes on disk are not the bytes installed.
    ///
    /// Without this the whole module is decoration: every other test installs
    /// successfully, so a `verify_installed_binary` that returned `Ok(())`
    /// unconditionally would pass all of them. Here the file is corrupted *after* a
    /// successful install and the gate must refuse — naming BOTH digests, so the error
    /// is usable without re-deriving what the installer already computed.
    #[test]
    fn the_gate_refuses_a_file_whose_bytes_are_not_the_artifact() {
        let tmp = tempfile::tempdir().unwrap();
        let bin = tmp.path().join("bin");
        let src = version_stub(tmp.path(), "src", "tool 1.0 (abcdef)");

        let out = install_cli_binary(&req("tool", &src, &bin)).unwrap();
        // Green first, so the red below is a change of one thing only.
        verify_installed_binary(&out.installed, &out.sha256).expect("the honest file passes");

        std::fs::write(&out.installed, b"#!/bin/echo something else entirely\n").unwrap();
        let err = verify_installed_binary(&out.installed, &out.sha256)
            .expect_err("a file whose bytes changed under us MUST NOT verify");
        let msg = format!("{err:#}");
        assert!(
            msg.contains(&out.sha256),
            "the error must name the digest that was built, got: {msg}"
        );
        let on_disk = crate::running::binary_sha256(&out.installed).unwrap();
        assert!(
            msg.contains(&on_disk),
            "the error must name the digest actually on disk, got: {msg}"
        );
        assert_ne!(on_disk, out.sha256, "the fixture must really differ");
    }

    /// ★ RED — the gate must fail when NOTHING landed.
    ///
    /// This is the failure an exit-status check is structurally blind to: a copy that
    /// returns success and writes no file looks identical to a copy that worked.
    #[test]
    fn the_gate_refuses_an_absent_file() {
        let tmp = tempfile::tempdir().unwrap();
        let err = verify_installed_binary(&tmp.path().join("never-written"), "00ff")
            .expect_err("a missing file is a failed install, not a pass");
        assert!(
            format!("{err:#}").contains("nothing at"),
            "the error must say the file is absent"
        );
    }

    /// ★ The outcome's `sha256` is read back FROM DISK, not carried over from the
    /// source — otherwise the field is a tautology and the gate is watching itself.
    #[test]
    fn the_reported_digest_is_the_one_on_disk() {
        let tmp = tempfile::tempdir().unwrap();
        let bin = tmp.path().join("bin");
        let src = version_stub(tmp.path(), "src", "tool 1.0 (abcdef)");

        let out = install_cli_binary(&req("tool", &src, &bin)).unwrap();
        let independently = crate::running::binary_sha256(&out.installed).unwrap();
        assert_eq!(out.sha256, independently, "reported digest must be the disk's");
        assert_eq!(
            out.sha256,
            crate::running::binary_sha256(&src).unwrap(),
            "and it must equal the artifact that was handed in"
        );
        assert_eq!(out.mode & 0o777, CLI_MODE, "an installed command is executable");
        assert!(out.changed, "a first install changed something");
        assert_eq!(out.replaced, None, "there was nothing to replace");
    }

    /// ★ RED — a `--version` probe MUST NOT be satisfied by the exit status.
    ///
    /// The fixture is a real program that **exits 0 and prints nothing**: a
    /// `#!/bin/true` shebang, which `execve` turns into `/bin/true <path> --version`.
    /// A probe written as `if !status.success() { bail }` calls that a healthy install
    /// of a healthy program. This test requires it to be refused, on the OUTPUT.
    ///
    /// (An earlier draft of this test used a Rust libtest harness as the fixture,
    /// believing it exited 0 while printing `error: Unrecognized option: 'version'`.
    /// It does not — it exits **101**. That first measurement had read the exit status
    /// of a `head` at the end of a pipeline rather than the binary's own. The fixture
    /// was replaced with one whose exit status is asserted here, in the test.)
    #[test]
    fn the_version_probe_refuses_a_binary_that_prints_nothing_though_it_exits_zero() {
        let tmp = tempfile::tempdir().unwrap();
        let bin = tmp.path().join("bin");
        let src = tmp.path().join("silent");
        {
            let mut f = std::fs::File::create(&src).unwrap();
            writeln!(f, "#!/bin/true").unwrap();
        }
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            std::fs::set_permissions(&src, std::fs::Permissions::from_mode(0o755)).unwrap();
        }

        // The premise, asserted rather than assumed: status 0, empty stdout.
        let raw = std::process::Command::new(&src)
            .args(VERSION_ARGV)
            .output()
            .unwrap();
        assert!(
            raw.status.success(),
            "the fixture only proves anything if it EXITS ZERO; got {}",
            raw.status
        );
        assert!(
            String::from_utf8_lossy(&raw.stdout).trim().is_empty(),
            "the fixture must print nothing on stdout"
        );

        let mut r = req("silent", &src, &bin);
        r.probe_version = true;
        let err = install_cli_binary(&r)
            .expect_err("an install whose program cannot name its build MUST fail");
        let msg = format!("{err:#}");
        assert!(
            msg.contains("printed NOTHING on stdout"),
            "the probe must fail on the OUTPUT, got: {msg}"
        );
    }

    /// ★ RED — a version line that does not carry the expected build marker fails, even
    /// though the file hashes correctly.
    ///
    /// This is the whole point of stamping a git hash into `--version`: the digest says
    /// the bytes arrived, the marker says WHICH source they came from. A door that only
    /// hashed would happily install last month's binary from last month's build dir.
    #[test]
    fn a_version_line_without_the_expected_marker_fails_the_install() {
        let tmp = tempfile::tempdir().unwrap();
        let bin = tmp.path().join("bin");
        let src = version_stub(tmp.path(), "src", "dwarves 0.1.4 (2079ba109c22)");

        let mut r = req("dwarves", &src, &bin);
        r.expect_marker = Some("7681a252956c");
        let err = install_cli_binary(&r).expect_err("the stale hash must be refused");
        assert!(
            format!("{err:#}").contains("7681a252956c"),
            "the error must name the marker that was expected"
        );

        // GREEN, one thing changed: the marker the binary really prints.
        let mut ok = req("dwarves", &src, &bin);
        ok.expect_marker = Some("2079ba109c22");
        let out = install_cli_binary(&ok).expect("the honest marker passes");
        assert!(
            out.version_line.as_deref().unwrap().contains("2079ba109c22"),
            "the kept line is the one that was matched"
        );
    }

    /// ★ The probe runs the INSTALLED file, not the source.
    ///
    /// A probe that ran the source would report a healthy version for an install that
    /// never landed — precisely the class of bug this module exists to close. Proven by
    /// making the two files disagree after the install and re-probing.
    #[test]
    fn the_probe_reads_the_installed_file_not_the_source() {
        let tmp = tempfile::tempdir().unwrap();
        let bin = tmp.path().join("bin");
        let src = version_stub(tmp.path(), "src", "tool 2.0 (from-source)");

        let mut r = req("tool", &src, &bin);
        r.probe_version = true;
        let out = install_cli_binary(&r).unwrap();
        assert!(out.version_line.as_deref().unwrap().contains("from-source"));

        // Now make them disagree; the probe must follow the destination.
        std::fs::write(&src, "#!/bin/echo tool 9.9 (source-moved-on)\n").unwrap();
        let line = probe_version_line(&out.installed).unwrap();
        assert!(
            line.contains("from-source") && !line.contains("source-moved-on"),
            "the probe must run {}, got `{line}`",
            out.installed.display()
        );
    }

    /// ★ RED then GREEN — installing over a RUNNING command.
    ///
    /// The red is inside the test: with a live process holding the destination's text
    /// segment, a direct `std::fs::copy` onto it fails `ETXTBSY` (errno 26). That is the
    /// obvious implementation, and it does not work. The door's stage-then-rename does,
    /// and the assertion is on the destination's digest afterwards — not on `Ok(_)`.
    ///
    /// This is not a corner case: `dwarves --install` replacing `dwarves` is the normal
    /// use, and on oden the running process may be a warm agent's own client.
    #[test]
    #[cfg(unix)]
    fn a_running_command_can_be_replaced_where_a_plain_copy_cannot() {
        let tmp = tempfile::tempdir().unwrap();
        let bin = tmp.path().join("bin");
        let first = elf_fixture(tmp.path(), "v1", "generation-one");
        let second = elf_fixture(tmp.path(), "v2", "generation-two");
        assert_ne!(
            crate::running::binary_sha256(&first).unwrap(),
            crate::running::binary_sha256(&second).unwrap(),
            "the two generations must really differ"
        );

        let out = install_cli_binary(&req("victim", &first, &bin)).unwrap();

        // Start it, and WAIT until its text segment is genuinely mapped — reusing the
        // settle wait that exists because `/proc/<pid>/exe` is unreadable between fork
        // and execve.
        let mut child = std::process::Command::new(&out.installed)
            .args(["--exact", "--nocapture", "cli_install::tests::victim_holds_a_text_segment_when_asked"])
            .env("SKID_CLI_DOOR_VICTIM", "1")
            .stdout(std::process::Stdio::null())
            .stderr(std::process::Stdio::null())
            .spawn()
            .expect("spawn the installed command");
        let running = crate::running::settle_running_exe_sha256(
            child.id(),
            crate::running::EXEC_SETTLE_TIMEOUT,
        );
        // If the platform will not let us confirm a live text segment, the red below is
        // not being demonstrated — say so rather than passing a hollow test.
        let running = match running {
            Ok(sha) => sha,
            Err(e) => {
                let _ = child.kill();
                panic!("could not confirm the victim is running ({e}) — this test proves nothing without it");
            }
        };
        assert_eq!(running, out.sha256, "the process is running the installed bytes");

        // RED: the obvious implementation.
        let direct = std::fs::copy(&second, &out.installed);
        let err = direct.expect_err("a direct copy onto a running command must fail");
        assert_eq!(
            err.raw_os_error(),
            Some(26),
            "expected ETXTBSY (Text file busy), got {err}"
        );

        // GREEN: the door.
        let out2 = install_cli_binary(&req("victim", &second, &bin))
            .expect("stage-then-rename replaces a running command");
        let on_disk = crate::running::binary_sha256(&out2.installed).unwrap();
        assert_eq!(
            on_disk,
            crate::running::binary_sha256(&second).unwrap(),
            "the NEW bytes must be on disk, asserted independently of the return value"
        );
        assert_eq!(out2.replaced.as_deref(), Some(out.sha256.as_str()));
        assert!(out2.changed);

        // The old process keeps its own image — that is why the rename was safe.
        let still = crate::running::running_exe_sha256(child.id()).unwrap();
        assert_eq!(still, out.sha256, "the running process kept the old inode");

        let _ = child.kill();
        let _ = child.wait();
    }

    /// ★ A reinstall of identical bytes reports `changed = false`.
    ///
    /// An installer that always claims an update trains its reader to ignore it, and
    /// `--refresh` needs to distinguish "already current" from "just replaced".
    #[test]
    fn reinstalling_identical_bytes_is_reported_as_unchanged() {
        let tmp = tempfile::tempdir().unwrap();
        let bin = tmp.path().join("bin");
        let src = version_stub(tmp.path(), "src", "tool 1.0 (abcdef)");

        let first = install_cli_binary(&req("tool", &src, &bin)).unwrap();
        assert!(first.changed && first.replaced.is_none());

        let again = install_cli_binary(&req("tool", &src, &bin)).unwrap();
        assert!(!again.changed, "identical bytes are not an update");
        assert_eq!(again.replaced.as_deref(), Some(first.sha256.as_str()));
        assert_eq!(again.sha256, first.sha256);
    }

    /// ★ RED — the refusals that keep a bad artifact out, each proven to fire.
    #[test]
    fn a_source_that_is_not_an_already_built_command_is_refused() {
        let tmp = tempfile::tempdir().unwrap();
        let bin = tmp.path().join("bin");

        // absent
        let missing = tmp.path().join("was-never-built");
        let err = install_cli_binary(&req("tool", &missing, &bin)).unwrap_err();
        assert!(
            format!("{err:#}").contains("never builds one"),
            "an absent artifact must say the door does not build: {err:#}"
        );

        // present but not executable — the shape of a file that was written, not built
        let dud = tmp.path().join("dud");
        std::fs::write(&dud, b"not a program").unwrap();
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            std::fs::set_permissions(&dud, std::fs::Permissions::from_mode(0o644)).unwrap();
        }
        let err = install_cli_binary(&req("tool", &dud, &bin)).unwrap_err();
        assert!(
            format!("{err:#}").contains("no execute bit"),
            "a non-executable artifact must be refused: {err:#}"
        );

        // a name that is a path
        let ok = version_stub(tmp.path(), "src", "tool 1.0 (abcdef)");
        let err = install_cli_binary(&req("sub/tool", &ok, &bin)).unwrap_err();
        assert!(
            format!("{err:#}").contains("no path separators"),
            "a name with a separator must be refused: {err:#}"
        );
    }

    /// ★ The staging file never survives — neither on success nor on the failure path.
    /// A `.dwarves.new-1234` left in `~/.cargo/bin` is a half-installed command sitting
    /// where a shell completion will find it.
    #[test]
    fn no_staging_file_is_left_behind() {
        let tmp = tempfile::tempdir().unwrap();
        let bin = tmp.path().join("bin");
        let src = version_stub(tmp.path(), "src", "tool 1.0 (abcdef)");

        install_cli_binary(&req("tool", &src, &bin)).unwrap();
        let strays: Vec<_> = std::fs::read_dir(&bin)
            .unwrap()
            .map(|e| e.unwrap().file_name().to_string_lossy().to_string())
            .filter(|n| n != "tool")
            .collect();
        assert!(strays.is_empty(), "bin dir must hold only the command, got {strays:?}");

        // And on the failing path (a marker that will not match).
        let mut bad = req("tool", &src, &bin);
        bad.expect_marker = Some("no-such-build");
        assert!(install_cli_binary(&bad).is_err());
        let strays: Vec<_> = std::fs::read_dir(&bin)
            .unwrap()
            .map(|e| e.unwrap().file_name().to_string_lossy().to_string())
            .filter(|n| n != "tool")
            .collect();
        assert!(strays.is_empty(), "a failed install must leave no staging file, got {strays:?}");
    }
}