//! First-run codesign autopilot.
//!
//! macOS HVF requires the `com.apple.security.hypervisor`
//! entitlement on whatever process calls `hv_vm_create`. In our
//! architecture that's the `supermachine-worker` binary, not the
//! `supermachine` CLI itself.
//!
//! `cargo install supermachine` builds an unsigned worker on the
//! user's machine. To make `cargo install … && supermachine run X`
//! Just Work with no manual setup, the CLI signs the worker with
//! the bundled entitlements plist on its first invocation —
//! transparently, in ~30–50 ms — and writes a sentinel so
//! subsequent invocations skip the signing in ~1 ms.
//!
//! The same path covers the dev-tree case where `cargo build`
//! strips the entitlement on every rebuild: the next CLI launch
//! re-signs automatically.

use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::OnceLock;
use std::time::Duration;

/// Spawn `worker_path --version` once, parse the
/// `supermachine-worker <semver>` line, compare to this crate's
/// version. Mismatch → `Err` with an integrator-actionable message
/// that names the path being used and the fix.
///
/// This catches the deadlock pattern where a stale
/// `~/.cargo/bin/supermachine-worker` (from an older `cargo install
/// supermachine`) is silently picked up by the locator
/// (priority 4 in `locate_worker_bin`) when the embedder bumps
/// their library dependency. Without this check the symptom is a
/// silent hang at "pool-worker connected" — the library expects
/// new-protocol messages (BAKE_READY/SNAPSHOT_ASYNC, added in
/// 0.4.6) the old worker doesn't speak, and both sides block
/// reading from each other.
///
/// Bounded at 5 s; if `--version` doesn't return in that window
/// we treat it as a stale binary too (a healthy worker prints +
/// exits in <100 ms).
pub fn verify_worker_version(worker_path: &Path) -> Result<(), String> {
    static VERSION_CHECKED: OnceLock<()> = OnceLock::new();
    if VERSION_CHECKED.get().is_some() {
        return Ok(());
    }
    let lib_version = env!("CARGO_PKG_VERSION");
    // Run `<worker> --version`. Capture stdout. We pipe a separate
    // thread to enforce the 5 s ceiling; child.wait_timeout would
    // be cleaner but pulls a dep.
    let output = match Command::new(worker_path)
        .arg("--version")
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
    {
        Ok(c) => c,
        Err(e) => {
            return Err(format!(
                "verify_worker_version: spawn {} --version: {e}",
                worker_path.display()
            ));
        }
    };
    let pid = output.id();
    let stdout_handle = std::thread::spawn(move || -> Result<(std::process::Output, ()), String> {
        let out = output
            .wait_with_output()
            .map_err(|e| format!("wait_with_output: {e}"))?;
        Ok((out, ()))
    });
    let deadline = std::time::Instant::now() + Duration::from_secs(5);
    let out = loop {
        if stdout_handle.is_finished() {
            break stdout_handle
                .join()
                .map_err(|_| "version-probe thread panicked".to_string())?
                .map(|(o, _)| o)?;
        }
        if std::time::Instant::now() > deadline {
            // SAFETY: kill(2) by pid is best-effort; thread will
            // wake on EPIPE and surface the reaped child.
            unsafe {
                libc::kill(pid as i32, libc::SIGKILL);
            }
            return Err(format!(
                "supermachine: worker `{}` did not respond to --version within 5s. \
                 This is almost certainly a stale supermachine-worker from an older \
                 release (the --version flag was added in 0.4.18). Run `cargo install \
                 supermachine --force` to update, or set SUPERMACHINE_WORKER_BIN to a \
                 freshly-built worker matching library version {lib_version}.",
                worker_path.display()
            ));
        }
        std::thread::sleep(Duration::from_millis(20));
    };
    if !out.status.success() {
        // `unknown arg: --version` from older workers (pre-0.4.18)
        // lands here. Surface the actionable fix.
        let stderr = String::from_utf8_lossy(&out.stderr);
        return Err(format!(
            "supermachine: worker `{}` is from an older supermachine release \
             (does not recognize --version, added in 0.4.18). Library is \
             v{lib_version}; the supervisor protocol changed between releases \
             (0.4.6 added BAKE_READY/SNAPSHOT_ASYNC) and a stale worker \
             deadlocks pipelined-bake silently. \
             Fix: `cargo install supermachine --force` to update the worker, \
             or set SUPERMACHINE_WORKER_BIN to a freshly-built one. \
             (worker stderr: {})",
            worker_path.display(),
            stderr.trim(),
        ));
    }
    let stdout = String::from_utf8_lossy(&out.stdout);
    let line = stdout.lines().next().unwrap_or("").trim();
    // Expected: `supermachine-worker <semver>`
    let worker_version = line
        .strip_prefix("supermachine-worker ")
        .ok_or_else(|| {
            format!(
                "supermachine: worker `{}` --version output unparseable: {line:?}. \
                 Expected `supermachine-worker <semver>`. Suggests a non-supermachine \
                 binary at this path or a build with a different output format.",
                worker_path.display()
            )
        })?;
    if worker_version != lib_version {
        return Err(format!(
            "supermachine: worker/library version mismatch. \
             Worker `{}` reports v{worker_version}; library is v{lib_version}. \
             Snapshot format and supervisor protocol are tied to crate version — \
             pin both to the same `=`-version. \
             Fix: `cargo install supermachine --force` to update the worker, \
             or set SUPERMACHINE_WORKER_BIN to a worker matching v{lib_version}.",
            worker_path.display()
        ));
    }
    VERSION_CHECKED.set(()).ok();
    Ok(())
}

/// Sign `worker_path` with the bundled HVF entitlement (ad-hoc),
/// idempotent. Caches the result both in-process (one signing
/// attempt per CLI invocation) and on disk (one across CLI
/// invocations until the worker binary changes).
///
/// Returns `Ok(())` on success or no-op skip; `Err` on codesign
/// failure with a message suitable for the user. Callers should
/// not panic on `Err` — we'd rather let `hv_vm_create` surface
/// its own error than block on a codesign issue.
pub fn ensure_worker_signed(worker_path: &Path) -> Result<(), String> {
    static IN_PROCESS_DONE: OnceLock<()> = OnceLock::new();
    if IN_PROCESS_DONE.get().is_some() {
        return Ok(());
    }

    // The sentinel records the (size, mtime, path) the worker had
    // **after the most recent successful sign**. We compare today's
    // stat against that and skip re-signing if it matches. Critical
    // detail: codesign rewrites the binary, which bumps mtime on
    // every successful sign — so the cached sentinel must record
    // the *post-sign* mtime, not the pre-sign one. Otherwise every
    // future call sees current_mtime ≠ sentinel_mtime, re-signs,
    // bumps mtime again, and the bake-key (which includes the
    // worker's mtime) never matches a previously baked snapshot —
    // every fresh process rebakes from scratch.
    let stat_marker = |path: &Path| -> Option<String> {
        let meta = std::fs::metadata(path).ok()?;
        let mtime = meta
            .modified()
            .ok()?
            .duration_since(std::time::UNIX_EPOCH)
            .ok()?
            .as_secs();
        Some(format!(
            "size={}\nmtime={}\npath={}\n",
            meta.len(),
            mtime,
            path.display()
        ))
    };

    let current_marker = stat_marker(worker_path).ok_or_else(|| {
        format!("stat {}: file disappeared", worker_path.display())
    })?;

    if let Some(sentinel) = sentinel_path() {
        if let Ok(existing) = std::fs::read_to_string(&sentinel) {
            if existing == current_marker {
                IN_PROCESS_DONE.set(()).ok();
                return Ok(());
            }
        }
    }

    // Clean up any orphan `<worker>.cstemp` from a prior aborted
    // signing run BEFORE we invoke codesign. macOS's `codesign`
    // writes the freshly-signed Mach-O to `<worker>.cstemp` and
    // then atomically renames it over the original path. If the
    // rename fails (e.g. the target had `chflags uchg`, or a
    // previous process was killed mid-sign), the `.cstemp` is
    // left orphaned AND codesign sets `uchg` on it as part of
    // its internal write-locking. Every subsequent
    // `codesign --force` then tries to write to the same
    // `.cstemp` path, can't overwrite it (uchg), and bails with
    // a cryptic "internal error in Code Signing subsystem" —
    // poisoning the user's worker install indefinitely. The user
    // can't even fix it via `npm install` because npm doesn't
    // touch `.cstemp`.
    //
    // Best-effort cleanup: clear flags, remove. Any failure is
    // OK — codesign will produce a real error on the next call
    // that surfaces what's wrong.
    cleanup_stuck_cstemp(worker_path);

    // Drop the entitlements plist (compile-time include_str) to a
    // temp file; codesign needs it on disk. Unique-per-pid so two
    // concurrent CLI invocations don't race on the same temp path.
    let plist = std::env::temp_dir().join(format!(
        "supermachine-entitlements-{}.plist",
        std::process::id()
    ));
    std::fs::write(&plist, crate::assets::ENTITLEMENTS_PLIST)
        .map_err(|e| format!("write entitlements plist: {e}"))?;

    let output = Command::new("codesign")
        .args(["-s", "-", "--entitlements"])
        .arg(&plist)
        .arg("--force")
        .arg(worker_path)
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .output();
    let _ = std::fs::remove_file(&plist);

    match output {
        Ok(o) if o.status.success() => {
            // Re-stat AFTER signing so the sentinel records the
            // post-sign mtime — that's what future stat() calls
            // (and the bake-key calculation) will see.
            if let (Some(sentinel), Some(post_marker)) =
                (sentinel_path(), stat_marker(worker_path))
            {
                if let Some(parent) = sentinel.parent() {
                    let _ = std::fs::create_dir_all(parent);
                }
                let _ = std::fs::write(&sentinel, post_marker);
            }
            IN_PROCESS_DONE.set(()).ok();
            Ok(())
        }
        Ok(o) => {
            // Surface the actual codesign error AND the recovery
            // hint. Without this, downstream code sees only
            // `hv_vm_create` returning HV_DENIED minutes later
            // with no diagnosis of the autopilot failure.
            let stderr = String::from_utf8_lossy(&o.stderr);
            let stderr_trim = stderr.trim();
            // Specifically catch the "internal error in Code
            // Signing subsystem" pattern that the
            // uchg-on-cstemp loop produces. The recovery hint
            // is exact even though we already tried to clean up
            // above — if cleanup_stuck_cstemp couldn't clear the
            // flag (permission denied on someone else's file,
            // disk full, …) the user needs to know what to do.
            let extra = if stderr_trim.contains("internal error in Code Signing") {
                format!(
                    "\n\nThis is the orphan-`.cstemp` recovery loop: a prior \
                     codesign attempt left `{cs}` behind with chflags uchg \
                     set on it, and macOS codesign can't overwrite it. \
                     Manual fix:\n\
                     \n\
                     \x20\x20chflags -R nouchg {dir}\n\
                     \x20\x20rm -f {cs}\n\
                     \n\
                     Then retry your bake.",
                    cs = cstemp_for(worker_path).display(),
                    dir = worker_path
                        .parent()
                        .map(|p| p.display().to_string())
                        .unwrap_or_else(|| "<worker dir>".to_owned()),
                )
            } else {
                String::new()
            };
            Err(format!(
                "codesign exited with {:?} for {}: {stderr_trim}{extra}",
                o.status.code(),
                worker_path.display()
            ))
        }
        Err(e) => Err(format!(
            "failed to spawn codesign for {}: {e}\n\
             (codesign ships with macOS by default; if missing, \
             reinstall Xcode Command Line Tools)",
            worker_path.display()
        )),
    }
}

/// Compute `<worker>.cstemp` next to a worker binary. macOS
/// `codesign` uses this name for its atomic-rename staging
/// file. Exposed for diagnostic messages.
fn cstemp_for(worker_path: &Path) -> PathBuf {
    let mut s = worker_path.as_os_str().to_owned();
    s.push(".cstemp");
    PathBuf::from(s)
}

/// Clear `chflags uchg` and unlink any orphan `<worker>.cstemp`
/// next to the worker binary. macOS codesign sets `uchg` on the
/// `.cstemp` during signing as an internal write-lock; if the
/// final rename fails (target was itself uchg-locked, or
/// codesign was killed mid-write), the cstemp is left behind
/// permanently locked — and every subsequent codesign --force
/// fails with "internal error in Code Signing subsystem" until
/// the cstemp is manually removed. We pre-emptively clean it
/// here so the autopilot is self-healing.
///
/// Best-effort: any failure is silent. The next codesign call
/// will surface a real diagnostic if the underlying problem
/// isn't a stuck cstemp.
fn cleanup_stuck_cstemp(worker_path: &Path) {
    let cstemp = cstemp_for(worker_path);
    if !cstemp.exists() {
        return;
    }
    // Try `chflags -R nouchg` first — covers `uchg`, `schg`, and
    // any other flag chflags can clear. `-R` is safe on a file
    // path (no recursion happens).
    let _ = Command::new("chflags")
        .args(["-R", "nouchg"])
        .arg(&cstemp)
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status();
    let _ = std::fs::remove_file(&cstemp);
}

/// Locate `supermachine-worker`. The single source of truth —
/// the bake pipeline delegates here so the two paths can't drift
/// apart again. Resolution order, returning the first hit:
///
///   1. `$SUPERMACHINE_WORKER_BIN` (explicit override; must
///      exist as a file or it's ignored — protects against stale
///      env vars silently shadowing a working install).
///   2. Sibling of the currently running binary
///      (`cargo install supermachine` layout, where every
///      supermachine-* binary lands in the same directory).
///   3. Sibling of the *canonicalized* running binary
///      (handles `~/.local/bin/supermachine` → dev-tree symlinks).
///   4. `$CARGO_HOME/bin/supermachine-worker` (or
///      `~/.cargo/bin/supermachine-worker`) — the canonical
///      `cargo install` location, picked up even when an
///      embedder's *own* binary is `current_exe`.
///   5. Walk `$PATH` for `supermachine-worker` — anything on
///      PATH counts (release tarball, package manager install,
///      symlink farm, …).
///   6. Ancestor walk for `target/release/supermachine-worker`
///      (cargo dev-tree fallback when running tests/examples).
pub fn locate_worker_bin() -> Option<PathBuf> {
    if let Some(p) = std::env::var_os("SUPERMACHINE_WORKER_BIN") {
        let p = PathBuf::from(p);
        if p.is_file() {
            return Some(p);
        }
    }
    if let Ok(exe) = std::env::current_exe() {
        if let Some(p) = sibling_worker(&exe) {
            return Some(p);
        }
        if let Ok(canonical) = std::fs::canonicalize(&exe) {
            if canonical != exe {
                if let Some(p) = sibling_worker(&canonical) {
                    return Some(p);
                }
            }
        }
    }
    if let Some(p) = cargo_bin_worker() {
        return Some(p);
    }
    if let Some(p) = path_walk_worker() {
        return Some(p);
    }
    if let Ok(exe) = std::env::current_exe() {
        for ancestor in exe.ancestors() {
            let p = ancestor.join("target/release/supermachine-worker");
            if p.is_file() {
                return Some(p);
            }
        }
        if let Ok(canonical) = std::fs::canonicalize(&exe) {
            for ancestor in canonical.ancestors() {
                let p = ancestor.join("target/release/supermachine-worker");
                if p.is_file() {
                    return Some(p);
                }
            }
        }
    }
    None
}

fn sibling_worker(exe: &Path) -> Option<PathBuf> {
    let dir = exe.parent()?;
    let p = dir.join("supermachine-worker");
    if p.is_file() {
        Some(p)
    } else {
        None
    }
}

fn cargo_bin_worker() -> Option<PathBuf> {
    let bin_dir = if let Some(cargo) = std::env::var_os("CARGO_HOME") {
        PathBuf::from(cargo).join("bin")
    } else if let Some(home) = std::env::var_os("HOME") {
        PathBuf::from(home).join(".cargo").join("bin")
    } else {
        return None;
    };
    let p = bin_dir.join("supermachine-worker");
    if p.is_file() {
        Some(p)
    } else {
        None
    }
}

fn path_walk_worker() -> Option<PathBuf> {
    let path = std::env::var_os("PATH")?;
    for dir in std::env::split_paths(&path) {
        let p = dir.join("supermachine-worker");
        if p.is_file() {
            return Some(p);
        }
    }
    None
}

/// Check whether the *currently running* binary has the
/// `com.apple.security.hypervisor` entitlement. Returns `Ok(())`
/// if it does, or a [`String`] describing the situation for the
/// caller to surface as an error if it doesn't.
///
/// Used by [`crate::Vm::start`] to fail fast with a clear message
/// instead of letting `hv_vm_create` return the cryptic
/// `Hv(-85377017)` (HV_DENIED) when the embedder forgot to sign
/// their binary. `Image::acquire` doesn't need this check — the
/// auto-signed worker subprocess handles HVF for those callers.
///
/// We cache the result in-process: the entitlement on a running
/// binary doesn't change underneath us.
pub fn check_self_has_hvf_entitlement() -> Result<(), String> {
    static CACHED: OnceLock<Result<(), String>> = OnceLock::new();
    CACHED
        .get_or_init(check_self_has_hvf_entitlement_uncached)
        .clone()
}

fn check_self_has_hvf_entitlement_uncached() -> Result<(), String> {
    let exe = std::env::current_exe().map_err(|e| {
        format!(
            "could not resolve current_exe to check HVF entitlement: {e} \
             (your binary may need to be codesigned with \
             `cargo supermachine build`)"
        )
    })?;
    let output = Command::new("codesign")
        .args(["--display", "--entitlements", "-", "--xml"])
        .arg(&exe)
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .output();
    let output = match output {
        Ok(o) => o,
        Err(_) => return Ok(()),  // codesign missing — best-effort skip
    };
    if !output.status.success() {
        // Not signed at all, or signature is invalid.
        return Err(missing_entitlement_message(&exe));
    }
    let stdout = String::from_utf8_lossy(&output.stdout);
    if stdout.contains("com.apple.security.hypervisor") {
        Ok(())
    } else {
        Err(missing_entitlement_message(&exe))
    }
}

fn missing_entitlement_message(exe: &Path) -> String {
    format!(
        "this binary lacks the `com.apple.security.hypervisor` entitlement, \
         so `Vm::start` cannot call `hv_vm_create` (it would return HV_DENIED).\n\
         \n\
         Two ways to fix:\n\
         \n\
           (a) Use `Image::acquire` / `Image::acquire_with` instead of \
               `Vm::start`. The library spawns a pre-signed \
               `supermachine-worker` subprocess that handles HVF on your \
               behalf, so your own binary never calls into HVF and doesn't \
               need codesigning. This is the recommended path for embedders.\n\
           (b) Build your binary with the bundled cargo plugin:\n\
                   cargo supermachine build --release\n\
               which wraps `cargo build` and codesigns the output with the \
               HVF entitlement. Use this if you specifically want the \
               in-process VM thread (`Vm::start`).\n\
         \n\
         Path: {}",
        exe.display()
    )
}

/// `$XDG_DATA_HOME/supermachine/v{VERSION}/.worker-signed` (or the
/// `$HOME/.local/share/...` fallback). Versioned so a supermachine
/// upgrade doesn't reuse the prior version's sentinel.
fn sentinel_path() -> Option<PathBuf> {
    let base = if let Some(d) = std::env::var_os("XDG_DATA_HOME") {
        PathBuf::from(d)
    } else if let Some(h) = std::env::var_os("HOME") {
        PathBuf::from(h).join(".local/share")
    } else {
        return None;
    };
    Some(
        base.join("supermachine")
            .join(format!("v{}", env!("CARGO_PKG_VERSION")))
            .join(".worker-signed"),
    )
}

#[cfg(test)]
mod cstemp_tests {
    //! Coverage for the 0.7.28 stuck-`.cstemp` self-heal. The
    //! cleanup logic is best-effort + silent in production; tests
    //! pin its shape so regressions get caught at unit-test time
    //! rather than via field reports.
    use super::{cleanup_stuck_cstemp, cstemp_for};
    use std::io::Write;
    use std::path::PathBuf;
    use std::sync::atomic::{AtomicU64, Ordering};

    static TEST_ID: AtomicU64 = AtomicU64::new(0);

    /// Unique temp file path scoped to this test invocation.
    /// Cleans up on drop, including the `.cstemp` sibling we
    /// might have created.
    struct TempWorker {
        path: PathBuf,
    }
    impl TempWorker {
        fn new() -> Self {
            let id = TEST_ID.fetch_add(1, Ordering::Relaxed);
            let path = std::env::temp_dir().join(format!(
                "supermachine-cstemp-test-{}-{id}",
                std::process::id()
            ));
            let mut f = std::fs::File::create(&path).expect("create temp worker");
            f.write_all(b"fake-worker-bytes").expect("write temp worker");
            Self { path }
        }
        fn cstemp(&self) -> PathBuf {
            cstemp_for(&self.path)
        }
        fn create_cstemp(&self) {
            let mut f = std::fs::File::create(self.cstemp()).expect("create cstemp");
            f.write_all(b"orphan-cstemp-contents").expect("write cstemp");
        }
    }
    impl Drop for TempWorker {
        fn drop(&mut self) {
            // Best-effort cleanup. If a test put `uchg` on the
            // cstemp without clearing it, the unlink will fail.
            // Try `chflags -R nouchg` then unlink anyway. Silence
            // stderr so missing-file noise from already-cleaned-up
            // cstemp files doesn't pollute test output.
            let cs = self.cstemp();
            let _ = std::process::Command::new("chflags")
                .args(["-R", "nouchg"])
                .arg(&cs)
                .arg(&self.path)
                .stdout(std::process::Stdio::null())
                .stderr(std::process::Stdio::null())
                .status();
            let _ = std::fs::remove_file(&cs);
            let _ = std::fs::remove_file(&self.path);
        }
    }

    #[test]
    fn cstemp_for_appends_dot_cstemp() {
        let worker = PathBuf::from("/usr/local/bin/supermachine-worker");
        let cs = cstemp_for(&worker);
        assert_eq!(
            cs,
            PathBuf::from("/usr/local/bin/supermachine-worker.cstemp")
        );
    }

    #[test]
    fn cstemp_for_handles_worker_in_root() {
        let worker = PathBuf::from("/worker");
        assert_eq!(cstemp_for(&worker), PathBuf::from("/worker.cstemp"));
    }

    #[test]
    fn cstemp_for_handles_relative_path() {
        let worker = PathBuf::from("./bin/w");
        assert_eq!(cstemp_for(&worker), PathBuf::from("./bin/w.cstemp"));
    }

    #[test]
    fn cleanup_no_op_when_no_cstemp_exists() {
        let w = TempWorker::new();
        assert!(!w.cstemp().exists());
        // Should not panic, error, or create anything.
        cleanup_stuck_cstemp(&w.path);
        assert!(!w.cstemp().exists());
        // Worker binary itself stays put.
        assert!(w.path.exists());
    }

    #[test]
    fn cleanup_removes_plain_orphan_cstemp() {
        let w = TempWorker::new();
        w.create_cstemp();
        assert!(w.cstemp().exists());
        cleanup_stuck_cstemp(&w.path);
        assert!(
            !w.cstemp().exists(),
            "cleanup should unlink orphan cstemp"
        );
        // The worker binary itself MUST NOT be touched.
        assert!(w.path.exists());
    }

    #[test]
    #[cfg(target_os = "macos")]
    fn cleanup_removes_uchg_locked_cstemp() {
        // Specifically the bug we're guarding against: cstemp with
        // chflags uchg set. Plain `unlink` would fail with EPERM;
        // the cleanup must `chflags -R nouchg` first.
        let w = TempWorker::new();
        w.create_cstemp();
        // Mark the cstemp uchg.
        let status = std::process::Command::new("chflags")
            .arg("uchg")
            .arg(w.cstemp())
            .status();
        // If chflags isn't available in this CI shape, skip — the
        // bug only manifests on real macOS.
        match status {
            Ok(s) if s.success() => {}
            _ => return,
        }
        // Sanity: plain unlink WOULD fail right now.
        let plain = std::fs::remove_file(w.cstemp());
        assert!(plain.is_err(), "uchg-locked file should reject plain unlink");
        // The cleanup path should succeed.
        cleanup_stuck_cstemp(&w.path);
        assert!(
            !w.cstemp().exists(),
            "cleanup should chflags-then-unlink even uchg-locked cstemp"
        );
    }

    #[test]
    fn cleanup_silent_when_cstemp_disappears_mid_call() {
        // Race: another process unlinks the cstemp between our
        // `.exists()` check and the unlink. Cleanup should not
        // panic or surface an error (it's best-effort).
        let w = TempWorker::new();
        w.create_cstemp();
        // Pre-emptively remove it (simulates the race).
        std::fs::remove_file(w.cstemp()).expect("remove cstemp");
        // The path doesn't exist anymore; cleanup must no-op.
        cleanup_stuck_cstemp(&w.path);
        // No assertion needed beyond "didn't panic" — the test
        // would fail on a panic in the cleanup helper.
    }
}
