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

nordisk/gunnar

xtask/src/main.rs

//! `cargo xtask <task>`: gunnar's repository tasks.
//!
//! ```text
//!   cargo xtask guard       the dependency guard, against RESOLVED graphs
//!   cargo xtask cve         the 0-known-CVE gate: cargo-deny advisories + bans
//!   cargo xtask preflight   assert the external binaries the tests need
//!   cargo xtask ci          fmt, clippy, preflight, tests, appliance, front-guest, guard
//!   cargo xtask bare        gunnar as an ORDINARY PROCESS: up, serve real git,
//!                           down, and the up/down/status/pressure verbs
//! ```
//!
//! Nothing here is a build dependency of anything: repository tooling is
//! invoked as a tool, never as a dependency.
//!
//! # This is the CI, and it is host-agnostic on purpose
//!
//! `cargo xtask ci` is not a convenience wrapper around a "real" CI that lives
//! somewhere else. **It is the whole of gunnar's CI** — decided 2026-08-05 and
//! written down, not left open. Every rule is here, in Rust, runnable on a
//! desktop.
//!
//! What changed on 2026-09-11 is only WHO TYPES IT. This file used to say
//! "there is no `.github/` in this repository", and the reason it gave was a
//! good one: one existed between `b07ca38` and `e50b962` (2026-08-03), never
//! executed once, and was deleted by a commit that said it touched only prose.
//! But a command a human types is a command a human can forget, and the CVE
//! gate is the step where forgetting is expensive: it was proved per deploy and
//! per remembered invocation, never on the branch that goes straight to a tag.
//! So `.github/workflows/ci.yml` exists now, and every step in it is ONE
//! `cargo` line calling back into here. The host still learns nothing about
//! gunnar's rules, and gunnar's rules still learn nothing about the host —
//! `xtask/src/ci_check.rs` is the guard that keeps that true, and it is a step
//! of this command.
//!
//! It needs nothing but cargo and a shell-free `Command`. It reads no `GITHUB_*`
//! variable, emits no `::error::` annotation and assumes no runner image, so the
//! same command is what a developer runs on the desktop and what any build server would
//! run if one were ever adopted. Keep it that way: the moment this file learns
//! about a specific host, gunnar's checks stop being portable and start being
//! that host's property.
//!
//! Because a human types it, the one thing it must never do is cover less than
//! it says. The worked example was the sibling-znippy probe: run from a linked
//! worktree it printed `NOT RUN` for two steps and returned SUCCESS, on a machine
//! where both would have passed. It is deleted now — the sibling it asked about is
//! a registry dependency — and the rule it taught is not.

use std::path::{Path, PathBuf};
use std::process::{Command, ExitCode};

use xtask::{fmt, guard};

// ── fmt ─────────────────────────────────────────────────────────────────────

/// `cargo xtask fmt [--write]`, and — via the `fmt` alias in
/// `.cargo/config.toml` — also what plain `cargo fmt` runs inside this
/// repository.
///
/// Defaults to `--check`, because the alias means this is now what a bare
/// `cargo fmt` does, and a bare command that silently rewrites 133 files is not
/// a thing to inherit by surprise. `xtask/src/fmt.rs` carries the whole argument
/// for why `cargo fmt --all` cannot be used here.
/// `cargo xtask trailer-guard [--commits N]` — report attribution trailers,
/// change nothing.
///
/// **Not in `ci` on purpose.** It walks git history, and a repository that has
/// been clean for a year does not need that re-proved on every build; `ci`
/// staying fast is worth more than a guard against a mistake a human makes at
/// commit time. Run it before a push, or when taking over a branch somebody
/// else wrote.
fn task_trailer_guard() -> ExitCode {
    let args: Vec<String> = std::env::args().skip(2).collect();
    let mut limit = None;
    let mut it = args.iter();
    while let Some(a) = it.next() {
        match a.as_str() {
            "--commits" => match it.next().and_then(|v| v.parse().ok()) {
                Some(n) => limit = Some(n),
                None => {
                    eprintln!("xtask trailer-guard: --commits needs a number");
                    return ExitCode::FAILURE;
                }
            },
            other => {
                eprintln!("xtask trailer-guard: unknown argument `{other}`");
                return ExitCode::FAILURE;
            }
        }
    }
    match xtask::trailer_guard::run(&guard::workspace_root(), limit) {
        Ok(()) => ExitCode::SUCCESS,
        Err(e) => {
            eprintln!("xtask trailer-guard: {e}");
            ExitCode::FAILURE
        }
    }
}

/// `cargo xtask publish [--push]`; see [`xtask::publish`].
///
/// Two steps, and the second is the one that matters: it writes branch `public`
/// from HEAD's tree minus the declared exclusion list, and then compiles that
/// branch in a throwaway worktree. Excluding a file a build reads produces a
/// branch that looks right and does not build, and nothing about writing the
/// list would have said so.
fn task_publish() -> ExitCode {
    let mut push = false;
    for a in std::env::args().skip(2) {
        match a.as_str() {
            "--push" => push = true,
            other => {
                eprintln!("xtask publish: unknown argument `{other}`");
                eprintln!("usage: cargo xtask publish [--push]");
                return ExitCode::FAILURE;
            }
        }
    }
    match xtask::publish::run(&guard::workspace_root(), push) {
        Ok(r) => {
            println!("\npublic tree {}", r.tree);
            println!("commit      {}", r.commit);
            println!("branch      public");
            println!("\n  kept      {} path(s)", r.kept);
            println!("  excluded  {} path(s):", r.excluded_total());
            for (why, n) in &r.excluded_by_reason {
                println!("    {n:>4}  {why}");
            }
            println!(
                "\n  cargo metadata + check -p gunnar-cli -p gunnar-server + readme_version: {}",
                if r.checked { "PASS" } else { "not run" }
            );
            if r.pushed {
                println!("  pushed to github-public (main) and gunnar-rs (master)");
            } else {
                println!("  nothing pushed; re-run with --push");
            }
            ExitCode::SUCCESS
        }
        Err(e) => {
            eprintln!("xtask publish: REFUSED\n{e}");
            ExitCode::FAILURE
        }
    }
}

fn task_fmt() -> ExitCode {
    let args: Vec<String> = std::env::args().skip(2).collect();
    let mut mode = fmt::Mode::Check;
    for a in &args {
        match a.as_str() {
            "--write" | "-w" => mode = fmt::Mode::Write,
            "--check" => mode = fmt::Mode::Check,
            // `--all` is the whole reason this task exists. Refuse it by name
            // rather than ignoring it: someone typing it believes it means
            // "everything in this repo", and it means neither that nor only
            // that.
            "--all" => {
                eprintln!("xtask fmt: refusing `--all`.");
                eprintln!();
                eprintln!("  `cargo fmt --all` follows LOCAL PATH DEPENDENCIES out of this");
                eprintln!("  repository. MEASURED 2026-08-04: 12556 diffs in five repos gunnar");
                eprintln!("  does not own (facett 8459, znippy-zoomies 2614, znippy 1262,");
                eprintln!("  Skidbladnir 218, nornir 3) against 583 of our own — and the writing");
                eprintln!("  form really did rewrite 923 of their files, which had to be reverted");
                eprintln!("  from saved patches.");
                eprintln!();
                eprintln!("  It is also too NARROW: it never reaches bench/ or");
                eprintln!("  installtest/, the detached workspaces.");
                eprintln!();
                eprintln!("  `cargo xtask fmt` with no argument already covers this whole");
                eprintln!("  repository and nothing outside it. That is what you want.");
                return ExitCode::FAILURE;
            }
            other => {
                eprintln!("xtask fmt: unknown argument `{other}`");
                eprintln!("usage: cargo xtask fmt [--check | --write]");
                return ExitCode::FAILURE;
            }
        }
    }
    ok(fmt::run(&guard::workspace_root(), mode))
}

fn main() -> ExitCode {
    let task = std::env::args().nth(1);
    match task.as_deref() {
        // Kept for callers that ran it first (monetize's build-guests, before it builds
        // crates/gunnar-monetize-guest `--locked`). Since G-B7 there is nothing to switch on:
        // each estate workspace carries the sibling patches it takes in its own manifest.
        Some("siblings") => {
            eprintln!("xtask siblings: nothing to switch on: the root takes no sibling patch, and each estate workspace ({}) carries the ones it takes in its own manifest (G-B7)", xtask::siblings::ESTATE_WORKSPACES.join(", "));
            ExitCode::SUCCESS
        }
        Some("trailer-guard") => task_trailer_guard(),
        Some("publish") => task_publish(),
        Some("guard") => task_guard(),
        Some("cve") => task_cve(),
        Some("fmt") => task_fmt(),
        Some("preflight") => ok(task_preflight()),
        Some("ci") => task_ci(),
        Some("fast") => task_fast(),
        Some("idp") => task_idp(),
        Some("build") => task_build(),
        Some("bench-build") => task_bench_build(),
        Some("bench-landed") => task_bench_landed(),
        Some("bench-readme") => task_bench_readme(),
        Some("bench") => task_bench(),
        Some("do-all") | Some("DO_ALL") => task_do_all(),
        Some("images") => task_images(),
        Some("svg") => task_svg(),
        Some("docs") => task_docs(),
        Some("mashup") => task_mashup(),
        Some("iso") => task_iso(),
        Some("iso-installer") => task_iso_installer(),
        Some("payload") => task_payload(),
        Some("reinstall-appliance") => task_reinstall_appliance(),
        Some("install-test") => task_install_test(),
        Some("deploy-check") => task_deploy_check(),
        Some("one-clone") => ok(xtask::one_clone::run(&guard::workspace_root())),
        Some("installer-pin") => ok(xtask::installer_pin::run(&guard::workspace_root())),
        Some("ci-check") => task_ci_check(),
        Some("key-census") => ok(xtask::key_census::run(&guard::workspace_root())),
        Some("free-tree") => ok(xtask::paid_guard::run(
            &guard::workspace_root(),
            &std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()),
        )),
        Some("lone-clone") => {
            let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
            let force = std::env::args().any(|a| a == "--force");
            match xtask::paid_guard::lone_clone(&guard::workspace_root(), &cargo, force) {
                xtask::paid_guard::LoneClone::Skipped(why) => {
                    println!("lone-clone: SKIPPED — {why} (`--force` runs it anyway)");
                    ExitCode::SUCCESS
                }
                xtask::paid_guard::LoneClone::Passed => ExitCode::SUCCESS,
                xtask::paid_guard::LoneClone::Failed(why) => {
                    eprintln!("lone-clone: ✗ {why}");
                    ExitCode::FAILURE
                }
            }
        }
        // `cargo xtask crates-io-install [--stage]`: `cargo install gunnar --features client`
        // from crates.io in an empty directory with an empty CARGO_HOME, or (`--stage`) the
        // packaged gunnar of this commit with the crates crates.io lacks patched in (G-BOTH-1).
        Some("crates-io-install") => {
            let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
            let staged = std::env::args().any(|a| a == "--stage");
            both_ways_verdict(
                if staged {
                    "crates-io-install-staged"
                } else {
                    "crates-io-install"
                },
                xtask::both_ways::crates_io_install(&guard::workspace_root(), &cargo, staged),
            )
        }
        // `cargo xtask twins`: every Build Thing cargo row resolves what its `cargo install`
        // line resolves and leaves the row's binary (G-BOTH-2).
        // `cargo xtask products [--no-console]`: the guide "Gunnar by hand", run as written.
        Some("products") => match xtask::products::main(
            &guard::workspace_root(),
            &std::env::args().skip(2).collect::<Vec<_>>(),
        ) {
            Ok(()) => ExitCode::SUCCESS,
            Err(e) => {
                eprintln!("xtask products: {e}");
                ExitCode::FAILURE
            }
        },
        Some("twins") => ok(xtask::twins::run(
            &guard::workspace_root(),
            &std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()),
        )),
        Some("bare") => task_bare(),
        Some("wireproxy") => task_wireproxy(),
        Some("roundtrip") => task_roundtrip(),
        Some(other) => {
            eprintln!("xtask: unknown task `{other}`");
            usage();
            ExitCode::FAILURE
        }
        None => {
            usage();
            ExitCode::FAILURE
        }
    }
}

/// `cargo xtask fast [--out DIR] [--budget-s N] [--twin-late-s N] [--no-build]`: Gunnar's
/// estate on monetize's monetize-fast, its real boxes in rootless containers (A28 H14).
fn task_fast() -> ExitCode {
    let argv: Vec<String> = std::env::args().skip(2).collect();
    match xtask::fast::run(&guard::workspace_root(), &argv) {
        Ok(()) => {
            println!("✅ fast: Gunnar's estate is green (target/fast/receipt.txt)");
            ExitCode::SUCCESS
        }
        Err(e) => {
            eprintln!("✗ fast failed: {e}");
            ExitCode::FAILURE
        }
    }
}

/// `cargo xtask idp [--with-red] [--keep] [<filter>]`: company SSO against a REAL Keycloak in a
/// container (`xtask/src/idp.rs`, `crates/gunnar-server/tests/sso_keycloak.rs`).
fn task_idp() -> ExitCode {
    let argv: Vec<String> = std::env::args().skip(2).collect();
    match xtask::idp::run(&guard::workspace_root(), &argv) {
        Ok(()) => {
            println!("✅ idp: company SSO is green against a real Keycloak");
            ExitCode::SUCCESS
        }
        Err(e) => {
            eprintln!("✗ idp failed: {e}");
            ExitCode::FAILURE
        }
    }
}

fn ok(passed: bool) -> ExitCode {
    if passed {
        ExitCode::SUCCESS
    } else {
        ExitCode::FAILURE
    }
}

fn usage() {
    eprintln!(
        "usage: cargo xtask <task>

  siblings    nothing to switch on since G-B7 (each estate workspace carries
              its sibling patches in its own manifest); kept for callers that
              run it first
  idp [--with-red] [--keep] [<filter>]
              company SSO against a REAL Keycloak in a container on a private
              podman network: OIDC, SAML, SCIM and the CSV import
              (crates/gunnar-server/tests/sso_keycloak.rs); needs podman
  deploy-check
              the deployment manifests under deploy/, asserted: the image is
              pinned by digest, the state is persistent, the keys are in the
              Secret, nothing is privileged, and the systemd units keep the
              sandbox reachable. `--red` proves every check bites.

  one-clone   every Cargo.toml and Cargo.lock in the tree, asserted to name
              nothing outside it. A `path = \"../edda/…\"` is read at RESOLVE
              time, so it makes `cargo metadata` — not merely a build — a hard
              error in a clone with nothing beside it.

  lone-clone [--stage]
              this commit cloned alone: `cargo build --locked`, `cargo install
              --locked --path crates/gunnar-cli --features client` and the same
              for crates/gunnar, the server booted. `--stage` packages this
              workspace's crates and patches in the ones crates.io lacks.

  crates-io-install [--stage]
              `cargo install gunnar --features client` from crates.io in an
              empty directory with an empty CARGO_HOME (and `--locked`);
              `--stage` installs the packaged gunnar of this commit instead.

  twins       every Build Thing cargo row resolves the same crates as its
              `cargo install` line and leaves the row's binary.

  ci-check    the workflows under .github/, asserted: something runs the CVE
              gate and the test suite per push, and every job's GITHUB_TOKEN can
              read and nothing else. `--red` proves every check bites.

  key-census  which tracked files hold a private key, base64-wrapped ones
              included. The three in assets/test/vetra-test-pki/ are the
              documented exception; a fourth fails.

  guard       the dependency guard — which crate may reach which — asserted against RESOLVED
              cargo trees for the appliance and default feature sets and for
              every S crate on its own
  publish     build the PUBLIC tree on branch `public`: HEAD's tree minus a
              declared exclusion list (the design record, the rendered books,
              the bench drivers, the working notes), as ONE orphan commit.
              Refuses a dirty tree and any HEAD that is not master, then proves
              the result with cargo metadata, `cargo check -p gunnar-cli -p
              gunnar-server` and the README version guard in a throwaway
              worktree beside this one. `--push` sends it to github-public
              (as main, --force-with-lease) and gunnar-rs (as master)
  cve         the 0-known-CVE gate: cargo-deny advisories + bans over gunnar's
              resolved Cargo.lock, keyed off deny.toml. The SAME gate the deploy
              `door cve` runs, made a CI step so a vulnerable dependency is
              refused per RELEASE, not only per deploy. Red-first proof in
              xtask/tests/cve.rs. Needs cargo-deny on PATH; a missing tool FAILS
  preflight   assert `git` and OpenSSH are on PATH. The interop tests
              shell out to them, and a test that skips itself is a green nobody
              has seen red
  ci          fmt, the bench guard, clippy, preflight, the test suite, the
              appliance build, the deployment and workflow checks, the guard and
              the CVE gate, in the order CI runs them
  fast        the fast test: Gunnar's box rows built, its container media, and
              monetize-fast's run of the estate in rootless containers, paired by
              the real ONE box (--out, --budget-s, --twin-late-s, --no-build);
              target/fast/receipt.txt. On the host: it starts containers
  bench-build the BENCH-TARGET GUARD: compile every detached bench manifest,
              `--all-targets` so the example targets the arms live in are
              actually built. `bench/` is a detached workspace, so `cargo
              build`, `cargo check --workspace`, `cargo test --workspace`,
              `cargo clippy --workspace` and `-p gunnar-bench` all miss it —
              it has silently stopped compiling twice. `--check` for the fast
  bench-readme
              README-BENCH: the README's measured table comes from the bencher's
              result files (crates/gunnar-perfbench/results/, schema
              nordisk-bench-result/1), never from a hand. `run --seeds <dir>
              --machine <name>` builds gunnar (both engines) and the bench, runs
              it on the real repositories in <dir> (git.git) and writes a new
              result file; `write` renders the block into .nornir/README.md and
              README.md; `check` is red when they differ from it; `compare
              --threshold <pct>` is red on a regression against the previous
              result of the same bench and machine (the weekly CI run);
              `import-reach --stdout <f> --stderr <f> --dataset <name>
              --machine <name> --engine <crate version> --commit <sha>` turns
              znippy-plugin-git's examples/reach_real_repo.rs output into a
              result file (the store-level rows of lane gitidx);
              `import-perfbench --jsonl <f> --bench <name> --machine <m>
              --date <d> --source-commit <what> --history <bare repo>`
              turns gunnar-perfbench's JSON lines (a run without
              GUNPERF_RESULT) into one, the repository counted by git
  bench-landed
              assert the NEWEST bench run reached BOTH stores — the iceberg
              `bench_runs` row and the `bench_history.jsonl` receipt — and print
              the agreement ratio over the whole receipt. Ten runs on 2026-08-12
              landed in the receipt and in no warehouse, and every command
              involved exited 0
              form (`cargo check`), which is what to run between edits
  svg         regenerate the generated figures: gunnar-znippy-cycle.svg and
              gunnar-data-path.svg from the verified-edge models in
              xtask/src/cycle_svg.rs and datapath_svg.rs — every box and arrow
              carries a file:line and a WIRED / BUILT-NOT-READ / NEVER-EMITTED
              verdict — and gunnar-setup.svg, the quick start's figure, from
              xtask/src/setup_svg.rs, whose arrows are held to the CLI's verbs.
              Edit the datum, never the picture
  docs        assemble docs/gunnar-manual.pdf and docs/gunnar-record.pdf from
              the .nornir/ sources and render them through `nornir docs export`
              (never `nornir docs book` — it hangs). Refuses to write a PDF that
              is truncated, has fewer pages than a cover plus contents, or
              embeds fewer images than the markdown referenced; refuses to
              render at all while a staged asset is an unsmudged Git-LFS
              pointer. Takes an optional book name: manual | record
  images      build every appliance container variant in containers/ with
              podman, start each one, clone from it with stock `git` over SSH,
              and print the size / starts / serves / freight table. Takes
              `--only NAME` and `--keep`
  iso-installer
              build the INSTALLER ISO — the medium that LAYS gunnar onto a disk
              (GPT + ESP + payload + a real FAT32 /data, then boots off it),
              as opposed to `iso` below which builds the LIVE medium that IS
              the appliance with /data in RAM. Runs gunnar's declared
              `gunnar-installer` atom through the dwarves Build Thing, because
              this xtask is dependency-free by law and cannot read the
              declaration itself. `--help` for requirements + the raw command
  reinstall-appliance
              ⚠ DESTRUCTIVE. Re-image the LIVE appliance on UpCloud from an
              installer ISO: upload it TWICE (once as a cdrom for the SeaBIOS
              firmware to boot, once as a virtio disk because korp-installer
              probes ONLY virtio for the ISO-9660 CD001 magic), attach both,
              install, detach BOTH — leave one on and the box reinstalls itself
              on its next boot — boot from disk, and then PROVE it by reading
              the SSH banner off the appliance, because every API call in a
              re-image answers 200 OK while the box is a brick. Needs
              --confirm-destroy; --expect-version arms GATE-IDENTITY;
              --dry-run changes nothing. `--help` for the rest
  iso         build a bootable appliance ISO: the musl binary as PID 1 in an
              initramfs, a stock kernel, a FAT ESP and an El Torito ISO, with no
              operating system anywhere under the server. `--prove` boots it in
              KVM and pushes and clones over SSH; `--red` makes that proof fail
              on purpose four ways; `--keep` leaves the guest up
  install-test
              BUILD a gunnar, make it name this checkout's HEAD out of its own
              `--version`, install THAT into a container (draupnir), into a KVM
              guest as a systemd unit (tunnr + Skidbladnir), and — as the OS-less
              appliance ISO the declared ⚒ Build Thing cell mastered — into a KVM
              guest with gunnar as PID 1. Push to each over SSH, clone back,
              compare the OBJECT GRAPHS, `fsck --full --strict` what came back,
              and record WHICH server answered from the pinned host key.
              Arguments pass straight through to `installtest/`:
              <container|kvm|iso|all|red> [--fault N] [--arm N] [--binary PATH]
              [--keep]. The `iso` arm consumes the forge's artefact; it is built
              by `cargo test -p dwarves --features appliance-iso --test
              gunnar_appliance_iso -- --ignored`, never here.
                `pair --keep` is the other door and is not a test: it installs
              TWO appliances from the declared `gunnar-installer-open` medium,
              boots both, prints the forwarded git-ssh and control ports plus the
              `10.0.2.2:<port>` address each guest reaches the other by, and
              stands still so a person can drive replication at them.
              `--members main,twin,spare` adds a cold spare, which is what the
              primary-was-nuked drill needs.
  bare        the same object-graph proof with NOTHING wrapped around the
              server: `gunnar serve` as an ordinary process, stock git over SSH,
              eight more git operations (tag, delete, force push, a fetch that
              negotiates), a restart on the same port, a stale-lock
              reclaim, and a teardown that is checked rather than assumed.
              Arguments pass through to `installtest/`:
                (no args)   the bare arm, must PASS
                red --arm bare   the green plus all eight red-first proofs
                up | status | down | pressure   the verbs a human drives
"
    );
}

// ── bench-build ─────────────────────────────────────────────────────────────

/// `cargo xtask bench-build [--check]`; see [`xtask::benchguard`].
///
/// Its own task, and cheap, because the failure mode this guards against is not
/// "the command was wrong" but "nobody ran the command". The equivalent step has
/// been inside `xtask ci` since `a30837c` (2026-08-04) — four days before the
/// first break it would have caught — and it caught neither of the two, because
/// `xtask ci` is tens of minutes and this is the only thing anyone needs after
/// `cargo xtask build [--variant <name>] [--list]` — build the rows
/// `gunnar-recipe` declares, through the SAME writer the dwarves forge uses.
/// The knowledge lives in the recipe; see [`xtask::build`].
fn task_build() -> ExitCode {
    let argv: Vec<String> = std::env::args().skip(2).collect();
    let root = xtask::guard::workspace_root();
    match xtask::build::run(&root, &argv) {
        Ok(_) => ExitCode::SUCCESS,
        Err(e) => {
            eprintln!("xtask build: {e}");
            ExitCode::FAILURE
        }
    }
}

/// touching `crates/`.
fn task_bench_build() -> ExitCode {
    let mut depth = xtask::benchguard::Depth::Build;
    for a in std::env::args().skip(2) {
        match a.as_str() {
            "--check" => depth = xtask::benchguard::Depth::Check,
            "--build" => depth = xtask::benchguard::Depth::Build,
            other => {
                eprintln!("xtask bench-build: unknown argument `{other}`");
                eprintln!("usage: cargo xtask bench-build [--check | --build]");
                return ExitCode::FAILURE;
            }
        }
    }
    let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
    ok(xtask::benchguard::run(
        &guard::workspace_root(),
        depth,
        &cargo,
    ))
}

// ── docs ────────────────────────────────────────────────────────────────────

/// `cargo xtask docs [manual|record]`; see `xtask/src/docsbook.rs`.
///
/// Prints one line per book with the numbers the check actually read back out
/// of the PDF — bytes, pages, embedded images — rather than "wrote N bytes",
/// which a hollow document satisfies just as well.
fn task_docs() -> ExitCode {
    let args: Vec<String> = std::env::args().skip(2).collect();
    match xtask::docsbook::run(&args) {
        Ok(reports) => {
            for r in &reports {
                println!(
                    "{}: {} — {} chapters, {} KiB markdown -> {} pages, {} image(s), {} bytes",
                    r.name,
                    r.out.display(),
                    r.chapters,
                    r.markdown_bytes / 1024,
                    r.pages,
                    r.images,
                    r.bytes,
                );
            }
            ExitCode::SUCCESS
        }
        Err(err) => {
            eprintln!("xtask docs: {err}");
            ExitCode::FAILURE
        }
    }
}

// ── wireproxy ───────────────────────────────────────────────────────────────

/// `cargo xtask wireproxy --listen A --to B --out F`; see
/// `xtask/src/wireproxy.rs`.
///
/// The counting relay the forge bake-off puts in front of all three servers, so
/// gunnar, gitea and Forgejo are measured by one instrument rather than three.
/// Blocks until killed.
fn task_wireproxy() -> ExitCode {
    let args: Vec<String> = std::env::args().skip(2).collect();
    let (listen, to, out) = match xtask::wireproxy::parse_args(&args) {
        Ok(v) => v,
        Err(err) => {
            eprintln!("{err}");
            return ExitCode::FAILURE;
        }
    };
    match xtask::wireproxy::run(listen, to, &out) {
        Ok(()) => ExitCode::SUCCESS,
        Err(err) => {
            eprintln!("{err}");
            ExitCode::FAILURE
        }
    }
}

// ── roundtrip ───────────────────────────────────────────────────────────────

/// `cargo xtask roundtrip …`; see `xtask/src/roundtrip.rs`.
///
/// Not a step in `task_ci`: it needs a RUNNING gunnar with an enrolled key, and a CI step
/// that silently skips when the server is absent is a green nobody has seen red. It is run
/// deliberately, against a named server, and its verdict is whether the working trees came
/// back identical — never a duration.
fn task_roundtrip() -> ExitCode {
    let args: Vec<String> = std::env::args().skip(2).collect();
    let plan = match xtask::roundtrip::parse_args(&args) {
        Ok(p) => p,
        Err(err) => {
            eprintln!("{err}");
            return ExitCode::FAILURE;
        }
    };
    let root = guard::workspace_root();
    match xtask::roundtrip::run(&root, &plan) {
        Ok(report) => {
            xtask::roundtrip::print(&report);
            ok(report.passed())
        }
        Err(err) => {
            eprintln!("{err}");
            ExitCode::FAILURE
        }
    }
}

// ── iso ─────────────────────────────────────────────────────────────────────

/// `cargo xtask iso …`; see `xtask/src/appliance.rs`.
///
/// Not a step in `task_ci`, for the same reason `images` is not: it builds a
/// Rust toolchain's worth of container image and boots a virtual machine.
fn task_iso() -> ExitCode {
    let args: Vec<String> = std::env::args().skip(2).collect();
    match xtask::appliance::run(&args) {
        Ok(true) => {
            eprintln!("iso: PASS");
            ExitCode::SUCCESS
        }
        Ok(false) => {
            eprintln!(
                "iso: FAIL. The ISO did not boot, did not serve a real push, or a red-first \
                 red-first proof did not fire"
            );
            ExitCode::FAILURE
        }
        Err(err) => {
            eprintln!("iso: {err}");
            ExitCode::FAILURE
        }
    }
}

// ── iso-installer ───────────────────────────────────────────────────────────

/// `cargo xtask iso-installer`; builds the INSTALLER ISO (the medium that lays
/// gunnar onto a disk) through the dwarves Build Thing. See `xtask::isoinstaller`
/// for why the door lives in dwarves and not here.
fn task_iso_installer() -> ExitCode {
    let args: Vec<String> = std::env::args().skip(2).collect();
    match xtask::isoinstaller::run(&guard::workspace_root(), &args) {
        Ok(true) => ExitCode::SUCCESS,
        Ok(false) => ExitCode::FAILURE,
        Err(err) => {
            eprintln!("iso-installer: {err}");
            ExitCode::FAILURE
        }
    }
}

fn task_payload() -> ExitCode {
    let args: Vec<String> = std::env::args().skip(2).collect();
    match xtask::payload::run(&guard::workspace_root(), &args) {
        Ok(true) => ExitCode::SUCCESS,
        Ok(false) => ExitCode::FAILURE,
        Err(err) => {
            eprintln!("payload: {err}");
            ExitCode::FAILURE
        }
    }
}

// ── reinstall-appliance ─────────────────────────────────────────────────────

/// Where the UpCloud crate's target directory goes: its own, per worktree, for
/// the reason `installtest_target_dir` records.
const UPCLOUD_TARGET_ROOT: &str = "~/scratch/cargo/gunnar-upcloud";

/// `cargo xtask reinstall-appliance …`; re-images the LIVE appliance on UpCloud.
/// See `xtask/src/reinstall.rs` for the virtio trap, for why the work lives in
/// `deploy/upcloud/` rather than here, and for the receipt this verb reads back.
fn task_reinstall_appliance() -> ExitCode {
    let root = guard::workspace_root();
    let args: Vec<String> = std::env::args().skip(2).collect();
    let target = lane_dir::lane_dir(UPCLOUD_TARGET_ROOT, &root);
    match xtask::reinstall::run(&root, &target, &args) {
        Ok(true) => ExitCode::SUCCESS,
        Ok(false) => ExitCode::FAILURE,
        Err(err) => {
            eprintln!("reinstall-appliance: {err}");
            ExitCode::FAILURE
        }
    }
}

// ── bare ────────────────────────────────────────────────────────────────────

/// `cargo xtask bare …`; runs the detached `installtest/` crate's bare arm and
/// its operator verbs.
///
/// Shares [`run_installtest`] with `install-test` rather than repeating the
/// spawn — one implementation, not two. The only difference is the default
/// with no arguments: `bare`
/// alone runs the bare arm, where `install-test` alone runs all of them.
fn task_bare() -> ExitCode {
    run_installtest("bare", &["bare".to_string()])
}

// ── install-test ────────────────────────────────────────────────────────────

/// `cargo xtask install-test …`; see `installtest/src/lib.rs`.
///
/// It runs the detached `installtest/` crate rather than linking it, for the
/// reason `xtask/Cargo.toml` gives: this crate has **zero dependencies** on
/// purpose, and draupnir + bollard + tokio + tunnr is not a tree the dependency
/// guard should be carrying. Every other repository task here reaches its tool
/// the same way, by running it.
///
/// Like `images`, it is deliberately NOT a step in `task_ci`: it builds a
/// container image and boots a virtual machine, which is minutes, and `ci` is
/// what a developer runs before every push.
fn task_install_test() -> ExitCode {
    // The default is the whole point of the task: every arm, green.
    run_installtest("install-test", &["all".to_string()])
}

/// The one derivation of a per-worktree target directory, shared with
/// `installtest`'s subject binary rather than written a second time.
///
/// `xtask` has ZERO dependencies on purpose, so it cannot depend on
/// `installtest`. A `#[path]` source include adds no edge to the dependency
/// graph and still leaves exactly one implementation.
#[path = "../../installtest/src/lane_dir.rs"]
mod lane_dir;

/// Where the `installtest` **harness** is built. Deliberately not
/// `installtest::subject::TARGET_ROOT`, which is where the **subject binary** is
/// built: they are two artefacts of two workspaces, and one directory for both
/// is the contention this pair of roots exists to prevent.
const INSTALLTEST_TARGET_ROOT: &str = "~/scratch/cargo/gunnar-install-test";

/// The harness target directory for the worktree at `root`.
///
/// Until 2026-08-05 this was the bare constant, so every worktree on this box ran
/// `cargo xtask install-test` into one directory and blocked on each other's
/// cargo package lock. The subject-binary half of the same defect was fixed in
/// `81c5ae5`, after a live proof that two lanes were reading each other's
/// binaries — one lane's `gunnar-install/release/gunnar` rewritten at 08:59
/// stamped `v1.1-8-g6c02185` while another's still read `v1.1-6-g95354c2`, and
/// two tests green *because* of the sharing. This is the harness half, and it
/// routes through the same function.
///
/// `GUNNAR_INSTALLTEST_TARGET_DIR` still wins: an explicitly named directory is
/// an instruction, not an accident.
fn installtest_target_dir(root: &Path) -> PathBuf {
    installtest_target_dir_with(root, std::env::var("GUNNAR_INSTALLTEST_TARGET_DIR").ok())
}

/// The override taken as an argument rather than read from the environment, so
/// the tests below say something about the derivation and not about whatever
/// happens to be exported in the shell that runs them.
fn installtest_target_dir_with(root: &Path, explicit: Option<String>) -> PathBuf {
    match explicit {
        Some(v) if !v.trim().is_empty() => PathBuf::from(v),
        _ => lane_dir::lane_dir(INSTALLTEST_TARGET_ROOT, root),
    }
}

/// Run the detached `installtest/` crate, passing `cargo xtask`'s own arguments
/// through, and falling back to `default_args` when there are none.
fn run_installtest(task: &str, default_args: &[String]) -> ExitCode {
    let root = guard::workspace_root();
    let manifest = root.join("installtest/Cargo.toml");
    if !manifest.is_file() {
        eprintln!(
            "{task}: {} is missing from the checkout",
            manifest.display()
        );
        return ExitCode::FAILURE;
    }
    let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
    let mut args: Vec<String> = vec![
        "run".into(),
        "--release".into(),
        "--manifest-path".into(),
        manifest.to_string_lossy().into_owned(),
        "--".into(),
    ];
    let passthrough: Vec<String> = std::env::args().skip(2).collect();
    if passthrough.is_empty() {
        args.extend(default_args.iter().cloned());
    } else {
        args.extend(passthrough);
    }

    let mut cmd = Command::new(&cargo);
    cmd.current_dir(&root).args(&args);
    // `installtest/` is a detached workspace and gets its OWN target directory,
    // unconditionally. Inheriting the caller's `CARGO_TARGET_DIR`, which on this
    // box is usually gunnar's own, would put two workspaces in one directory and
    // make this task block on whatever build is already running there.
    // `GUNNAR_INSTALLTEST_TARGET_DIR` overrides it; `CARGO_TARGET_DIR` does not.
    cmd.env("CARGO_TARGET_DIR", installtest_target_dir(&root));
    match cmd.status() {
        Ok(s) if s.success() => ExitCode::SUCCESS,
        Ok(s) => {
            eprintln!("{task}: FAILED ({s})");
            ExitCode::FAILURE
        }
        Err(err) => {
            eprintln!("{task}: could not run `{cargo}`: {err}");
            ExitCode::FAILURE
        }
    }
}

// ── images ──────────────────────────────────────────────────────────────────

/// `cargo xtask images`; see `xtask/src/images.rs`.
///
/// Deliberately NOT a step in `task_ci`. It builds two full Rust toolchains'
/// worth of container image and takes minutes; `ci` is what a developer runs
/// before every push. The appliance *build* is already a `ci` step; this is the
/// appliance *running*.
fn task_images() -> ExitCode {
    let args: Vec<String> = std::env::args().skip(2).collect();
    match xtask::images::run(&args) {
        Ok(true) => {
            eprintln!("images: PASS. Every candidate variant started and served a real clone");
            ExitCode::SUCCESS
        }
        Ok(false) => {
            eprintln!(
                "images: FAIL. A variant did not start, did not serve a clone, broke a \
                 no-shell claim, or a red-first proof did not fire"
            );
            ExitCode::FAILURE
        }
        Err(err) => {
            eprintln!("images: {err}");
            ExitCode::FAILURE
        }
    }
}

// ── svg ─────────────────────────────────────────────────────────────────────

/// `cargo xtask svg` — regenerate `assets/gunnar-znippy-cycle.svg`.
///
/// Cheap and pure, so it *is* safe in `ci`; it is not a step there only because
/// `ci` must not write into the working tree. Run it after changing anything in
/// `xtask/src/cycle_svg.rs` and commit the asset with the datum that moved.
fn task_svg() -> ExitCode {
    // `guard::workspace_root()` already resolves this from the compiled-in
    // manifest directory, and one resolver beats two that can disagree.
    // Deriving it from the cwd instead would silently write the asset into
    // whatever directory the developer happened to be in.
    let root = guard::workspace_root();
    match xtask::cycle_svg::write_asset(&root) {
        Ok(len) => {
            eprintln!(
                "svg: wrote {} ({len} bytes, {} sections, {} operation lanes)",
                xtask::cycle_svg::ASSET,
                xtask::cycle_svg::SECTIONS.len(),
                xtask::cycle_svg::LANES.len()
            );
        }
        Err(err) => {
            eprintln!("svg: {err}");
            return ExitCode::FAILURE;
        }
    }
    match xtask::datapath_svg::write_asset(&root) {
        Ok(len) => {
            eprintln!(
                "svg: wrote {} ({len} bytes, {} read nodes, {} write nodes, {} store structures)",
                xtask::datapath_svg::ASSET,
                xtask::datapath_svg::READ.nodes.len() + xtask::datapath_svg::READ.inner.len(),
                xtask::datapath_svg::WRITE.nodes.len() + xtask::datapath_svg::WRITE.inner.len(),
                xtask::datapath_svg::STORE.len()
            );
        }
        Err(err) => {
            eprintln!("svg: {err}");
            return ExitCode::FAILURE;
        }
    }
    // The quick start's figure. It carries no measurement, so it is not held to
    // a `file:line`; it is held to the CLI's VERBS instead — see the module.
    match xtask::setup_svg::write_asset(&root) {
        Ok(written) => {
            for (asset, len) in written {
                eprintln!("svg: wrote {asset} ({len} bytes)");
            }
            eprintln!(
                "svg: {} setup scenes from {} units, {} wires; {} variant pictures",
                xtask::setup_svg::SCENES.len(),
                xtask::setup_svg::UNITS.len(),
                xtask::setup_svg::WIRES.len(),
                xtask::variant_svg::SCENES.len()
            );
            ExitCode::SUCCESS
        }
        Err(err) => {
            eprintln!("svg: {err}");
            ExitCode::FAILURE
        }
    }
}

// ── do-all ──────────────────────────────────────────────────────────────────

/// `cargo xtask do-all` — the whole matrix, unattended.
///
/// Five store configurations × every test, the monkey isolated under a
/// watchdog, then the containerised bake-off with all five gunnar columns
/// against gitea and Forgejo. Containers and store-sensitive fixtures are
/// cleaned between every pass. No environment variable is typed by a person,
/// and nobody has to be watching.
fn task_do_all() -> ExitCode {
    match xtask::benchrun::do_all(&guard::workspace_root()) {
        Ok(()) => ExitCode::SUCCESS,
        Err(e) => {
            eprintln!("xtask do-all: {e}");
            ExitCode::FAILURE
        }
    }
}

// ── bench ───────────────────────────────────────────────────────────────────

/// `cargo xtask bench <suite>` — run a bench suite with its configuration set
/// from a table in Rust, not from your shell.
///
/// See [`xtask::benchrun`]. Every `GUNNAR_*` / `NORNIR_BENCH_*` variable the run
/// needs is written by that module onto the child process; none is typed by a
/// person, and a stray one in the caller's shell is actively removed.
fn task_bench() -> ExitCode {
    let root = guard::workspace_root();
    // `all` — the whole bencher, once per store configuration. x1 … x5 vs y on
    // EVERY test, not only the containerised bake-off.
    if std::env::args().nth(2).as_deref() == Some("all") {
        return match xtask::benchrun::run_every_store(&root, xtask::benchrun::INPROC_SKIP, true) {
            Ok(()) => ExitCode::SUCCESS,
            Err(e) => {
                eprintln!("xtask bench all: {e}");
                ExitCode::FAILURE
            }
        };
    }
    let Some(name) = std::env::args().nth(2) else {
        eprintln!("usage: cargo xtask bench <suite>\n\nsuites:");
        for s in xtask::benchrun::SUITES {
            // First clause of `why` only: the usage list is a menu, and the
            // full reasoning lives on the suite where it can be read in full.
            let gist: String = s
                .why
                .split(". ")
                .next()
                .unwrap_or(s.why)
                .chars()
                .take(96)
                .collect();
            eprintln!("  {:<16} engine {:<16} {gist}", s.name, s.engine.label());
        }
        return ExitCode::FAILURE;
    };
    let Some(s) = xtask::benchrun::suite(&name) else {
        eprintln!("xtask bench: unknown suite `{name}`");
        eprintln!(
            "known: {}",
            xtask::benchrun::SUITES
                .iter()
                .map(|s| s.name)
                .collect::<Vec<_>>()
                .join(", ")
        );
        return ExitCode::FAILURE;
    };
    match xtask::benchrun::run(&root, s) {
        Ok(()) => ExitCode::SUCCESS,
        Err(e) => {
            eprintln!("xtask bench: {e}");
            ExitCode::FAILURE
        }
    }
}

// ── bench-readme ────────────────────────────────────────────────────────────

/// `cargo xtask bench-readme run|write|check|compare`: see [`xtask::bench_readme`].
fn task_bench_readme() -> ExitCode {
    use xtask::bench_readme as br;
    let args: Vec<String> = std::env::args().skip(2).collect();
    let value = |flag: &str| {
        args.iter()
            .position(|a| a == flag)
            .and_then(|i| args.get(i + 1))
            .cloned()
    };
    let root = guard::workspace_root();
    match args.first().map(String::as_str) {
        Some("run") => {
            let (Some(seeds), Some(machine)) = (value("--seeds"), value("--machine")) else {
                eprintln!("usage: cargo xtask bench-readme run --seeds <dir> --machine <name>");
                return ExitCode::from(2);
            };
            match br::run(&root, Path::new(&seeds), &machine) {
                Ok(file) => {
                    println!("bench-readme run: {}", file.display());
                    ExitCode::SUCCESS
                }
                Err(e) => {
                    eprintln!("bench-readme run: {e}");
                    ExitCode::FAILURE
                }
            }
        }
        Some("import-reach") => {
            let need = [
                "--stdout",
                "--stderr",
                "--dataset",
                "--machine",
                "--engine",
                "--commit",
            ];
            let got: Vec<Option<String>> = need.iter().map(|f| value(f)).collect();
            if got.iter().any(Option::is_none) {
                eprintln!(
                    "usage: cargo xtask bench-readme import-reach --stdout <file> --stderr <file> --dataset <name> --machine <name> --engine <crate version> --commit <sha>"
                );
                return ExitCode::from(2);
            }
            let g: Vec<String> = got.into_iter().flatten().collect();
            let read = |p: &str| std::fs::read_to_string(p).map_err(|e| format!("{p}: {e}"));
            let how = br::ReachImport {
                dataset: g[2].clone(),
                machine: g[3].clone(),
                engine: g[4].clone(),
                commit: g[5].clone(),
            };
            let made = read(&g[0])
                .and_then(|o| read(&g[1]).map(|e| (o, e)))
                .and_then(|(o, e)| br::import_reach(&o, &e, &how))
                .and_then(|doc| {
                    let r = br::parse_result("import", &doc)?;
                    let date = r
                        .json
                        .get("date")
                        .and_then(xtask::json::Json::str)
                        .unwrap_or("")
                        .to_string();
                    let file = root.join(br::RESULTS_DIR).join(format!(
                        "{date}-{}-{}.json",
                        how.machine,
                        br::REACH_BENCH
                    ));
                    std::fs::create_dir_all(root.join(br::RESULTS_DIR))
                        .map_err(|e| e.to_string())?;
                    std::fs::write(&file, doc).map_err(|e| format!("{}: {e}", file.display()))?;
                    Ok(file)
                });
            match made {
                Ok(f) => {
                    println!("bench-readme import-reach: {}", f.display());
                    ExitCode::SUCCESS
                }
                Err(e) => {
                    eprintln!("bench-readme import-reach: {e}");
                    ExitCode::FAILURE
                }
            }
        }
        Some("import-perfbench") => {
            let need = [
                "--jsonl",
                "--bench",
                "--machine",
                "--date",
                "--source-commit",
                "--history",
            ];
            let got: Vec<Option<String>> = need.iter().map(|f| value(f)).collect();
            if got.iter().any(Option::is_none) {
                eprintln!(
                    "usage: cargo xtask bench-readme import-perfbench --jsonl <file> --bench <name> --machine <name> --date <YYYY-MM-DD> --source-commit <what> --history <bare git.git> [--history-name git.git] [--notes <text>] [--load-before <load1>]"
                );
                return ExitCode::from(2);
            }
            let g: Vec<String> = got.into_iter().flatten().collect();
            let how = br::PerfbenchImport {
                bench: g[1].clone(),
                machine: g[2].clone(),
                date: g[3].clone(),
                source_repo: "gunnar".into(),
                source_commit: g[4].clone(),
                history_name: value("--history-name").unwrap_or_else(|| "git.git".into()),
                history_facts: br::repo_facts(Path::new(&g[5])),
                notes: value("--notes").unwrap_or_default(),
                load_before: value("--load-before").and_then(|v| v.parse().ok()),
            };
            if how.history_facts.is_none() {
                eprintln!("bench-readme import-perfbench: git cannot count {}", g[5]);
                return ExitCode::FAILURE;
            }
            let made = std::fs::read_to_string(&g[0])
                .map_err(|e| format!("{}: {e}", g[0]))
                .and_then(|src| br::import_perfbench(&src, &how))
                .and_then(|doc| {
                    br::parse_result("import", &doc)?;
                    let dir = root.join(br::RESULTS_DIR);
                    std::fs::create_dir_all(&dir).map_err(|e| e.to_string())?;
                    let file = dir.join(format!("{}-{}-{}.json", how.date, how.machine, how.bench));
                    std::fs::write(&file, doc).map_err(|e| format!("{}: {e}", file.display()))?;
                    Ok(file)
                });
            match made {
                Ok(f) => {
                    println!("bench-readme import-perfbench: {}", f.display());
                    ExitCode::SUCCESS
                }
                Err(e) => {
                    eprintln!("bench-readme import-perfbench: {e}");
                    ExitCode::FAILURE
                }
            }
        }
        Some("write") => match br::write(&root) {
            Ok(changed) => {
                for p in &changed {
                    println!("bench-readme write: {}", p.display());
                }
                if changed.is_empty() {
                    println!("bench-readme write: every README is current");
                }
                ExitCode::SUCCESS
            }
            Err(e) => {
                eprintln!("bench-readme write: {e}");
                ExitCode::FAILURE
            }
        },
        Some("check") => match br::check(&root) {
            Ok(()) => {
                println!("bench-readme check: every README holds what the newest results render");
                ExitCode::SUCCESS
            }
            Err(e) => {
                eprintln!("bench-readme check: RED: {e}; run `cargo xtask bench-readme write`");
                ExitCode::FAILURE
            }
        },
        Some("compare") => {
            let threshold = value("--threshold")
                .and_then(|v| v.parse::<f64>().ok())
                .unwrap_or(25.0);
            match br::compare(&root, threshold) {
                Ok(r) if r.is_empty() => {
                    println!("bench-readme compare: no regression past {threshold} %");
                    ExitCode::SUCCESS
                }
                Ok(r) => {
                    eprintln!(
                        "bench-readme compare: RED: {} regression(s) past {threshold} %:",
                        r.len()
                    );
                    for m in r {
                        eprintln!("  {m}");
                    }
                    ExitCode::FAILURE
                }
                Err(e) => {
                    eprintln!("bench-readme compare: {e}");
                    ExitCode::FAILURE
                }
            }
        }
        _ => {
            eprintln!(
                "usage: cargo xtask bench-readme run --seeds <dir> --machine <name> | write | check | compare [--threshold <pct>] | import-reach ..."
            );
            ExitCode::from(2)
        }
    }
}

// ── bench-landed ────────────────────────────────────────────────────────────

/// `cargo xtask bench-landed` — the measurement is in BOTH stores, or this is
/// red.
///
/// See [`xtask::benchland`]. The short version: one bench run writes one value
/// twice — the iceberg `bench_runs` row and the `bench_history.jsonl` receipt —
/// and on 2026-08-12 ten runs went into the receipt and no warehouse while every
/// command involved exited 0.
fn task_bench_landed() -> ExitCode {
    match xtask::benchland::run(&guard::workspace_root()) {
        Ok(l) => {
            eprintln!("bench-landed: {}", l.summary());
            for k in &l.orphans {
                eprintln!("  ✗ receipt with no warehouse row: {}", k.label());
            }
            if l.is_red() {
                eprintln!(
                    "bench-landed: RED. Re-run through the door that writes both:\n  \
                     nornir bench run gunnar --heavy --config {}\n\
                     (`dwarves bench run` writes the receipt and submits to the warehouse ONLY \
                     with a nornir server configured; with none it prints NOT SUBMITTED and \
                     still exits 0.)",
                    xtask::mashup::CONFIG,
                );
                return ExitCode::FAILURE;
            }
            ExitCode::SUCCESS
        }
        Err(err) => {
            eprintln!("bench-landed: {err}");
            ExitCode::FAILURE
        }
    }
}

// ── mashup ──────────────────────────────────────────────────────────────────

/// `cargo xtask mashup` — regenerate the bake-off chart and its tables from the
/// iceberg `bench_runs` table.
///
/// Nothing here is typed in. The numbers are read back through
/// `nornir bench history-show gunnar --json`, and an arm with no row is printed
/// as NOT MEASURED rather than filled in from a literal — see
/// `xtask/src/mashup.rs`. To put rows in the table, run the arms:
/// `NORNIR_BENCH_ONLY=gunnar.vs_forge_ nornir bench run gunnar --heavy`.
fn task_mashup() -> ExitCode {
    match xtask::mashup::run(&guard::workspace_root()) {
        Ok(r) => {
            eprintln!(
                "mashup: wrote {} ({} bytes) from {}",
                xtask::mashup::ASSET,
                r.svg_bytes,
                r.provenance
            );
            eprintln!(
                "mashup: {} measured row(s), {} drawn, {} of them arms gunnar LOSES",
                r.rows, r.drawn, r.losses
            );
            eprintln!(
                "mashup: wrote {} ({} bytes) — the same rows as JSON, readable with no nornir, \
                 no catalog and no server",
                xtask::mashup::ASSET_JSON,
                r.json_bytes
            );
            if !r.missing.is_empty() {
                eprintln!(
                    "mashup: NOT MEASURED, and not substituted: {}",
                    r.missing.join(", ")
                );
            }
            if let Some(note) = &r.skipped {
                eprintln!("mashup: {note}");
            }
            if r.wrote.is_empty() {
                eprintln!("mashup: every generated table was already current");
            } else {
                eprintln!("mashup: regenerated the table in {}", r.wrote.join(", "));
            }
            // Named, not passed over: a run that splices into nothing has to
            // say why. See `mashup::TARGETS_IN_THE_RECORD`.
            for t in &r.not_in_this_tree {
                eprintln!("mashup: no generated block here — {t}");
            }
            ExitCode::SUCCESS
        }
        Err(err) => {
            eprintln!("mashup: {err}");
            ExitCode::FAILURE
        }
    }
}

// ── guard ───────────────────────────────────────────────────────────────────

fn task_guard() -> ExitCode {
    let profiles = guard::gunnar_profiles();
    eprintln!("dependency guard: {} resolved graphs", profiles.len());
    for p in &profiles {
        eprintln!("  profile {:<18} cargo tree {}", p.name, p.args.join(" "));
    }
    eprintln!();

    let report = match guard::run_gunnar() {
        Ok(r) => r,
        Err(err) => {
            eprintln!("dependency guard could not resolve a graph:\n{err}");
            return ExitCode::FAILURE;
        }
    };

    print!("{}", report.render());

    // the pack-pipeline source rules, run in the same task. They answer the same question,
    // "is this build about to cost what the design says it costs", and a
    // second command nobody remembers to run is not a guard.
    let root = guard::workspace_root();
    let (pipeline, files) = xtask::pack_pipeline::scan_workspace(&root);
    if files == 0 {
        eprintln!("pack-pipeline guard: could not read any source under {root:?}/crates");
        return ExitCode::FAILURE;
    }
    eprintln!("\npack-pipeline guard: {files} shipped source file(s) scanned");
    // Rule 3 reads a WIDER set than the other two — tests and `bench/` included
    // — because a wrapper that silently disables an optimisation does its damage
    // in an instrument, not in a server. Three of its five known instances were
    // in exactly those directories.
    let (stores, store_files) = xtask::pack_pipeline::scan_object_stores(&root);
    if store_files == 0 {
        eprintln!("pack-pipeline guard: could not read any source for the ObjectStore rule");
        return ExitCode::FAILURE;
    }
    eprintln!("pack-pipeline guard (GitOps opt-outs): {store_files} source file(s) scanned");

    // The engine seam, in the same task and for the same reason. It is the
    // narrow pair the other two guards cannot form between them: `guard` reads
    // resolved graphs and goes red twelve edges from the line that caused it,
    // `pack_pipeline` reads call sites and was green throughout the five days
    // `gix-pack` sat in the appliance graph with no caller. This one reads the
    // two serving-tier manifests and their sources together, so the failure
    // lands on the line somebody typed. See `xtask/src/engine_seam.rs`.
    let (seam, seam_files) = xtask::engine_seam::scan(&root);
    if seam_files == 0 {
        eprintln!("engine-seam guard: could not read any source under {root:?}/crates");
        return ExitCode::FAILURE;
    }
    eprintln!("engine-seam guard: {seam_files} serving-tier file(s) scanned");

    // The staging tree, in the same task and for the same reason as the three
    // above: a second command nobody remembers to run is not a guard. This one
    // reads the FILESYSTEM rather than source or a resolved graph, because the
    // leak it exists for (I6) was invisible to every git-shaped check — the
    // tree's own `.gitignore` is `*`, so 4.4 MB of another repository's
    // forge-test fixtures rode the initramfs root of every ISO built here for
    // a fortnight with `git status` clean throughout. See
    // `xtask/src/staging_guard.rs`.
    let (strays, walked) = xtask::staging_guard::scan(&root);
    if walked == 0 {
        eprintln!(
            "staging-tree guard: walked nothing under {}/{} — the tree moved, and a guard \
             that cannot find what it watches must not report clean",
            root.display(),
            xtask::staging_guard::STAGING_TREE
        );
        return ExitCode::FAILURE;
    }
    eprintln!(
        "staging-tree guard: {walked} entr(ies) walked under {}",
        xtask::staging_guard::STAGING_TREE
    );

    // Rendered to strings on the way in: the three scans have three `Finding`
    // types with three `Rule` enums, and one flat list of already-formatted
    // failures is the right shape for a verdict. Each `Display` impl carries
    // its own `file:line [Rule] why`, so nothing is lost.
    let pipeline: Vec<String> = pipeline
        .iter()
        .map(ToString::to_string)
        .chain(stores.iter().map(ToString::to_string))
        .chain(seam.iter().map(ToString::to_string))
        .chain(strays.iter().map(ToString::to_string))
        .collect();
    for f in &pipeline {
        eprintln!("  {f}");
    }

    if report.is_clean() && pipeline.is_empty() {
        eprintln!(
            "\ndependency guard: PASS ({} tracked deviation(s), 0 violations)",
            report.tracked.len()
        );
        eprintln!(
            "pack-pipeline guard + engine seam + staging tree: PASS (0 undeclared call \
             sites, 0 seam crossings, 0 strays)"
        );
        ExitCode::SUCCESS
    } else if report.is_clean() {
        eprintln!(
            "\npack-pipeline guard + engine seam + staging tree: FAIL, {} undeclared call \
             site(s) / seam crossing(s) / staging stray(s)",
            pipeline.len()
        );
        ExitCode::FAILURE
    } else {
        eprintln!(
            "\ndependency guard: FAIL. {} violation(s), {} stale deviation(s), \
             {} phantom rule name(s)",
            report.violations.len(),
            report.stale_deviations.len(),
            report.phantom_rules.len()
        );
        ExitCode::FAILURE
    }
}

// ── cve ───────────────────────────────────────────────────────────────────────

/// `cargo xtask cve`: the 0-known-CVE gate — `cargo-deny` advisories + bans over
/// gunnar's resolved `Cargo.lock`, keyed off the repo's own `deny.toml`.
///
/// This is the SAME gate the deploy pipeline's `door cve` runs, made a step of
/// gunnar's own CI so "0 known CVE" is asserted *per release* and not only *per
/// deploy*. Its red-first proof is permanent, in `xtask/tests/cve.rs`.
///
/// A missing `cargo-deny`, or a red on either sub-check, is a FAILURE — never a
/// silent pass. `deny.toml`'s dated `[advisories] ignore` entries are the only
/// sanctioned exceptions, and they live once, there.
fn task_cve() -> ExitCode {
    let root = xtask::cve::workspace_root();
    eprintln!("cve gate: cargo-deny advisories + bans over gunnar's Cargo.lock (deny.toml policy)");
    if !xtask::cve::tool_available() {
        eprintln!(
            "cve gate: FAIL — cargo-deny is not installed (`cargo install cargo-deny`).\n\
             A CVE gate that cannot run is not a gate that passed."
        );
        return ExitCode::FAILURE;
    }
    let report = match xtask::cve::run_gunnar() {
        Ok(r) => r,
        Err(err) => {
            eprintln!("cve gate: FAIL — {err}");
            return ExitCode::FAILURE;
        }
    };
    print!("{}", report.render());
    if report.is_clean() {
        eprintln!(
            "cve gate: PASS — 0 unignored advisories, bans clean. \
             ({} advisory ignore(s) triaged in deny.toml)",
            deny_ignore_count(&root)
        );
        ExitCode::SUCCESS
    } else {
        let ids = report.rustsec_ids();
        if ids.is_empty() {
            eprintln!("cve gate: FAIL — see the cargo-deny output above.");
        } else {
            eprintln!(
                "cve gate: FAIL — {} vulnerable dependency advisory(ies): {}",
                ids.len(),
                ids.join(", ")
            );
        }
        ExitCode::FAILURE
    }
}

/// How many `[advisories] ignore` entries `deny.toml` carries, for an honest
/// PASS line: a green here means "0 UNIGNORED advisories", and the count is what
/// keeps that from being read as "0 advisories in the tree". Best-effort; a
/// parse it cannot read reports 0 rather than lying with a larger number.
fn deny_ignore_count(root: &std::path::Path) -> usize {
    let Ok(text) = std::fs::read_to_string(root.join("deny.toml")) else {
        return 0;
    };
    text.lines()
        .filter(|l| l.trim_start().starts_with("{ id = \"RUSTSEC-"))
        .count()
}

// ── preflight ───────────────────────────────────────────────────────────────

/// The external binaries the interop tests drive.
///
/// gunnar itself spawns nothing: zero shell, client and server both. Its *tests* do,
/// because the only honest proof that a git server works is that stock `git`
/// talks to it. Several of those tests print `SKIPPED:` and pass when the
/// binary is absent, which in CI is a permanent green; this task is what turns
/// that into a red before the suite ever starts.
const REQUIRED_TOOLS: &[(&str, &[&str])] = &[
    ("git", &["--version"]),
    // `ssh -V` prints to stderr and exits non-zero on some builds, so the check
    // below is on spawnability plus *some* output, never on exit status.
    ("ssh", &["-V"]),
    // `-?` is not a real option: OpenSSH answers with its usage banner on
    // stderr and never prompts. `ssh-keygen` with no arguments would ask for a
    // filename and hang a CI job forever.
    ("ssh-keygen", &["-?"]),
];

fn task_preflight() -> bool {
    let mut missing = Vec::new();
    for (tool, args) in REQUIRED_TOOLS {
        match Command::new(tool).args(*args).output() {
            Ok(out) => {
                let text = String::from_utf8_lossy(&out.stdout).trim().to_string();
                let text = if text.is_empty() {
                    String::from_utf8_lossy(&out.stderr).trim().to_string()
                } else {
                    text
                };
                let first = text.lines().next().unwrap_or("").trim();
                if first.is_empty() {
                    // Output, not exit code: a tool that spawns but says
                    // nothing is indistinguishable from one that is not there.
                    missing.push(format!("{tool}: spawned but produced no output"));
                } else {
                    println!("  {tool:<12} {first}");
                }
            }
            Err(err) => missing.push(format!("{tool}: {err}")),
        }
    }
    if missing.is_empty() {
        println!("preflight: PASS. Every external binary the interop tests need is present");
        true
    } else {
        eprintln!("preflight: FAIL, the interop tests cannot be proven:");
        for m in &missing {
            eprintln!("  {m}");
        }
        eprintln!(
            "\nInstall stock `git` and OpenSSH. Do NOT run the suite without them: the \
             interop tests print `SKIPPED:` and pass, which is a green nobody has seen red."
        );
        false
    }
}

// ── ci ──────────────────────────────────────────────────────────────────────

// **`ci` runs every step and fails at the end, not at the first one.**
//
// It used to `return ExitCode::FAILURE` at the first failing step, and step 1
// is `fmt --check`. MEASURED 2026-08-04 on pristine `origin/master`: 65 rustfmt
// diffs in `gunnar-store` alone. So `cargo xtask ci` has been exiting FAILURE
// on whitespace **before reaching clippy, the test suite, the dependency guard
// or the pack-pipeline guard** — the guards were unreachable, which is the same
// as absent, and the one thing worse than a guard that cannot fail is a guard
// nobody can run.
//
// This file's own comment above `fmt_steps` says "This is also why `xtask ci`
// never reached a test" about the previous incarnation of exactly this. The
// scoping was fixed; the short-circuit was not.
//
// Ordering is unchanged and so is the verdict: any failing step still fails
// `ci`. What changed is that a whitespace diff can no longer hide a violation
// behind it. Every failure is named again at the end, so a long log still ends
// with the list.
// The znippy-checkout probe that stood here is DELETED (2026-09-09).
//
// It answered one question — "does `crates/gunnar-server`'s `path =
// \"../../../znippy/...\"` point at a manifest that exists" — and that path is
// gone: `znippy-plugin-git` and `git-storage-trait` are crates.io VERSIONS now,
// so the root workspace resolves with no sibling checkout anywhere. The probe
// could therefore never again answer `Present`, and `ci` would have printed
//
//     NOT RUN: no znippy checkout beside this one, and none beside a main
//     worktree either. This machine cannot test the znippy engine.
//
// on every run, for ever, while the suites it names ran perfectly. That is the
// exact disease its own doc comment was written against — a `NOT RUN` that
// returns true and quietly shrinks the suite the number came from — with the
// sign flipped: not a green covering less than it claims, but a permanent
// warning covering more. A probe that cannot say `Present` measures nothing, and
// the honest form of "this no longer needs asking" is deletion, not a constant.
//
// Gone with it: `znippy_plugin_manifest`, `ZnippyCheckout`, `znippy_state`,
// `report_znippy_skip`, the `znippy_probe` test module and its worktree half.
// `git log -S znippy_checkout_present` has all of it if the sibling layout ever
// comes back.

/// The packages `cargo test --workspace` cannot reach, and how to run each.
///
/// `(step name, manifest path, extra argv)`. Detached from the root workspace,
/// each for a reason that is still correct — which is why they are run by
/// manifest path rather than attached:
///
///  * `installtest` — its own `[workspace]`, and it may take dependencies
///    (draupnir, tunnr) that `xtask` deliberately has none of. It was NOT in
///    this table until 2026-08-05, and the reason it was kept out was real: it
///    installed whatever `gunnar` binary happened to be in a scratch directory —
///    measured once at 1998 minutes old, from another lane's worktree — so its
///    verdict was about an unrelated artefact and wiring it in would have made
///    gunnar's CI depend on one. `installtest/src/subject.rs` closed that: the
///    crate now BUILDS its subject and refuses one whose `--version` does not
///    name this checkout's `HEAD`. The objection was to the provenance, not to
///    the tests, and the provenance is fixed, so the tests run.
///
///    What runs here is the crate's `#[test]`s — the exit-code verdict suite and
///    the guards under `src/` — not the install ARMS. Those boot containers and
///    virtual machines and belong to `cargo xtask install-test`, which is a
///    different command with a different cost.
/// # What LEFT this table on 2026-08-10, and where it went
///
///  * the two znippy engine suites — the old znippy engine crate is
///    **deleted**. Both entries named manifests that
///    are not on disk, so both steps had become guaranteed failures with a
///    "manifest path does not exist" that says nothing about this repository.
///  * `bench-compile` — moved OUT and UP, into [`xtask::benchguard`], which
///    `ci` now runs as its **second step, before `preflight`**. Two reasons, and
///    the first is the one that matters: as an entry here it ran only inside a
///    tens-of-minutes `xtask ci`, and it therefore caught **neither** of the two
///    breaks it existed for (2026-08-08 `a38a236`, two days and six commits;
///    2026-08-10, the the old znippy engine crate deletion). It is now also `cargo xtask
///    bench-build`, which is the whole point. Second, `--all-targets` is
///    explicit there rather than implied by `cargo test`, with the measurement
///    that makes it load-bearing written down beside it.
///
///    `bench/` is still the ONLY place `gunnar-wire/stages` is ever compiled, so
///    the coverage that entry provided has to keep existing somewhere; it does.
///  * `guards` — `xtask/guards`, the repository guards that call a shared crate (RA4-7: the
///    A32-14 spawn guard is nornir-xtask-support's `spawn_guard`). Detached so that
///    nornir-xtask-support never unifies into the root; this entry is the only thing that runs it.
///  * `front`, `front-features` — `crates/gunnar-front` (OX-GUNBR 2026-10-28): detached since
///    2026-10-22 (it takes crates that live only in sibling checkouts) and run by NO step until
///    then, `tests/grpc_hole.rs` among them. Once at its defaults, once with every feature a
///    front `[[test]]` requires; `xtask/tests/front_tests_run_in_ci.rs` reads the second list
///    against the front's Cargo.toml, so a new feature-gated front test cannot hide.
///
/// `rustfmt::skip`: one entry per line, because `front_tests_run_in_ci.rs` reads the entries
/// as text.
#[rustfmt::skip]
const DETACHED: &[(&str, &str, &[&str])] = &[
    ("installtest", "installtest/Cargo.toml", &[]),
    ("guards", "xtask/guards/Cargo.toml", &[]),
    ("front", "crates/gunnar-front/Cargo.toml", &[]),
    ("front-features", "crates/gunnar-front/Cargo.toml", &["--features", "metered,mcp,clock-fetch,public-site"]),
];

fn task_ci() -> ExitCode {
    // `--quick`: the per-push set dwarf's CI forge runs (40 GB target cap). It used to be
    // ignored, and the full set below filled 138 GB of target there (2026-10-29).
    if std::env::args().skip(2).any(|a| a == "--quick") {
        return task_ci_quick();
    }
    let root = guard::workspace_root();
    let mut failed: Vec<String> = Vec::new();
    // Every step that was announced, in order. See `ci_step`.
    let mut ran: Vec<&str> = Vec::new();

    // ONE writer for "format this repository", shared with
    // `cargo xtask fmt` and with the `fmt` alias in `.cargo/config.toml`.
    // `xtask/src/fmt.rs` carries the argument; the short version is that
    // `cargo fmt --all` walks local path dependencies out of the tree (12556
    // diffs in five repositories gunnar does not own) AND still never reaches
    // three trees inside it (608 more).
    //
    // This step used to build its own argv from `member_manifests()` and shell
    // out to `cargo fmt --manifest-path`. Both halves were wrong now:
    //
    //  * `members` is not the tree. It misses `bench/` and `installtest/`, both
    //    detached workspaces. `fmt::package_manifests` walks the filesystem
    //    instead and finds them.
    //  * `cargo fmt` is now the alias, so shelling out to it from here would
    //    re-enter xtask. `fmt::run` invokes the `cargo-fmt` binary directly.
    //
    // Collected rather than fatal, per `8ceda51`: a whitespace diff must not be
    // able to hide a dependency violation behind it.
    ci_cheap_rows(&root, &mut ran, &mut failed, false);

    let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());

    // ── THE BENCH-TARGET GUARD, and it is SECOND on purpose ─────────────────
    //
    // The rule this step exists for: build the bench target.
    // `bench/` is a detached workspace, so nothing else in this
    // file — not `clippy --workspace --all-targets`, not `test --workspace` —
    // compiles a single line of it, and it has silently stopped building twice.
    //
    // Placed here, ahead of `preflight`, and that placement is the fix rather
    // than a preference. `preflight` `return`s FAILURE for the whole task when a
    // required external binary is missing, so anything below it is unreachable
    // on a machine without OpenSSH — and whether `bench/` compiles has nothing
    // whatsoever to do with whether this box can run the interop tests. The old
    // `bench-compile` entry in `DETACHED` sat below that early return and below
    // twenty minutes of clippy and tests.
    //
    // One writer with `cargo xtask bench-build`: the same function, so
    // the cheap command a human runs between edits and the step that gates `ci`
    // cannot come to different verdicts.
    ci_step(&mut ran, "bench-build");
    if !xtask::benchguard::run(&root, xtask::benchguard::Depth::Build, &cargo) {
        eprintln!("xtask ci: FAILED at `bench-build` (continuing, so a later guard is still run)");
        failed.push("bench-build".to_string());
    }

    // A `znippy checkout` STEP stood here and is gone (2026-09-09). It diagnosed
    // the one failure mode a sibling path dependency creates — without `../znippy`
    // on disk the ROOT workspace does not resolve, and every step below dies with
    // a manifest error that says nothing about this repository. `znippy-plugin-git`
    // and `git-storage-trait` are crates.io versions now, so there is no such
    // state to diagnose: the resolve needs nothing but the registry, and the step
    // could only ever have printed its `NOT RUN` banner. See the note above
    // `ci_step` for why a permanently-skipping step is worse than no step.

    let steps: &[(&str, &[&str])] = &[
        (
            "clippy",
            &[
                "clippy",
                "--workspace",
                "--all-targets",
                // `gunnar-store/fs` was in this list until 2026-08-16, and the
                // feature it named was amputated with the packfile engine on
                // 2026-08-10 (gunnar-store now offers only `conformance`).
                // cargo refuses the whole resolve for a feature that does not
                // exist, so the stale word did not silently narrow this step —
                // it took the step down entirely, and `test` below with it.
                "--features",
                "gunnar-lfs/fs,gunnar-cli/http",
                "--",
                "-D",
                "warnings",
            ],
        ),
        (
            "test",
            // `gunnar-cli/http` is not optional here even though the feature
            // itself is default-OFF: `tests/http_transport.rs` is gated to it,
            // so without this the whole smart-HTTP suite reports *nothing* —
            // not a failure, not a skip, nothing — and the transport would be
            // covered by a green nobody has seen red.
            //
            // `--no-fail-fast` for the same reason, one level up. Without it
            // cargo stops after the FIRST failing test target, and the run's
            // totals are then a prefix of the suite rather than the suite.
            // MEASURED on `bca5c6f`, twice, minutes apart on the same tree:
            //
            //   without --no-fail-fast   72 targets, 555 passed, 1 failed
            //   with    --no-fail-fast  146 targets, 1130 passed, 0 failed
            //
            // The one failure was `gunnar-pack`'s
            // `the_pack_trailer_is_not_paying_collision_detection`, which is a
            // load-sensitive perf guard: it refuses when the machine is too
            // busy to tell 18 MiB/s from 17 MiB/s, which is the RIGHT thing for
            // it to do — but on a loaded the desktop that one refusal was silently
            // removing 575 real tests from the run, and 555 is a number a
            // reader would happily quote as "the suite".
            &[
                "test",
                "--workspace",
                "--no-fail-fast",
                // `gunnar-store/fs` gone for the same reason as in `clippy`
                // above: the feature no longer exists and cargo fails the
                // resolve outright when it is named.
                "--features",
                // `gunnar-server/fleet-pull`: the pull channel on the shared pump (M12) is
                // estate-only now; its tests (fleet_order, sign_in_again, the repair units) run here.
                // A32-18: the mirror pull's https half and the SSO egress are default-off
                // (the airgap default) and the appliance rows name them; their tests run here.
                // One list with the workflow's suite (`ci_check::SUITE_FEATURES`, OX-WIRED4).
                xtask::ci_check::SUITE_FEATURES,
            ],
        ),
        (
            "appliance-build",
            // The appliance is its own crate now; the whole crate IS the server,
            // so no `--features server` / `--no-default-features` dance.
            &["build", "-p", "gunnar-server"],
        ),
    ];

    if !task_preflight() {
        // Still fatal, and immediately: `preflight` says the interop tests
        // CANNOT be proven on this machine, so running them would produce
        // `SKIPPED:` lines that pass. A step that would report a green nobody
        // has seen red is not worth continuing to.
        return ExitCode::FAILURE;
    }

    for (name, args) in steps {
        ci_step(&mut ran, name);
        if !run_at(&root, &cargo, args) {
            eprintln!("xtask ci: FAILED at `{name}` (continuing, so a later guard is still run)");
            failed.push((*name).to_string());
        }
    }

    // ── the free server links nothing paid (round 5.3.1, owner 2026-10-08) ──
    //
    // `appliance-build` above built `gunnar-server` with its default features;
    // its tree must hold nothing from Vetra's monetize-impl and none of the
    // metered half. Each offender is named with the path that pulls it in.
    ci_step(&mut ran, "free-tree");
    if !xtask::paid_guard::run(&root, &cargo) {
        eprintln!("xtask ci: FAILED at `free-tree` (continuing, so a later guard is still run)");
        failed.push("free-tree".to_string());
    }
    // ── G-BOTH: gunnar builds BOTH ways, on every push (owner order 2026-10-26) ──
    //
    // `lone-clone`: this commit cloned alone, `cargo build --locked` and the two
    // `cargo install --locked --path … --features client` lines, the server booted, in an
    // empty CARGO_HOME. The committed root lock is the crates.io lock (G-B7);
    // `lone-clone-staged` runs the same on this workspace's packaged crates.
    // `crates-io-install`: `cargo install gunnar --features client` from crates.io in a
    // clean home (needs the network); `crates-io-install-staged` installs the PACKAGED
    // gunnar with the crates crates.io lacks patched in, so a broken install is seen
    // BEFORE the publish. `twins`: every Build Thing cargo row is its `cargo install`
    // line (the same crates, the row's binary).
    ci_step(&mut ran, "lone-clone");
    if !lone_ok(
        "lone-clone",
        xtask::paid_guard::lone_clone_with(&root, &cargo, false, false),
    ) {
        failed.push("lone-clone".to_string());
    }
    ci_step(&mut ran, "lone-clone-staged");
    if !lone_ok(
        "lone-clone-staged",
        xtask::paid_guard::lone_clone_with(&root, &cargo, false, true),
    ) {
        failed.push("lone-clone-staged".to_string());
    }
    ci_step(&mut ran, "crates-io-install");
    if !both_ways_ok(
        "crates-io-install",
        xtask::both_ways::crates_io_install(&root, &cargo, false),
    ) {
        failed.push("crates-io-install".to_string());
    }
    ci_step(&mut ran, "crates-io-install-staged");
    if !both_ways_ok(
        "crates-io-install-staged",
        xtask::both_ways::crates_io_install(&root, &cargo, true),
    ) {
        failed.push("crates-io-install-staged".to_string());
    }
    ci_step(&mut ran, "twins");
    if !xtask::twins::run(&root, &cargo) {
        eprintln!("xtask ci: FAILED at `twins` (continuing, so a later guard is still run)");
        failed.push("twins".to_string());
    }

    // ── The named extras (GUN-PRODUCTS, 2026-10-30): each product with `full` ──
    // No estate row ships `online`, `federation` or `full` (`[build.allow_unbuilt]` says
    // so), so this step is what compiles them: `cargo install <product> --features full`.
    ci_step(&mut ran, "extras");
    for product in ["gunnar", "gunnar-cli"] {
        if !run_at(
            &root,
            &cargo,
            &["check", "--locked", "-p", product, "--features", "full"],
        ) {
            eprintln!("xtask ci: FAILED at `extras` ({product} --features full; continuing)");
            failed.push(format!("extras ({product})"));
        }
    }

    // ── The guide "Gunnar by hand", run as written (GUN-PRODUCTS) ──
    // The three products built from this checkout (and the gunnar-ui one beside it) in the
    // toolchain container, then the guide's own commands: two servers, the console, a
    // headless browser's passkey, a push, an encrypted repository and a twin. Not in
    // `--quick`.
    ci_step(&mut ran, "products");
    if let Err(e) = xtask::products::main(&root, &["--no-console".to_string()]) {
        eprintln!("xtask ci: FAILED at `products`: {e} (continuing)");
        failed.push("products".to_string());
    }

    ci_step(&mut ran, "gix engine arm");
    if !run_engine_arms(&root, &cargo) {
        eprintln!("xtask ci: FAILED at `engine-gix` (continuing, so a later guard is still run)");
        failed.push("engine-gix".to_string());
    }

    // The packages `--workspace` cannot see. Every one of them is on disk,
    // holds real targets, and is invisible to BOTH of the commands anyone quotes
    // a suite number from: `cargo test --workspace` skips them, and
    // `-p <name>` does not match them either (it exits 101 with "package ID
    // specification did not match any packages" — MEASURED 2026-08-04 for
    // the then-detached the old znippy engine crate, and still true of `gunnar-bench`).
    //
    // They are detached for reasons that are still correct, so the fix is not to
    // attach them; it is to run them by manifest path.
    for (name, manifest, extra) in DETACHED {
        ci_step(&mut ran, name);
        let mut argv: Vec<&str> = vec!["test", "--manifest-path", manifest, "--no-fail-fast"];
        argv.extend_from_slice(extra);
        if !run_at(&root, &cargo, &argv) {
            eprintln!("xtask ci: FAILED at `{name}` (continuing, so a later guard is still run)");
            failed.push((*name).to_string());
        }
    }

    // `gunnar-cli/systemd` was resolved by the guard and by
    // `tests/feature_split.rs` but never COMPILED by anything — a `cargo tree`
    // is a resolve, not a build, so the feature could stop compiling and no
    // command in this repository would say so.
    ci_step(&mut ran, "systemd-check");
    if !run_at(
        &root,
        &cargo,
        &[
            "check",
            "-p",
            "gunnar-cli",
            "--no-default-features",
            "--features",
            "server,systemd",
        ],
    ) {
        eprintln!(
            "xtask ci: FAILED at `systemd-check` (continuing, so a later guard is still run)"
        );
        failed.push("systemd-check".to_string());
    }

    // …and `check` COMPILES the feature without running a single one of its
    // tests. `cargo check` does not even build `#[cfg(test)]` code, and
    // `systemd` is off by default, so `cargo test --workspace` never reaches
    // `gunnar_cli::service` either. MEASURED 2026-08-06: **twelve** tests in
    // that module — the unit renderer, the `--data-root` regression of
    // 2026-08-04, the `RUST_LOG` the unit carries and the `ExecStart` preflight
    // — and no command in this repository ran any of them. The step above was
    // written because a resolve is not a build; this one exists because a build
    // is not a run.
    //
    // `--lib` and not `--all-targets`: the module under test is in the lib, and
    // `gunnar-cli`'s integration tests are `cfg(feature = "server")` and were
    // already run by the workspace pass. Re-running them under a second feature
    // resolution costs minutes and asserts nothing new.
    ci_step(&mut ran, "systemd-tests");
    if !run_at(
        &root,
        &cargo,
        &[
            "test",
            "-p",
            "gunnar-cli",
            "--no-default-features",
            "--features",
            "server,systemd",
            "--lib",
            "--no-fail-fast",
        ],
    ) {
        eprintln!(
            "xtask ci: FAILED at `systemd-tests` (continuing, so a later guard is still run)"
        );
        failed.push("systemd-tests".to_string());
    }

    // ── the front's self-registration, with the guest it pushes ─────────────
    //
    // `tests/self_register.rs` pushes gunnar's monetize guest (the wasm) through vm1's door and
    // is `metered`-only, so the `test` step above never compiled it; and it skipped itself as
    // `SKIPPED:` (a pass) whenever the wasm was not built. Here the guest is built first, and
    // `GUNNAR_TEST_REQUIRE_GUEST` turns a missing wasm into a FAILURE (wiring tracer B
    // 2026-10-08): a gate that names these tests must have run them.
    ci_step(&mut ran, "front-guest");
    // Both are DETACHED workspaces since 2026-10-22 (a lone clone must load the root
    // one): built by manifest path, into this repo's target dir as before.
    let detached_target = std::env::var("CARGO_TARGET_DIR")
        .ok()
        .filter(|v| !v.is_empty())
        .unwrap_or_else(|| root.join("target").to_string_lossy().into_owned());
    let guest = run_at_env(
        &root,
        &cargo,
        &[
            "build",
            "--manifest-path",
            "crates/gunnar-monetize-guest/Cargo.toml",
            "--release",
            "--target",
            "wasm32-unknown-unknown",
        ],
        &[("CARGO_TARGET_DIR", detached_target.as_str())],
    );
    // `tests/self_register.rs` went with the push ops (monetize v2.0 step 1: the ONE box never
    // calls into an estate); the guest is still BUILT here, so a wasm that stops compiling is red.
    if !guest {
        eprintln!("xtask ci: FAILED at `front-guest` (continuing, so a later guard is still run)");
        failed.push("front-guest".to_string());
    }

    ci_step(&mut ran, "guard");
    if task_guard() != ExitCode::SUCCESS {
        failed.push("guard".to_string());
    }

    // ── company SSO against a REAL identity provider ──────────────────────
    //
    // Keycloak in a container on a private podman network (`xtask/src/idp.rs`): the
    // OIDC, SAML, SCIM and CSV tests that only a real IdP can hold. Without podman this
    // step FAILS by name, like `cve` without cargo-deny: a gate that cannot run is not a
    // gate that passed.
    ci_step(&mut ran, "idp");
    if let Err(e) = xtask::idp::run(&root, &[]) {
        eprintln!("idp: ✗ {e}");
        eprintln!("xtask ci: FAILED at `idp` (continuing)");
        failed.push("idp".to_string());
    }

    // ── the 0-known-CVE gate ────────────────────────────────────────────────
    //
    // Last, next to `guard`: both answer "is this build safe to ship", and both
    // read a RESOLVED graph rather than source, so both need the workspace to
    // have resolved (a missing sibling znippy already failed earlier steps).
    // This is the SAME `cargo-deny` gate the deploy `door cve` runs; making it a
    // CI step asserts "0 known CVE" per RELEASE, not only per deploy. Collected
    // rather than fatal, like every step above, so a red here still prints the
    // full step list. If cargo-deny is absent this step FAILS — a gate that
    // cannot run is not a gate that passed.
    ci_step(&mut ran, "cve");
    if task_cve() != ExitCode::SUCCESS {
        failed.push("cve".to_string());
    }

    if failed.is_empty() {
        // The applied output, and the reason `ci_step` exists: the names of the
        // steps that actually ran, not the word "ok". A green that does not say
        // what it covered is indistinguishable from a run that stopped early,
        // and this function used to print nothing whatsoever on success.
        eprintln!("\nxtask ci: PASS, {} step(s):", ran.len());
        for name in &ran {
            eprintln!("  {name}");
        }
        return ExitCode::SUCCESS;
    }
    eprintln!(
        "\nxtask ci: FAILED, {} of {} step(s):",
        failed.len(),
        ran.len()
    );
    for name in &failed {
        eprintln!("  {name}");
    }
    ExitCode::FAILURE
}

/// `fmt` and the five no-cargo checks: the first rows of both the full and the quick set
/// (both check the whole tree, [`xtask::ci_quick::fmt_whole_tree`]).
fn ci_cheap_rows(root: &Path, ran: &mut Vec<&'static str>, failed: &mut Vec<String>, quick: bool) {
    let root = root.to_path_buf();
    ci_step(ran, "fmt");
    let fmt_ok = if quick {
        xtask::ci_quick::fmt_whole_tree(&root)
    } else {
        fmt::run(&root, fmt::Mode::Check)
    };
    if !fmt_ok {
        eprintln!("xtask ci: FAILED at `fmt` (continuing, so a later guard is still run)");
        failed.push("fmt".to_string());
    }

    // ── the deployment manifests ────────────────────────────────────────────
    //
    // Cheap (four files, no cargo), collected rather than fatal, and early:
    // a floating image tag or a KEK in a ConfigMap is a shipping defect, and
    // finding it after twenty minutes of clippy is finding it late.
    ci_step(ran, "deploy-check");
    if !xtask::deploy_check::run(&root) {
        eprintln!("xtask ci: FAILED at `deploy-check` (continuing)");
        failed.push("deploy-check".to_string());
    }

    // ── the installer's pin ─────────────────────────────────────────────────
    //
    // Six files, no cargo, and it belongs beside `deploy-check` for the same
    // reason: the appliance INSTALLS itself with edda's `korp-installer`, and
    // three manifests in this tree name it through three separately resolved
    // lockfiles. A bench that measures one installer while the medium ships
    // another is a shipping defect that reads green everywhere.
    ci_step(ran, "installer-pin");
    if !xtask::installer_pin::run(&root) {
        eprintln!("xtask ci: FAILED at `installer-pin` (continuing)");
        failed.push("installer-pin".to_string());
    }

    // ── ONE CLONE, and it is the cheapest step in the run ───────────────────
    //
    // A directory walk and two set differences — no cargo, no network. It is
    // here rather than late because a `path = "../edda/…"` does not break the
    // BUILD, it breaks `cargo metadata`, which means every step after it in a
    // fresh clone fails with a message about a missing manifest rather than
    // about the line that asked for it. Finding it first is finding it where
    // the answer is legible.
    ci_step(ran, "one-clone");
    if !xtask::one_clone::run(&root) {
        eprintln!("xtask ci: FAILED at `one-clone` (continuing)");
        failed.push("one-clone".to_string());
    }

    // ── and the workflows, for the same reason and at the same price ────────
    //
    // Two files, no cargo. The rules still live here rather than in YAML; what
    // `.github/` adds is that the CVE gate and the suite run without anybody
    // remembering to type this command. That is worth checking from the command
    // it is a backstop for.
    ci_step(ran, "ci-check");
    if !xtask::ci_check::run(&root) {
        eprintln!("xtask ci: FAILED at `ci-check` (continuing)");
        failed.push("ci-check".to_string());
    }

    // ── and what the tracked tree is publishing ─────────────────────────────
    //
    // `git ls-files` and a read of each file's first 512 bytes. Beside the two
    // document checks because it is the same shape of question — what does this
    // repository SHIP — and the same price.
    ci_step(ran, "key-census");
    if !xtask::key_census::run(&root) {
        eprintln!("xtask ci: FAILED at `key-census` (continuing)");
        failed.push("key-census".to_string());
    }
}

/// `cargo xtask ci --quick`: [`xtask::ci_quick`] (the rows, the one graph and why). fmt, the
/// no-cargo checks, preflight, then clippy and the tests of the default build, both into the
/// caller's `CARGO_TARGET_DIR` with [`xtask::ci_quick::ENV`].
fn task_ci_quick() -> ExitCode {
    let root = guard::workspace_root();
    let mut failed: Vec<String> = Vec::new();
    let mut ran: Vec<&str> = Vec::new();
    ci_cheap_rows(&root, &mut ran, &mut failed, true);
    if !task_preflight() {
        return ExitCode::FAILURE;
    }
    let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
    ci_step(&mut ran, "clippy");
    if !run_at_env(&root, &cargo, xtask::ci_quick::CLIPPY, xtask::ci_quick::ENV) {
        eprintln!("xtask ci: FAILED at `clippy` (continuing)");
        failed.push("clippy".to_string());
    }
    ci_step(&mut ran, "test");
    if !xtask::ci_quick::test_row(&root, &cargo) {
        eprintln!("xtask ci: FAILED at `test`");
        failed.push("test".to_string());
    }
    ci_step(&mut ran, "front");
    if !run_at_env(&root, &cargo, xtask::ci_quick::FRONT, xtask::ci_quick::ENV) {
        eprintln!("xtask ci: FAILED at `front` (gunnar-front's tests)");
        failed.push("front".to_string());
    }
    for (name, why) in xtask::ci_quick::SKIP {
        eprintln!("xtask ci --quick: skipped {name}: {why} (the full `cargo xtask ci` runs it)");
    }
    if failed.is_empty() {
        eprintln!("\nxtask ci --quick: PASS, {} step(s):", ran.len());
        for name in &ran {
            eprintln!("  {name}");
        }
        return ExitCode::SUCCESS;
    }
    eprintln!(
        "\nxtask ci --quick: FAILED, {} of {} step(s):",
        failed.len(),
        ran.len()
    );
    for name in &failed {
        eprintln!("  {name}");
    }
    ExitCode::FAILURE
}

/// Announce a `ci` step **and count it**, because the count is what makes the
/// green mean something.
///
/// `xtask ci` returned `ExitCode::SUCCESS` and printed **nothing at all** until
/// 2026-08-05. That is the silent green this repository keeps finding in other
/// people's code: a log that ends after the last guard and a log from a process
/// that was killed halfway are byte-identical, and the only way to tell them
/// apart was to already know how many steps there are. It cost an hour
/// on 2026-08-05, reading a passing run as a truncated one.
///
/// So every step is announced through here, `ran` is the list, and the verdict
/// at the end prints it. One writer rather than eleven `eprintln!`s and a
/// literal `11` somewhere: a step added without a line here would make
/// the count wrong, and a step added without the *header* is not a step.
/// Print a lone-clone verdict; true unless it failed.
fn lone_ok(name: &str, v: xtask::paid_guard::LoneClone) -> bool {
    match v {
        xtask::paid_guard::LoneClone::Skipped(why) => {
            eprintln!("{name}: SKIPPED — {why}");
            true
        }
        xtask::paid_guard::LoneClone::Passed => {
            eprintln!("{name}: a clone of this commit alone builds --locked, installs both lines and serves");
            true
        }
        xtask::paid_guard::LoneClone::Failed(why) => {
            eprintln!("{name}: ✗ {why}");
            eprintln!("xtask ci: FAILED at `{name}` (continuing, so a later guard is still run)");
            false
        }
    }
}

/// Print a both-ways verdict; true when it passed.
fn both_ways_ok(name: &str, v: xtask::both_ways::Verdict) -> bool {
    match v {
        xtask::both_ways::Verdict::Passed(said) => {
            eprintln!("{name}: {said}");
            true
        }
        xtask::both_ways::Verdict::Failed(why) => {
            eprintln!("{name}: ✗ {why}");
            eprintln!("xtask ci: FAILED at `{name}` (continuing, so a later guard is still run)");
            false
        }
    }
}

/// [`both_ways_ok`] as an exit code, for the verb.
fn both_ways_verdict(name: &str, v: xtask::both_ways::Verdict) -> ExitCode {
    ok(both_ways_ok(name, v))
}

fn ci_step<'a>(ran: &mut Vec<&'a str>, name: &'a str) {
    ran.push(name);
    eprintln!("\n── xtask ci: {name} ──");
}

/// The **gix** storage arm, which the `test` step above cannot reach.
///
/// # What this step used to be, and why it is not that any more
///
/// Until 2026-08-10 this ran `cargo test --manifest-path
/// crates/gunnar-znippy/Cargo.toml`: a DETACHED crate holding the `gunnar`
/// binary with the storage engine installed into a runtime `OnceLock`, which
/// `cargo test --workspace` compiled none of and `-p gunnar-znippy` did not
/// match either.
///
/// **`crates/gunnar-znippy` is deleted.** The engine
/// is a *feature* of `gunnar-server` now — `engine-znippy`, and it is the
/// **default** — so the znippy arm is covered by `cargo test --workspace` like
/// any other workspace member, and a step of its own would be a second run of
/// the same thing.
///
/// Two facts about the old step are worth not losing:
///
/// * Its skip message named three test files — `cold_tier_wired.rs`,
///   `refs_from_archive.rs`, `refs_overlay_after_a_push.rs` — and **none of the
///   three was on disk**. The crate's only test was `tests/engine_wired.rs`. So
///   the `UNCOVERED by this ci run:` list, whose whole job is to say truthfully
///   what a green does not cover, had been naming files that did not exist.
/// * The `NOT RUN` fall-open it guarded is largely moot for the root workspace:
///   `gunnar-server` names `git-storage-trait` and `znippy-plugin-git` by path
///   unconditionally, so **without a sibling znippy checkout the workspace does
///   not resolve at all** and every earlier step has already failed. That is
///   now the probe's whole job: it is asked once, before the workspace steps,
///   so the resolve failure is diagnosed in one place instead of four times.
///
/// # What is genuinely uncovered, and is what this runs
///
/// `engine-gix`. `cargo test --workspace` takes default features, which is
/// `engine-znippy`, so nothing in the covering command ever compiles the gix
/// arm — the second implementation of `GitOps`, whose entire reason to exist is
/// to be compared against the first. That is the same class of hole the old
/// step existed to close, one seam further in.
fn run_engine_arms(root: &std::path::Path, cargo: &str) -> bool {
    run_at(
        root,
        cargo,
        &[
            "test",
            "-p",
            "gunnar-server",
            "--no-default-features",
            "--features",
            "server,engine-gix",
            "--no-fail-fast",
        ],
    )
}

fn run_at(dir: &Path, program: &str, args: &[&str]) -> bool {
    run_at_env(dir, program, args, &[])
}

/// [`run_at`] with `env` set for that one command.
fn run_at_env(dir: &Path, program: &str, args: &[&str], env: &[(&str, &str)]) -> bool {
    Command::new(program)
        .current_dir(dir)
        .args(args)
        .envs(env.iter().copied())
        .status()
        .map(|s| s.success())
        .unwrap_or(false)
}

/// `cargo xtask deploy-check [--red]`: the deployment manifests, asserted.
///
/// `--red` runs the injury sweep instead: every deliberate defect must turn the
/// check red, because a checker that cannot report red is worth nothing.
fn task_deploy_check() -> ExitCode {
    let root = guard::workspace_root();
    if std::env::args().any(|a| a == "--red") {
        let manifests = match xtask::deploy_check::Manifests::read(&root) {
            Ok(manifests) => manifests,
            Err(why) => {
                eprintln!("xtask deploy-check --red: {why}");
                return ExitCode::FAILURE;
            }
        };
        let survived = xtask::deploy_check::red(&manifests);
        if survived.is_empty() {
            println!(
                "deploy-check --red: all {} injuries turned it red",
                xtask::deploy_check::Injury::ALL.len()
            );
            return ExitCode::SUCCESS;
        }
        for injury in survived {
            eprintln!(
                "  ✗ {} stayed GREEN; that check does not bite",
                injury.label()
            );
        }
        return ExitCode::FAILURE;
    }
    ok(xtask::deploy_check::run(&root))
}

/// `cargo xtask ci-check [--red]`: the workflows under `.github/`, asserted.
///
/// The mirror of `deploy-check`, over the other set of documents this
/// repository ships and does not compile. `--red` proves every check bites.
fn task_ci_check() -> ExitCode {
    let root = guard::workspace_root();
    if std::env::args().any(|a| a == "--red") {
        let workflows = match xtask::ci_check::Workflows::read(&root) {
            Ok(workflows) => workflows,
            Err(why) => {
                eprintln!("xtask ci-check --red: {why}");
                return ExitCode::FAILURE;
            }
        };
        let survived = xtask::ci_check::red(&workflows);
        if survived.is_empty() {
            println!(
                "ci-check --red: all {} injuries turned it red",
                xtask::ci_check::Injury::ALL.len()
            );
            return ExitCode::SUCCESS;
        }
        for injury in survived {
            eprintln!(
                "  ✗ {} stayed GREEN; that check does not bite",
                injury.label()
            );
        }
        return ExitCode::FAILURE;
    }
    ok(xtask::ci_check::run(&root))
}

/// The harness half of the shared-directory defect, asserted where `xtask`
/// makes the decision.
///
/// `installtest`'s own tests prove `lane_dir::lane_dir` separates worktrees;
/// these prove `xtask` actually *calls* it, which is a different claim — the bug
/// removed was a `String` constant sitting exactly here, and it would survive
/// every test in the other crate untouched.
#[cfg(test)]
mod installtest_target {
    use super::*;

    const LANE_A: &str = "~/scratch/wt/__lane_a__/gunnar";
    const LANE_B: &str = "~/scratch/wt/__lane_b__/gunnar";

    /// Red-first, seen: put the constant back where the `.env()` call had it and
    /// this fails naming the shared path.
    #[test]
    fn two_worktrees_do_not_share_the_harness_target_directory() {
        let a = installtest_target_dir_with(Path::new(LANE_A), None);
        let b = installtest_target_dir_with(Path::new(LANE_B), None);
        assert_ne!(
            a,
            b,
            "`cargo xtask install-test` sends every worktree to {}, so five worktrees queue on one \
             cargo package lock instead of running",
            a.display()
        );
        assert_ne!(
            a,
            lane_dir::home_expanded(INSTALLTEST_TARGET_ROOT),
            "the derivation collapsed back onto the shared root"
        );
        assert!(
            a.starts_with(lane_dir::home_expanded(INSTALLTEST_TARGET_ROOT)),
            "{} is not on md1 under the harness root",
            a.display()
        );
    }

    /// And it is still the same directory next time, or no `install-test` run is
    /// ever incremental.
    #[test]
    fn one_worktree_gets_one_harness_target_directory() {
        assert_eq!(
            installtest_target_dir_with(Path::new(LANE_A), None),
            installtest_target_dir_with(Path::new(LANE_A), None)
        );
    }

    /// An explicitly named directory is an instruction and still wins; an empty
    /// one is not a name and must not send a build to `""`.
    #[test]
    fn an_explicit_directory_wins_and_an_empty_one_does_not() {
        assert_eq!(
            installtest_target_dir_with(Path::new(LANE_A), Some("~/scratch/x".into())),
            Path::new("~/scratch/x")
        );
        assert_eq!(
            installtest_target_dir_with(Path::new(LANE_A), Some("  ".into())),
            installtest_target_dir_with(Path::new(LANE_A), None),
            "a blank override was taken as a directory name"
        );
    }
}