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

nordisk/gunnar

crates/gunnar-recipe/tests/cli_matrix.rs

//! **The CLI-door matrix** — every variant `variants()` declares, driven
//! through `dwarves buildthing gunnar --variant <v>`, the way the plan's rule
//! reads (dwarves `build-thing-model-plan.md`, *"Integration tests build
//! through the CLI"*): the dry gate always, the real build when the box says
//! so, ONE nornir-testmatrix row per variant either way.
//!
//! ```text
//! tier  when                     what runs                                                    row
//! dry   always                   dwarves buildthing gunnar --variant v --list                 cli_matrix/<v>
//! real  GUNNAR_BUILD_REAL=1      dwarves buildthing gunnar --variant v [flags]                cli_matrix/<v>
//!       GUNNAR_BUILD_REAL=a,b    the same, for the named variants only
//! ```
//!
//! # The `--build` tier (wave 3) — at the foot of this file
//!
//! `--build <edge>` is the design's OTHER kind of dependency: another build
//! thing whose artifact this one consumes, built as a STEP with its own
//! `--opt`. gunnar declares one such edge, on both media
//! (`tunnr/tunnr-kernel : kernel`).
//!
//! ```text
//! tier  when                     what runs                                                    row
//! dry   always                   dwarves … --variant v --list --build kernel [--opt kernel.…] cli_matrix_build/<v>
//! real  GUNNAR_BUILD_REAL        dwarves … --variant v --build kernel --opt kernel.preset=…   cli_matrix_build/<v>+real
//! ```
//!
//! # The binary
//!
//! `DWARVES_BIN`, else the sibling dwarves checkout's DECLARED `dwarves` row:
//! its artifact where the row says it lands, or — none built yet — built from
//! the row by `nornir_build_thing::build_variant` (see `dwarves_bin`). Never an
//! INSTALLED `dwarves`, because every "the CLI lacks flag X" report on this
//! constellation so far was a stale install rather than a missing surface.
//! **This matrix needs a dwarves checkout** beside the repository (or
//! `DWARVES_BIN`). With NEITHER, and only then, both tiers SKIP by name
//! (`[cli-matrix-needs-dwarves]`, `no_dwarves_checkout`): the first test rowless,
//! the `--build` tier filing ONE RED row naming the absence on the sink. A
//! checkout that is there and does not give a usable binary (its declaration
//! does not load, its build failed, the binary cannot run) is RED to cargo — a
//! matrix that turns green because nothing ran is the failure this crate's rows
//! exist to make impossible. Every row carries the driving binary's own
//! `--version` (`dwarves <ver> (<sha>)`), so which build answered is on the
//! record rather than inferred.
//!
//! # The flags (law C through the door)
//!
//! The real tier renders each variant's DEFAULT state from its settings tree
//! through
//! `flags_of` — `--opt <feature>=on|off` as declared, `--opt <val>=<default>`
//! — so the line the node menu shows is the line the CLI is given. Settings
//! (`--param`) carry no default and are not invented here; a variant that
//! REQUIRES one (the installer's `admin_key`, `--app`) is refused by dwarves
//! BY NAME, and that refusal is the row. Without the feature no flags are
//! rendered (the exe variants declare none anyway). Ungated since the 0.1.10
//! pin, which is where the settings tree ships.
//!
//! # The receipt (law E on a real build)
//!
//! A green real build of a cargo variant is read back off the artifact:
//! `receipt::read_receipt` over the `.nordisk.buildthing` section — never by
//! running it. Asserted: `typ` (the receipt's `kind` equals the declared
//! kind), `sha` (the receipt names THIS recipe by `CONTENT_HASH`, and the
//! artifact's own sha256 goes on the row), `fragment` (a kernel decision's
//! fragment sha when the variant carries a kernel edge; the exe variants carry
//! none and the row says so). Composed kinds (container, iso, installer) have
//! no cargo artifact to read a section out of; their row is dwarves' verdict.
//!
//! # Where the real tier builds — said, not assumed
//!
//! The declared `[build.toolchain]` (nordisk-cargo-toolchain 1.97.1) is the
//! road every forge build is meant to take. Which road the `dwarves` on the
//! box takes is the binary's decision, not this test's; the row records the
//! cargo line dwarves printed and where the artifact landed, so a host build
//! is visible as one.
//!
//! # The real tier, run once on this box (2026-08-26, `GUNNAR_BUILD_REAL=gunnar-cli`)
//!
//! `dwarves` of 20:16 (carries `f5e0854`'s code road). The code road was REFUSED
//! BY NAME — `nordisk-cargo-toolchain` declares no `wasm32-unknown-unknown` —
//! and the door fell back to the declaration, as the refusal says it does. The
//! variant then built in the toolchain container (5 m 23 s wall, 2.7 s of CPU
//! in this process tree: the compile ran outside it) and the receipt read back
//! off the artifact: `typ exe · recipe 6e20f1cde604 · artifact sha256
//! c9a4fc6c98ce9b45 · fragment none (no kernel edge)`. Eight rows landed on the
//! `NORNIR_TESTMATRIX_OUT` sink, `cli_matrix/<variant>`, seven dry and one
//! real. The `"cell"` key read below is the receipt's wire literal, kept on
//! purpose (see `variants.rs`).

use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::OnceLock;

use gunnar_recipe::{GunnarBuildThing, CONTENT_HASH};
use nornir_build_thing::plugin::BuildThing;
use nornir_build_thing::variant::BuildVariantSet;
use nornir_testmatrix::functional_status;
use sha2::{Digest, Sha256};

/// The matrix component every row of this file is filed under.
const COMPONENT: &str = "gunnar-build-thing";
/// The check name: one per variant, `cli_matrix/<variant>`.
const CHECK: &str = "cli_matrix";

fn repo_root() -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR"))
        .parent()
        .and_then(Path::parent)
        .expect("crates/gunnar-build-thing has two ancestors")
        .to_path_buf()
}

/// **The one absence this matrix skips for, named**: no `DWARVES_BIN` and no dwarves checkout
/// beside this repository at all (`../dwarves` is not there). The matrix NEEDS a dwarves
/// checkout (or `DWARVES_BIN`); a box that has neither cannot run it, and says so by name
/// (`[cli-matrix-needs-dwarves]`) instead of failing the workspace's `cargo test`.
///
/// Everything else stays RED, as before: a `DWARVES_BIN` that is not a file or does not run, a
/// dwarves checkout whose declaration does not load, a declared row that does not build, a
/// binary that refuses a variant. A checkout that is there and broken is the failure this file
/// exists to catch, so only the checkout being absent is a skip.
fn no_dwarves_checkout() -> Option<String> {
    if std::env::var_os("DWARVES_BIN").is_some() {
        return None;
    }
    let sibling = repo_root()
        .parent()
        .map(|p| p.join("dwarves"))
        .unwrap_or_default();
    (!sibling.exists()).then(|| {
        format!(
            "[cli-matrix-needs-dwarves] this matrix needs a dwarves checkout at {} (or DWARVES_BIN \
             pointed at a binary) and there is none: SKIPPED. Check dwarves out beside this \
             repository to run it.",
            sibling.display()
        )
    })
}

/// The `dwarves` to drive, or the reason there is none.
///
/// `DWARVES_BIN` first — an explicit choice stays one. Otherwise the binary is
/// **the sibling dwarves checkout's DECLARED `dwarves` row**
/// ([`DWARVES_VARIANT`] in `../dwarves/.nornir/build-variants.toml`), found
/// where that row says its artifact lands (`artifact_under_target`, under the
/// sibling's `target/` or `CARGO_TARGET_DIR`), and — when none has been built
/// yet — BUILT from that row by `nornir_build_thing::build_variant`, the one
/// writer the forge and every xtask materialize a row through.
///
/// When dwarves declares a `[build.toolchain]` (it does since dwarves 3f57aad) that
/// build runs in the pinned container through `build_variant_in_container`, never on
/// this host, the same road the forge takes.
///
/// Until 2026-09-26 this searched `target/{release,debug}/dwarves` by hand and
/// otherwise asked for `DWARVES_BIN` pointed at a hand-built binary: any
/// `cargo build` of dwarves with any feature set answered, including a debug
/// build without `build-image`/`appliance-iso` — the very builders the fat
/// client is declared to carry for the container/iso rows this matrix drives.
/// Now the declaration picks the features, the profile and the bin, and a
/// build this file makes is that row, not a guess at it.
///
/// An INSTALLED `dwarves` is still never searched for: every "the CLI lacks
/// flag X" report on this constellation so far was a stale install rather
/// than a missing surface. [`dwarves_stamp`] puts the chosen binary's own
/// `--version` on every row, so which build answered is on the record.
fn dwarves_bin() -> Result<PathBuf, String> {
    if let Some(p) = std::env::var_os("DWARVES_BIN") {
        let p = PathBuf::from(p);
        return if p.is_file() {
            usable(&p)
        } else {
            Err(format!("DWARVES_BIN={} is not a file", p.display()))
        };
    }
    // Once per test binary: three tests ask, and a second concurrent
    // `build_variant` would only queue on cargo's lock to redo the first.
    static DECLARED: OnceLock<Result<PathBuf, String>> = OnceLock::new();
    DECLARED.get_or_init(declared_dwarves).clone()
}

/// The declared row of the sibling dwarves this matrix is driven by: the FAT
/// client, the one declared with the container and iso builders compiled in.
const DWARVES_VARIANT: &str = "dwarves";

/// Find the declared `dwarves` row's artifact, or build it from the row.
fn declared_dwarves() -> Result<PathBuf, String> {
    let sibling = repo_root()
        .parent()
        .map(|p| p.join("dwarves"))
        .unwrap_or_default();
    let decl = sibling.join(".nornir/build-variants.toml");
    let set = BuildVariantSet::load_file(&decl).map_err(|e| {
        format!(
            "no declared dwarves to drive: {} does not load ({e:#}). Check out dwarves beside \
             this repository, or point DWARVES_BIN at a binary.",
            decl.display()
        )
    })?;
    let row = set
        .variants
        .iter()
        .find(|v| v.name == DWARVES_VARIANT)
        .ok_or_else(|| format!("{} declares no `{DWARVES_VARIANT}` row", decl.display()))?;
    let under = row
        .artifact_under_target()
        .ok_or_else(|| format!("`{DWARVES_VARIANT}` declares no bin, so it names no artifact"))?;
    // Where cargo puts it: the sibling's own `target/`, or the target dir this
    // run shares (`CARGO_TARGET_DIR`), which `build_variant` below also honours.
    let mut roots = vec![sibling.join("target")];
    if let Some(t) = std::env::var_os("CARGO_TARGET_DIR") {
        roots.insert(0, PathBuf::from(t));
    }
    if let Some(found) = roots.iter().map(|r| r.join(&under)).find(|p| p.is_file()) {
        return usable(&found);
    }
    eprintln!(
        "cli_matrix: no built `{DWARVES_VARIANT}` under {} — building the declared row \
         (`{}`)",
        roots
            .iter()
            .map(|r| r.display().to_string())
            .collect::<Vec<_>>()
            .join(", "),
        row.command_line()
    );
    // The road is the DECLARATION's, as in the forge (`build_thing.rs`, "THE
    // CONTAINERS RULE"): dwarves declares `[build.toolchain]` (dwarves 3f57aad), so
    // it builds inside that pinned image and never on this host. Host cargo only
    // for a checkout that declares none.
    let out = match set.toolchain.as_ref() {
        Some(tc) => {
            let paths = nornir_build_thing::HostPaths::from_env(&sibling);
            std::fs::create_dir_all(&paths.target_dir)
                .map_err(|e| format!("create {}: {e}", paths.target_dir.display()))?;
            nornir_build_thing::build_variant_in_container(
                &set,
                DWARVES_VARIANT,
                &sibling,
                &paths.target_dir,
                paths.registry_bind(),
                tc,
                &nornir_build_thing::cargo_container::DraupnirCargoRunner::new(),
            )
        }
        None => nornir_build_thing::build_variant(&set, DWARVES_VARIANT, &sibling),
    }
    .map_err(|e| format!("building the declared `{DWARVES_VARIANT}` row failed: {e:#}"))?;
    let bin = out
        .artifacts
        .iter()
        .find(|a| a.file_name() == under.file_name())
        .ok_or_else(|| {
            format!(
                "the declared `{DWARVES_VARIANT}` row built, but reported no `{}` among {:?}",
                under.display(),
                out.artifacts
            )
        })?;
    usable(bin)
}

/// **Can this binary RUN here?** — asked once, where the binary is chosen.
///
/// `dwarves_bin` used to hand back any path that `is_file()`, and a binary
/// that exists is not a binary that executes. MEASURED on builder 2026-09-12:
/// gunnar's suite run in the toolchain container found the host-built
/// `../dwarves/target/release/dwarves`, which needs GLIBC 2.43 where the image
/// has 2.41, so the dynamic loader killed it before `main`. Every variant then
/// reported
///
/// ```text
///   gunnar-server: dry: REFUSED exit ok=false listed=false — …
///   /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.43' not found …
/// ```
///
/// — eighteen rows that read as *dwarves refused eighteen variants* and were
/// one environment mismatch. The loader's sentence was in there, at the end of
/// each row, which is exactly where a reader stops looking.
///
/// So the probe happens once and the refusal names the cause. This is the
/// external twin of gunnar-server's `make_unwritable`: a premise that
/// cannot be established is a loud, named failure, never a misattributed one.
fn usable(bin: &Path) -> Result<PathBuf, String> {
    match Command::new(bin).arg("--version").output() {
        Ok(out) if out.status.success() => Ok(bin.to_path_buf()),
        Ok(out) => Err(format!(
            "{} exists but `--version` exited {} — it cannot run in THIS environment, so \
             every variant below would report dwarves refusing it. Its own words:\n    {}\n  \
             The usual cause is a binary built against another libc (a host build run inside \
             a container: `version GLIBC_x.yz not found`). Build dwarves where the tests run, \
             or point DWARVES_BIN at one that does.",
            bin.display(),
            out.status,
            String::from_utf8_lossy(&out.stderr)
                .trim()
                .replace('\n', "\n    ")
        )),
        Err(e) => Err(format!(
            "{} exists but could not be executed at all: {e}. Point DWARVES_BIN at a binary \
             built for this environment.",
            bin.display()
        )),
    }
}

/// The chosen binary's own `--version` line — `dwarves <ver> (<sha>)`, which
/// names the source commit it was built from. On every row this file emits, so
/// a green matrix says WHICH dwarves produced it.
fn dwarves_stamp(bin: &Path) -> String {
    Command::new(bin)
        .arg("--version")
        .output()
        .ok()
        .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
        .filter(|s| !s.is_empty())
        .unwrap_or_else(|| "(no --version)".to_string())
}

/// Which variants the real tier builds: none, all, or a named list.
fn real_tier(names: &[String]) -> Vec<String> {
    match std::env::var("GUNNAR_BUILD_REAL") {
        Err(_) => Vec::new(),
        Ok(v) if v.trim().is_empty() || v.trim() == "0" => Vec::new(),
        Ok(v) if v.trim() == "1" => names.to_vec(),
        Ok(v) => {
            let wanted: Vec<String> = v.split(',').map(|s| s.trim().to_string()).collect();
            for w in &wanted {
                assert!(
                    names.iter().any(|n| n == w),
                    "GUNNAR_BUILD_REAL names '{w}', which variants() does not declare: {names:?}"
                );
            }
            wanted
        }
    }
}

struct Run {
    ok: bool,
    stdout: String,
    stderr: String,
}

/// `dwarves buildthing <this checkout> --variant <v> <args>`.
///
/// ⚠ **It said `gunnar` — the NAME — until 2026-09-20, and that resolved a
/// DIFFERENT CHECKOUT.** dwarves' `../<p>` rule turns the bare name into a
/// sibling of the current directory, and it says so out loud:
///
/// ```text
/// buildthing gunnar: /home/rickard/git/gunnar — resolved by sibling of cwd (../)
/// ```
///
/// So this gate drove whatever `~/git/gunnar` happened to be — another lane's
/// branch, a stale tree, or (measured on loki 2026-09-20, from a second gunnar
/// clone) a checkout that simply did not have the row under test:
///
/// ```text
/// gunnar-clock-musl: dry: REFUSED … no such variant. 'ORIGINAL' has: … [20 rows]
/// ```
///
/// for a variant this crate's own `VARIANTS` declares and whose emitted toml
/// carries. A test that compiles from one tree and asks about another is not a
/// gate; it is a coincidence that has been green because everybody had exactly
/// one gunnar checkout and it was called `gunnar`. The path is passed instead,
/// which is the same argument `private-gunnar-ops` reaches gunnar by
/// (`GUNNAR_CHECKOUT`, a path chosen at run time) and the same lesson as
/// dwarves' recipe-crate lookup: the directory name is a convention, the tree
/// is the fact.
fn dwarves(bin: &Path, variant: &str, args: &[String]) -> Run {
    let out = Command::new(bin)
        .current_dir(repo_root())
        .arg("buildthing")
        .arg(repo_root())
        .args(["--variant", variant])
        .args(args)
        .output()
        .unwrap_or_else(|e| panic!("could not run {}: {e}", bin.display()));
    Run {
        ok: out.status.success(),
        stdout: String::from_utf8_lossy(&out.stdout).into_owned(),
        stderr: String::from_utf8_lossy(&out.stderr).into_owned(),
    }
}

/// The last non-empty line of a refusal, for the row.
fn last_line(s: &str) -> String {
    s.lines()
        .rev()
        .map(str::trim)
        .find(|l| !l.is_empty())
        .unwrap_or("(no output)")
        .to_string()
}

/// The default-state flags for `variant` — law C through the door.
fn default_flags(set: &BuildVariantSet, variant: &str) -> Vec<String> {
    use nornir_build_thing::flags::{flags_of, Flag};
    use nornir_build_thing::tree::{NodeKind, SettingsTree};
    let Some(tree) = SettingsTree::of_set(set, variant) else {
        return Vec::new();
    };
    let mut out = Vec::new();
    for node in tree.walk() {
        match &node.kind {
            NodeKind::Feature(f) => out.push(Flag::opt(&node.path, f.default).render()),
            NodeKind::Val(v) if !v.default.is_empty() => {
                out.push(Flag::choice(&node.path, &v.default).render())
            }
            _ => {}
        }
    }
    // Every flag rendered here is one `flags_of` can produce — the menu's
    // line and this line are the same statement.
    let all: Vec<String> = flags_of(&tree).iter().map(Flag::render).collect();
    for f in &out {
        assert!(
            all.contains(f),
            "{f} is not a flag the tree can produce: {all:?}"
        );
    }
    // `--opt a=b` as two argv words, the shell shape dwarves takes.
    out.iter()
        .flat_map(|f| f.splitn(2, ' ').map(str::to_string).collect::<Vec<_>>())
        .collect()
}

/// Where dwarves' host cargo puts a cargo variant's binary: the target dir
/// this process inherits (dwarves inherits the same environment), else the
/// one cargo's own config names, else `<repo>/target`.
fn artifact_of(variant: &nornir_build_thing::variant::BuildVariant) -> Option<PathBuf> {
    let bin = variant.bin.as_deref()?;
    let target_dir = std::env::var_os("CARGO_TARGET_DIR")
        .map(PathBuf::from)
        .or_else(|| {
            let out = Command::new(env!("CARGO"))
                .current_dir(repo_root())
                .args([
                    "metadata",
                    "--format-version",
                    "1",
                    "--no-deps",
                    "--offline",
                ])
                .output()
                .ok()?;
            let text = String::from_utf8_lossy(&out.stdout).into_owned();
            let key = "\"target_directory\":\"";
            let start = text.find(key)? + key.len();
            let end = text[start..].find('"')? + start;
            Some(PathBuf::from(&text[start..end]))
        })
        .unwrap_or_else(|| repo_root().join("target"));
    let profile = match variant.profile.label() {
        "" | "dev" => "debug".to_string(),
        other => other.to_string(),
    };
    Some(match &variant.target {
        Some(t) => target_dir.join(t).join(&profile).join(bin),
        None => target_dir.join(&profile).join(bin),
    })
}

fn sha256_of(path: &Path) -> String {
    let bytes = std::fs::read(path).unwrap_or_else(|e| panic!("read {}: {e}", path.display()));
    let digest = Sha256::digest(&bytes);
    digest.iter().map(|b| format!("{b:02x}")).collect()
}

/// The receipt half of a green real build: typ, sha, fragment.
fn assert_receipt(variant: &nornir_build_thing::variant::BuildVariant) -> Result<String, String> {
    let Some(path) = artifact_of(variant) else {
        return Ok(format!(
            "typ {} · composed (no cargo artifact carries a section) · fragment none",
            variant.kind.as_str()
        ));
    };
    if !path.is_file() {
        return Err(format!(
            "dwarves reported green and {} does not exist — the artifact landed somewhere \
             this test did not look (CARGO_TARGET_DIR / cargo config target-dir)",
            path.display()
        ));
    }
    let receipt = nornir_build_thing::receipt::read_receipt(&path)
        .map_err(|e| format!("read the receipt of {}: {e:#}", path.display()))?
        .ok_or_else(|| format!("{} carries no .nordisk.buildthing section", path.display()))?;
    // typ
    let kind = receipt.settings.get("kind").cloned().unwrap_or_default();
    if kind != String::from(variant.kind) {
        return Err(format!(
            "receipt typ {kind:?} ≠ declared {:?} for {}",
            String::from(variant.kind),
            variant.name
        ));
    }
    // sha: the recipe this receipt names, and the artifact itself
    if receipt.build_thing.hash.as_deref() != Some(CONTENT_HASH) {
        return Err(format!(
            "receipt names recipe hash {:?}, this recipe is {CONTENT_HASH} — the binary was \
             built by another spelling of gunnar-build-thing",
            receipt.build_thing.hash
        ));
    }
    let artifact_sha = sha256_of(&path);
    // fragment: only a kernel edge commits one
    let fragment = receipt
        .settings
        .iter()
        .chain(receipt.inputs.iter())
        .find(|(k, _)| k.contains("fragment"))
        .map(|(k, v)| format!("{k}={v}"));
    // The edge from the MIRROR, not from the loaded set: `IsoBuild` under the
    // 0.1.9 pin has no `kernel_donor`, so `set.isos` answers "no edge" for the
    // live medium and this law would hold the installer while waving the iso
    // through. `declared_kernel_edge` reads `recipe::kernel_donor_of`, which
    // covers both consumers under either pin.
    let has_kernel_edge = declared_kernel_edge(&variant.name).is_some();
    if fragment.is_none() && has_kernel_edge {
        return Err(format!(
            "{} carries a kernel edge and its receipt records no fragment",
            variant.name
        ));
    }
    let variant_key = receipt.settings.get("cell").cloned().unwrap_or_default();
    if variant_key != variant.name {
        return Err(format!(
            "receipt says it is {variant_key:?}, dwarves built {:?}",
            variant.name
        ));
    }
    Ok(format!(
        "typ {kind} · recipe {} · artifact sha256 {} · fragment {} · {}",
        &CONTENT_HASH[..12],
        &artifact_sha[..16],
        fragment.unwrap_or_else(|| "none (no kernel edge)".to_string()),
        path.display()
    ))
}

#[test]
fn every_declared_variant_passes_through_the_cli_door() {
    if let Some(why) = no_dwarves_checkout() {
        eprintln!("cli_matrix: {why}");
        return;
    }
    let bin = match dwarves_bin() {
        Ok(b) => b,
        // Not a skip. This used to `return` and report `ok`, so on any box
        // without a dwarves build beside the checkout the door was "tested" by
        // a test that could not fail — MEASURED by the mon session 2026-09-05:
        // listed as a known red, green in fact, and hollow. A guard that
        // cannot fail in the most common way of running it is worse than red.
        // Only a box with NO dwarves checkout at all skips, by name, above
        // (`no_dwarves_checkout`); a checkout that is there and fails is this panic.
        Err(why) => {
            panic!("cli_matrix: the CLI door cannot be tested without a dwarves binary — {why}")
        }
    };
    let set = GunnarBuildThing.variants();
    let names: Vec<String> = set.variants.iter().map(|v| v.name.clone()).collect();
    assert!(names.len() >= 8, "gunnar declares eight rows; {names:?}");
    let real = real_tier(&names);
    eprintln!(
        "cli_matrix: {} via {} — dry gate over {} variant(s), real tier over {:?}",
        COMPONENT,
        bin.display(),
        names.len(),
        real
    );

    let mut rows: BTreeMap<String, (bool, String)> = BTreeMap::new();
    let mut failures = Vec::new();

    for v in &set.variants {
        // ── dry ──────────────────────────────────────────────────────────────
        let dry = dwarves(&bin, &v.name, &["--list".to_string()]);
        let listed = dry.stdout.contains(&v.name);
        let dry_ok = dry.ok && listed;
        let dry_detail = if dry_ok {
            let line = dry
                .stdout
                .lines()
                .map(str::trim)
                .find(|l| l.starts_with("cargo "))
                .map(str::to_string)
                .unwrap_or_else(|| format!("kind {}", v.kind.as_str()));
            format!("dry: {line}")
        } else {
            format!(
                "dry: REFUSED exit ok={} listed={} — {}",
                dry.ok,
                listed,
                last_line(&dry.stderr)
            )
        };
        eprintln!("  {:<22} {}", v.name, dry_detail);
        if !dry_ok {
            failures.push(format!("{}: {dry_detail}", v.name));
        }
        rows.insert(v.name.clone(), (dry_ok, dry_detail.clone()));

        // ── real ─────────────────────────────────────────────────────────────
        if !real.contains(&v.name) {
            continue;
        }
        let flags = default_flags(&set, &v.name);
        let run = dwarves(&bin, &v.name, &flags);
        let detail = if run.ok {
            match assert_receipt(v) {
                Ok(d) => (true, format!("real: {d}")),
                Err(e) => (false, format!("real: built, receipt REFUSED — {e}")),
            }
        } else {
            (false, format!("real: REFUSED — {}", last_line(&run.stderr)))
        };
        eprintln!(
            "  {:<22} {} (flags {:?})\n{}",
            v.name,
            detail.1,
            flags,
            run.stderr
                .lines()
                .filter(|l| l.starts_with("──") || l.contains("$ cargo"))
                .map(|l| format!("      {l}"))
                .collect::<Vec<_>>()
                .join("\n")
        );
        if !detail.0 {
            failures.push(format!("{}: {}", v.name, detail.1));
        }
        rows.insert(v.name.clone(), detail);
    }

    // ONE row per variant, on the channel appliance_matrix_rows.rs uses.
    for (name, (ok, detail)) in &rows {
        functional_status(COMPONENT, &format!("{CHECK}/{name}"), *ok, detail);
    }
    assert_eq!(rows.len(), names.len(), "one row per declared variant");

    // A composed kind is refused by dwarves BY NAME in the real tier when it
    // needs what nobody gave it (an app .znippy, a site key); that refusal is
    // a red row, not a failure of this gate — the gate fails on a SILENT
    // outcome (exit 0 with nothing produced, a receipt that lies) or a dry
    // refusal. Real-tier refusals are printed and carried on the rows.
    let hard: Vec<&String> = failures
        .iter()
        .filter(|f| !f.contains("real: REFUSED") || f.contains("(no output)"))
        .collect();
    assert!(
        hard.is_empty(),
        "{} variant(s) failed the CLI door:\n  {}",
        hard.len(),
        hard.iter()
            .map(|s| s.as_str())
            .collect::<Vec<_>>()
            .join("\n  ")
    );
}

// ─────────────────────────────────────────────────────────────────────────────
// THE `--build` TIER — wave 3
// ─────────────────────────────────────────────────────────────────────────────
//
// `--build <edge>` is the second kind of dependency in the design's f3: not a
// `--opt` already in the recipe, but ANOTHER build thing whose artifact this
// one consumes, built as a STEP with its own `--opt`. gunnar declares exactly
// one such edge, twice: the live medium and the installer both name
// `tunnr/tunnr-kernel : kernel`. The two tests below drive that edge through
// the CLI door — the plan always, the build when the box says so.

/// The check name of the `--build` tier: one row per variant,
/// `cli_matrix_build/<variant>`.
const CHECK_BUILD: &str = "cli_matrix_build";

/// **The kernel edge a variant declares**, `"<repo>/<kernel>"` — read from the
/// recipe mirror (`recipe::kernel_donor_of`), which is the ONE place this crate
/// answers "does this variant consume a donor kernel?".
///
/// It covers BOTH consumers: `[build.installer.kernel_donor]` rides the
/// `InstallerBuild` field the 0.1.9 pin has, and `[build.iso.kernel_donor]`
/// rides the free function the pin forces (`recipe::iso_kernel_donor`, and
/// `tests/iso_edge_linked.rs` is the RED that says why). Reading the loaded
/// `BuildVariantSet` instead — as `assert_receipt` did until this change — sees
/// the installer's edge and NOT the iso's, because `IsoBuild` under 0.1.9 has
/// no such field: the fragment law would then hold the installer and wave the
/// live medium through.
fn declared_kernel_edge(variant: &str) -> Option<String> {
    gunnar_recipe::kernel_donor_of(variant).map(|d| format!("{}/{}", d.repo, d.kernel))
}

/// One `step i/N · …` line of a dry pass, taken apart.
///
/// The shape dwarves prints (`build_steps::BuildStep::describe`):
///
/// ```text
/// step 1/1 · kernel → tunnr/tunnr-kernel : kernel · donor at ~/git/tunnr (resolved by sibling of cwd (../)) · --opt kernel.preset=nuked · after a, b
/// ```
#[derive(Debug, Clone)]
struct PlannedStep {
    /// 1-based position as the line states it.
    index: usize,
    /// How many steps the line says there are.
    of: usize,
    /// The consuming variant's path to this edge (`kernel`; `rootfs.kernel` for
    /// a donor's donor, which is why the path is read and not assumed).
    path: String,
    /// `<repo>/<variant>` of the donor.
    coordinate: String,
    /// The edge's declared typ.
    typ: String,
    /// Where the donor checkout resolved to on this box.
    donor_root: PathBuf,
    /// The `--opt` pairs ROUTED onto this step, `key=value`, in order.
    opts: Vec<String>,
    /// Coordinates this step waits for.
    needs: Vec<String>,
}

/// Every `step …` line of a dry pass, in the order printed.
///
/// A parser and not a `contains`: the ORDER, the ROUTED opts and the `after`
/// list are the three things this tier asserts, and none of them survives a
/// substring test — `stderr.contains("tunnr/tunnr-kernel")` is true of a plan
/// that never ordered anything.
fn parse_plan(stderr: &str) -> Vec<PlannedStep> {
    let mut out = Vec::new();
    for line in stderr.lines().map(str::trim) {
        let Some(rest) = line.strip_prefix("step ") else {
            continue;
        };
        let mut segs = rest.split(" · ");
        let Some(pos) = segs.next() else { continue };
        let Some((i, n)) = pos.split_once('/') else {
            continue;
        };
        let (Ok(index), Ok(of)) = (i.trim().parse::<usize>(), n.trim().parse::<usize>()) else {
            continue;
        };
        // `<path> → <coordinate> : <typ>`
        let Some(edge) = segs.next() else { continue };
        let Some((path, tail)) = edge.split_once(" → ") else {
            continue;
        };
        let Some((coordinate, typ)) = tail.rsplit_once(" : ") else {
            continue;
        };
        let mut donor_root = PathBuf::new();
        let mut opts = Vec::new();
        let mut needs = Vec::new();
        for seg in segs {
            if let Some(d) = seg.strip_prefix("donor at ") {
                let d = d.split(" (resolved by ").next().unwrap_or(d);
                donor_root = PathBuf::from(d.trim());
            } else if seg.starts_with("--opt ") {
                opts.extend(
                    seg.split_whitespace()
                        .filter(|w| *w != "--opt")
                        .map(str::to_string),
                );
            } else if let Some(a) = seg.strip_prefix("after ") {
                needs.extend(
                    a.split(',')
                        .map(|s| s.trim().to_string())
                        .filter(|s| !s.is_empty()),
                );
            }
        }
        out.push(PlannedStep {
            index,
            of,
            path: path.trim().to_string(),
            coordinate: coordinate.trim().to_string(),
            typ: typ.trim().to_string(),
            donor_root,
            opts,
            needs,
        });
    }
    out
}

/// **Donor-first, as a law over the printed plan** — the positions are the
/// list's own, every coordinate a step waits for stands EARLIER in the list,
/// and the consuming variant is not a step of its own plan.
///
/// Depth, said honestly rather than implied: gunnar's live chain is ONE deep —
/// both media consume `tunnr/tunnr-kernel`, and tunnr's `[[build.kernel]]`
/// declares no edges of its own — so the `after` loop below walks a
/// one-element list HERE, and a one-element list is topologically ordered by
/// construction. What this file therefore proves about gunnar is the part that
/// is NOT free: that the plan is the DONOR's step and not the target's, that
/// the numbering is the plan's, and (in the caller) that the choice is routed
/// onto it. The deep case — a three-link chain, a ring refused by name, one
/// coordinate asked for two different builds — is edda's `BuildDag::derive`
/// and, since dwarves `7881fd7`, dwarves' `plan_build_steps`, which is a
/// CONSUMER of that derivation rather than a second ordering of it. This
/// function is written for depth N so that the day gunnar grows a second link
/// it is already asserted.
fn assert_donor_first(steps: &[PlannedStep], target_coordinate: &str) -> Result<(), String> {
    let mut seen: Vec<&str> = Vec::new();
    for (i, s) in steps.iter().enumerate() {
        if s.index != i + 1 || s.of != steps.len() {
            return Err(format!(
                "step {} of {} is printed as 'step {}/{}' — the plan's numbering is not the plan",
                i + 1,
                steps.len(),
                s.index,
                s.of
            ));
        }
        if s.coordinate == target_coordinate {
            return Err(format!(
                "{target_coordinate} is a STEP of its own plan — the target is what the steps \
                 feed, not one of them"
            ));
        }
        for need in &s.needs {
            if !seen.contains(&need.as_str()) {
                return Err(format!(
                    "step {}/{} ({}) waits for {need}, which is not planned before it: {:?}",
                    s.index,
                    s.of,
                    s.coordinate,
                    steps
                        .iter()
                        .map(|x| x.coordinate.as_str())
                        .collect::<Vec<_>>()
                ));
            }
        }
        seen.push(&s.coordinate);
    }
    Ok(())
}

/// The donor step of a plan, by the coordinate the recipe declares.
fn donor_step<'a>(steps: &'a [PlannedStep], coordinate: &str) -> Result<&'a PlannedStep, String> {
    let hits: Vec<&PlannedStep> = steps
        .iter()
        .filter(|s| s.coordinate == coordinate)
        .collect();
    match hits.as_slice() {
        [one] => Ok(one),
        [] => Err(format!(
            "the plan names no step for {coordinate}: {:?}",
            steps
                .iter()
                .map(|s| s.coordinate.as_str())
                .collect::<Vec<_>>()
        )),
        many => Err(format!("{coordinate} is planned {} times", many.len())),
    }
}

/// **The dry tier of `--build`: every variant, the plan written donor-first.**
///
/// The two arms are the point, and the second is the red the first would be
/// worthless without:
///
/// * a variant that DECLARES a kernel edge (`gunnar-appliance-iso`,
///   `gunnar-installer`, `gunnar-installer-open`, `gunnar-installer-gce`) must exit 0 and
///   print a plan whose step is ITS OWN DONOR at the donor checkout, ordered
///   donor-first — `tunnr/tunnr-kernel` for three of them and
///   `tunnr/tunnr-kernel-scsi` for the virtio-scsi medium, which is why the
///   expected coordinate is read off each row rather than written here once;
/// * a variant that declares NONE (the five exe/service rows, the container)
///   must be REFUSED BY NAME, naming the variant and the flag, and must print
///   NO step at all. Seven of gunnar's eleven rows take this arm, so a door that
///   planned a kernel for everything — or for nothing — cannot pass both.
///
/// And the identity value, which is the trap this edge sits right on top of:
/// tunnr DECLARES `preset = "nuked"`, so a `--opt kernel.preset=nuked` that
/// never reached the donor would look exactly like one that did. The routing is
/// therefore asserted over THREE lines per consumer — no `--opt` at all (the
/// step must carry none), `kernel.preset=nuked`, and `kernel.preset=slim` (a
/// preset tunnr does NOT declare) — and each must appear on the step verbatim.
/// A constant router fails at least one of the three.
#[test]
fn the_build_kernel_line_plans_the_donor_first() {
    let set = GunnarBuildThing.variants();
    let names: Vec<String> = set.variants.iter().map(|v| v.name.clone()).collect();
    let edges: Vec<(&String, Option<String>)> =
        names.iter().map(|n| (n, declared_kernel_edge(n))).collect();
    // **The media WITH a kernel edge, by (variant, donor) pair rather than by
    // count.** The count is the right property — a medium that silently lost
    // its kernel edge is what this catches — and the bare number was the wrong
    // way to spell it: it read `4` and failed `left: 5 / right: 4` from
    // `c12aac70` (2026-09-02, the AWS Nitro variant) onwards, saying only that
    // a number moved. The pairs say WHICH medium and WHICH donor, so a wrong
    // donor is caught too — which a count never could, and which is the
    // failure that matters here: each cloud twin needs its own donor row
    // because the shared donor's `nuked` preset vetoes the controller each one
    // needs by name.
    let mut with_edge: Vec<(&str, &str)> = edges
        .iter()
        .filter_map(|(n, e)| e.as_deref().map(|d| (n.as_str(), d)))
        .collect();
    with_edge.sort_unstable();
    let mut expected = vec![
        ("gunnar-appliance-iso", "tunnr/tunnr-kernel"),
        (gunnar_recipe::SEALED_INSTALLER, "tunnr/tunnr-kernel"),
        (gunnar_recipe::OPEN_INSTALLER, "tunnr/tunnr-kernel"),
        (gunnar_recipe::GCE_INSTALLER, "tunnr/tunnr-kernel-scsi"),
        (gunnar_recipe::AWS_INSTALLER, "tunnr/tunnr-kernel-nvme"),
        (gunnar_recipe::AZURE_INSTALLER, "tunnr/tunnr-kernel-hyperv"),
        // The METERED twin shares the UpCloud donor, and that is the POINT:
        // metering is a property of the binary the medium composes, not of the
        // machine it boots on. A metered medium needing its own kernel would
        // mean the entitlement code had reached into the hardware, which it has
        // not.
        (gunnar_recipe::METERED_INSTALLER, "tunnr/tunnr-kernel"),
        // And the metered twins of the two cloud rows take THEIR donors, for
        // the same reason: the binary changed, the machine did not.
        (
            gunnar_recipe::METERED_AWS_INSTALLER,
            "tunnr/tunnr-kernel-nvme",
        ),
        (
            gunnar_recipe::METERED_AZURE_INSTALLER,
            "tunnr/tunnr-kernel-hyperv",
        ),
        // The FRONT medium (2026-09-25) boots the UpCloud machine vm1 is, on the
        // SAME donor: a different payload, the same machine.
        (gunnar_recipe::FRONT_INSTALLER, "tunnr/tunnr-kernel"),
        (gunnar_recipe::FRONT_METERED_INSTALLER, "tunnr/tunnr-kernel"),
        // A twin is NOT a row here (2026-09-17): it installs the
        // medium of the primary it twins (the role comes over the wire), so it takes that row's donor by being
        // that row.
    ];
    expected.sort_unstable();
    assert_eq!(
        with_edge, expected,
        "gunnar declares its kernel edge on exactly these media, each with the donor its \
         hardware needs: the live iso and the sealed installer and its keyless twin share \
         `tunnr-kernel`; the GCE twin needs `tunnr-kernel-scsi` and the AWS Nitro twin \
         `tunnr-kernel-nvme`, because the shared donor's `nuked` preset vetoes CONFIG_SCSI \
         and the NVMe driver by name. A new cloud twin is added HERE in the same commit \
         that declares it. The recipe mirror says: {edges:?}"
    );

    if let Some(why) = no_dwarves_checkout() {
        // RED on the sink still (the matrix reads that no dwarves answered), skipped to cargo:
        // the absence is named, and a checkout that is there and broken stays red below.
        eprintln!("cli_matrix_build: {why}");
        functional_status(
            COMPONENT,
            &format!("{CHECK_BUILD}/-no-dwarves-checkout"),
            false,
            &why,
        );
        return;
    }
    let bin = match dwarves_bin() {
        Ok(b) => b,
        Err(why) => {
            // Loud and RED on the sink, never a silent skip: a `--build` matrix
            // that goes green because no binary answered is precisely the shape
            // this file exists to make impossible. One row, named, carrying the
            // reason verbatim.
            eprintln!("cli_matrix_build: NO DWARVES — {why}");
            functional_status(
                COMPONENT,
                &format!("{CHECK_BUILD}/-no-dwarves-binary"),
                false,
                &why,
            );
            // Red on the sink AND red to cargo: the row above is what the
            // testmatrix reads, this is what `cargo test` reads, and until
            // 2026-09-05 only the first was true.
            panic!(
                "cli_matrix_build: the --build matrix cannot run without a dwarves binary — {why}"
            );
        }
    };
    let stamp = dwarves_stamp(&bin);
    eprintln!("cli_matrix_build: driving {} — {stamp}", bin.display());

    let mut failures: Vec<String> = Vec::new();
    let mut rows: BTreeMap<String, (bool, String)> = BTreeMap::new();
    // How many consumers routed BOTH presets. At least one must, or a router
    // that ignored the line entirely could pass by having every variant veto the
    // value that would have caught it.
    let mut routed_both = 0usize;

    for (name, edge) in &edges {
        let target = format!("gunnar/{name}");
        let base = [
            "--list".to_string(),
            "--build".to_string(),
            "kernel".to_string(),
        ];
        let run = dwarves(&bin, name, &base);
        let steps = parse_plan(&run.stderr);

        let Some(edge) = edge else {
            // ── the refusal arm ──────────────────────────────────────────────
            let named = run.stderr.contains(name.as_str())
                && run.stderr.contains("--build kernel")
                && run.stderr.contains("edge");
            let ok = !run.ok && named && steps.is_empty();
            let detail = if ok {
                format!("no edge: REFUSED BY NAME — {}", last_line(&run.stderr))
            } else {
                format!(
                    "no edge: the door did NOT refuse by name (exit ok={}, named={named}, \
                     {} step(s) planned) — {}",
                    run.ok,
                    steps.len(),
                    last_line(&run.stderr)
                )
            };
            eprintln!("  {name:<22} {detail}");
            if !ok {
                failures.push(format!("{name}: {detail}"));
            }
            rows.insert((*name).clone(), (ok, format!("{detail} · {stamp}")));
            continue;
        };

        // ── the edge arm ─────────────────────────────────────────────────────
        let mut why: Vec<String> = Vec::new();
        if !run.ok {
            why.push(format!(
                "`--build kernel` was refused: {}",
                last_line(&run.stderr)
            ));
        }
        if let Err(e) = assert_donor_first(&steps, &target) {
            why.push(e);
        }
        let mut line = String::new();
        match donor_step(&steps, edge) {
            Err(e) => why.push(e),
            Ok(s) => {
                if s.typ != "kernel" {
                    why.push(format!(
                        "{edge} is planned as typ {:?}, not `kernel`",
                        s.typ
                    ));
                }
                if s.path != "kernel" {
                    why.push(format!(
                        "the consuming path is {:?}; gunnar declares the edge directly on the \
                         medium, so it is `kernel`",
                        s.path
                    ));
                }
                if !s.donor_root.is_dir() {
                    why.push(format!(
                        "the donor checkout {} does not exist — the plan resolved a coordinate to \
                         nothing",
                        s.donor_root.display()
                    ));
                }
                if !s.opts.is_empty() {
                    why.push(format!(
                        "nothing was given on the line and the step carries {:?} — the router \
                         invents opts",
                        s.opts
                    ));
                }
                line = format!(
                    "step {}/{} · {} → {} : {} · donor at {}",
                    s.index,
                    s.of,
                    s.path,
                    s.coordinate,
                    s.typ,
                    s.donor_root.display()
                );
            }
        }

        // The choice, routed — two values, one of them NOT the donor's declared
        // default, so a constant cannot satisfy both.
        //
        // **A consumer may legitimately REFUSE one of them**, and since
        // `gunnar-installer-gce` was added one does: that medium needs virtio-scsi,
        // so its closure requires `CONFIG_SCSI=y`, and the `nuked` preset
        // fragment says `n` BY NAME. The composer refuses at plan time and is
        // right to. Driving every variant with a hardcoded `nuked` was an
        // assumption that stopped being true, not a defect in the refusal.
        //
        // So each (consumer, preset) must end in ONE of two correct outcomes —
        // the step carries the opt verbatim, or the plan is refused by a
        // sentence that names the preset AND the conflict. Silence, or a
        // refusal that explains nothing, is still a failure.
        //
        // The identity-value trap survives this: `routed_both` below requires
        // that SOME consumer routed both values, so a constant router cannot
        // pass by having every variant veto the awkward one.
        let mut routed_here = 0usize;
        for preset in ["nuked", "slim"] {
            let args = [
                "--list".to_string(),
                "--build".to_string(),
                "kernel".to_string(),
                "--opt".to_string(),
                format!("kernel.preset={preset}"),
            ];
            let r = dwarves(&bin, name, &args);
            if !r.ok {
                // A refusal is correct when this consumer's own requirements
                // conflict with the preset — but it has to SAY so, naming both,
                // or an unrelated breakage would be absorbed as "expected".
                let said = format!("{}{}", r.stdout, r.stderr);
                let names_preset = said.contains(preset);
                let names_conflict = said.contains("vetoes")
                    || said.contains("preset fragment says")
                    || said.contains("preset vetoes");
                if names_preset && names_conflict {
                    eprintln!(
                        "  {name:<22} preset={preset} refused by name (the consumer's closure \
                         conflicts with the preset) — correct"
                    );
                } else {
                    why.push(format!(
                        "`--build kernel --opt kernel.preset={preset}` was refused, and the \
                         refusal names {}{} — a refusal an operator cannot act on is not a \
                         correct one: {}",
                        if names_preset {
                            "the preset but not the conflict"
                        } else {
                            "neither the preset"
                        },
                        if names_preset {
                            ""
                        } else {
                            " nor the conflict"
                        },
                        last_line(&r.stderr)
                    ));
                }
                continue;
            }
            routed_here += 1;
            let p = parse_plan(&r.stderr);
            match donor_step(&p, edge) {
                Err(e) => why.push(format!("with preset={preset}: {e}")),
                Ok(s) => {
                    let want = format!("kernel.preset={preset}");
                    if s.opts != [want.clone()] {
                        why.push(format!(
                            "`--opt kernel.preset={preset}` reached the donor step as {:?}, not \
                             [{want:?}] — the consuming line's choice is not what the donor is asked to build",
                            s.opts
                        ));
                    }
                }
            }
        }

        if routed_here == 2 {
            routed_both += 1;
        }
        let ok = why.is_empty();
        let detail = if ok {
            format!("edge {edge}: {line} · routed {routed_here} of 2 presets")
        } else {
            format!("edge {edge}: {}", why.join(" ; "))
        };
        eprintln!("  {name:<22} {detail}");
        if !ok {
            failures.push(format!("{name}: {detail}"));
        }
        rows.insert((*name).clone(), (ok, format!("{detail} · {stamp}")));
    }

    for (name, (ok, detail)) in &rows {
        functional_status(COMPONENT, &format!("{CHECK_BUILD}/{name}"), *ok, detail);
    }
    assert!(
        routed_both >= 1,
        "no consumer routed BOTH presets, so nothing here would catch a router that ignores \
         `--opt kernel.preset` entirely — the identity value this test exists for is gone"
    );
    assert_eq!(
        rows.len(),
        names.len(),
        "one --build row per declared variant"
    );
    assert!(
        failures.is_empty(),
        "{} variant(s) failed the --build door:\n  {}",
        failures.len(),
        failures.join("\n  ")
    );
}

/// The pin verdict a dry pass prints under a kernel step — `describe_plan`'s
/// second line, verbatim.
///
/// ```text
///    by pin: /…/.kernel-out/tunnr-kernel/bzImage (preset nuked · fragment bd2d9dbc…) — this step would build NOTHING
///    would BUILD: kernel 'tunnr-kernel' … NOT BUILT …
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
enum PinVerdict {
    /// The pin serves this choice: the step builds nothing.
    ByPin(String),
    /// The step would build, and why.
    WouldBuild(String),
    /// Neither line was printed.
    Silent,
}

fn pin_verdict(stderr: &str) -> PinVerdict {
    for line in stderr.lines().map(str::trim) {
        if let Some(r) = line.strip_prefix("by pin: ") {
            return PinVerdict::ByPin(r.to_string());
        }
        if let Some(r) = line.strip_prefix("would BUILD: ") {
            return PinVerdict::WouldBuild(r.to_string());
        }
    }
    PinVerdict::Silent
}

/// One `── step <coordinate> [<typ>]: …` outcome line of a REAL chain run.
///
/// ```text
/// ── step tunnr/tunnr-kernel [kernel]: BUILT in 78.4 s · fragment bd2d9dbcf1a03e77 · artifact 4f1c…
/// ── step tunnr/tunnr-kernel [kernel]: BY PIN in 0.1 s — built nothing · fragment bd2d9dbcf1a03e77… · artifact …
/// ```
///
/// The fragment is `short()`ed to 16 hex by dwarves, so what comes back is a
/// PREFIX of the pin's own sha — which is why the pin file is the thing asserted
/// against and not the other way round.
fn chain_step_line(stderr: &str, coordinate: &str) -> Option<String> {
    let needle = format!("step {coordinate} [");
    stderr
        .lines()
        .map(str::trim)
        .find(|l| l.starts_with("── ") && l.contains(&needle))
        .map(|l| l.trim_start_matches("── ").to_string())
}

/// The 16-hex fragment a chain outcome line names.
fn fragment_of(line: &str) -> Option<String> {
    let after = line.split("fragment ").nth(1)?;
    let tok: String = after
        .chars()
        .take_while(|c| c.is_ascii_hexdigit())
        .collect();
    (tok.len() >= 12).then_some(tok)
}

/// The kernel pin dwarves writes beside a built kernel:
/// `<donor_root>/.kernel-out/<kernel>/<kernel>.kernel-pin.json`.
fn pin_path(donor_root: &Path, kernel: &str) -> PathBuf {
    donor_root
        .join(".kernel-out")
        .join(kernel)
        .join(format!("{kernel}.kernel-pin.json"))
}

/// **The real tier of `--build`: the chain runs, and the receipt's fragment sha
/// IS the choice** — the design's law D, at the CLI door.
///
/// Behind `GUNNAR_BUILD_REAL` (the same selector the dry matrix uses: `1` for
/// every variant, or a comma list of names), because a green run builds a real
/// Linux kernel in the `nordisk-kernel-toolchain` container and then the
/// medium. Off, it is not silent: every kernel-edge variant still files a row
/// saying the tier was OFF and naming the env var, so an empty sink cannot be
/// read as a pass.
///
/// What is asserted, in the order that makes each assertion able to fail:
///
/// 1. `--build kernel --opt kernel.preset=nuked` exits green, and the chain
///    printed an outcome line for `tunnr/tunnr-kernel` carrying a fragment sha.
///    A refusal is a RED row with dwarves' own last line verbatim — never a
///    skip.
/// 2. The PIN on disk (`.kernel-out/tunnr-kernel/tunnr-kernel.kernel-pin.json`)
///    exists, says `nuked`, and contains the fragment the console printed. The
///    on-disk pin is the applied output; the console line is the claim about it.
/// 3. Law D against the IDENTITY VALUE, which this axis sits directly on:
///    tunnr DECLARES `preset = "nuked"`, so a pin whose fragment ignored the
///    choice entirely would satisfy 1 and 2. So the dry pass is asked twice
///    more against the pin just written — with `nuked` it must answer BY PIN
///    ("would build NOTHING"), and with `slim` it must answer WOULD BUILD. A
///    fragment sha that is not a function of the choice cannot produce both.
///
/// # RUN 2026-08-27, and it is RED — the wall, verbatim
///
/// Not a skip and not an assumption. `dwarves 0.1.5 (7881fd720c92)`,
/// source-built, `--variant gunnar-appliance-iso --build kernel --opt
/// kernel.preset=nuked`. The chain WIRED end to end and ran for real:
///
/// ```text
/// -- chain · gunnar/gunnar-appliance-iso --build · 1 step(s) then the consumer · runa stages: tunnr/tunnr-kernel -> consumer
/// -- step 1/2 · tunnr/tunnr-kernel · kernel · --opt kernel.preset=nuked
///    pin check: kernel 'tunnr-kernel' ... NOT BUILT — .../.kernel-out/tunnr-kernel has no bzImage + pin
///    building in container docker.io/library/debian:trixie (bounded 8 cpus · 8192 MB)
/// -- step tunnr/tunnr-kernel [kernel]: FAILED after 263.2 s — dwarves-buildthing-7 —
///    build container exited 100 (expected 0); stderr tail:
///    W: Failed to fetch http://deb.debian.org/debian/dists/trixie/InRelease  Temporary failure resolving 'deb.debian.org'
///    E: Unable to locate package gcc / make / flex / bison / bc / libelf-dev / libssl-dev
///    E: Package 'xz-utils' has no installation candidate
/// -- step 2/2 · gunnar/gunnar-appliance-iso · iso: NOT ATTEMPTED — its step tunnr/tunnr-kernel failed
/// -- chain · failed · 0/2 runa step(s) passed · 263172 ms
/// ```
///
/// Two facts in that, and neither is gunnar's to repair:
///
/// 1. **The kernel step has no DNS inside its build container.** The 6.12.104
///    tarball IS fetched (142 MB landed in `tunnr/.kernel-out/`, from the host),
///    and then the container cannot reach `deb.debian.org` to install its
///    toolchain. Essentially all of the 263 s wall is apt retrying.
/// 2. **It is a bare `docker.io/library/debian:trixie` that apt-installs
///    gcc/make/flex/bison/bc/libelf-dev/libssl-dev/xz-utils at build time — not
///    the declared, digest-pinned `nordisk-kernel-toolchain`**, which is absent
///    from this box's podman images (`podman images | grep -i kernel` -> nothing,
///    exit 1). Under the containers rule a build that is not in the declared
///    toolchain container is supposed to be a NAMED refusal; here the generic
///    base was taken and only apt said no.
///
/// For the record, the wall this run did NOT hit: the `E0463 can't find crate
/// for core` musl story. It appeared once, at the top, on the WASM code road —
/// `variant gunnar-build-thing-wasm targets wasm32-unknown-unknown, which the
/// toolchain image nordisk-cargo-toolchain does not carry std for (it declares:
/// x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl)` — and that IS a named
/// refusal: the door said so and fell back to the declaration, exactly as
/// designed. Nothing on the musl axis failed.
///
/// The cold and cached timings this tier wants belong to wave 3's timing tier, and
/// the two facts above are what W2 has to clear first. This test is written so
/// that W2's run is what turns it green, and so that the run is ASSERTED rather
/// than watched.
#[test]
fn the_real_chain_pins_the_kernel_the_choice_names() {
    let set = GunnarBuildThing.variants();
    let names: Vec<String> = set.variants.iter().map(|v| v.name.clone()).collect();
    let wanted = real_tier(&names);
    let consumers: Vec<(String, String)> = names
        .iter()
        .filter_map(|n| declared_kernel_edge(n).map(|e| (n.clone(), e)))
        .collect();
    assert!(
        !consumers.is_empty(),
        "gunnar declares a kernel edge on the two media"
    );

    let mut rows: Vec<(String, bool, String)> = Vec::new();
    let mut failures: Vec<String> = Vec::new();

    let bin = match dwarves_bin() {
        Ok(b) => Some(b),
        Err(why) => {
            eprintln!("cli_matrix_build real: NO DWARVES — {why}");
            None
        }
    };

    for (name, edge) in &consumers {
        let check = format!("{CHECK_BUILD}/{name}+real");
        if !wanted.contains(name) {
            let detail = format!(
                "real: OFF — GUNNAR_BUILD_REAL does not name {name} (set GUNNAR_BUILD_REAL=1 or \
                 =…,{name} to build the {edge} chain for real)"
            );
            eprintln!("  {name:<22} {detail}");
            rows.push((check, true, detail));
            continue;
        }
        let Some(bin) = bin.as_ref() else {
            let detail = format!(
                "real: REFUSED BY NAME — GUNNAR_BUILD_REAL names {name} and there is no \
                 source-built dwarves to drive: {}",
                dwarves_bin().err().unwrap_or_default()
            );
            eprintln!("  {name:<22} {detail}");
            failures.push(format!("{name}: {detail}"));
            rows.push((check, false, detail));
            continue;
        };
        let stamp = dwarves_stamp(bin);

        // ── 1. the chain runs ────────────────────────────────────────────────
        let args = [
            "--build".to_string(),
            "kernel".to_string(),
            "--opt".to_string(),
            "kernel.preset=nuked".to_string(),
        ];
        let run = dwarves(bin, name, &args);
        if !run.ok {
            let detail = format!(
                "real: REFUSED — `--build kernel --opt kernel.preset=nuked` exited red: {} · {stamp}",
                last_line(&run.stderr)
            );
            eprintln!("  {name:<22} {detail}");
            failures.push(format!("{name}: {detail}"));
            rows.push((check, false, detail));
            continue;
        }
        let mut why: Vec<String> = Vec::new();
        let line = chain_step_line(&run.stderr, edge);
        let printed = line.as_deref().and_then(fragment_of);
        if line.is_none() {
            why.push(format!(
                "the chain printed no outcome line for {edge} — the consumer built without its step"
            ));
        }
        if printed.is_none() {
            why.push(format!("the outcome line names no fragment sha: {line:?}"));
        }

        // ── 2. the pin on disk ───────────────────────────────────────────────
        let (_, kernel) = edge.split_once('/').unwrap_or(("", edge.as_str()));
        // The donor root the DRY pass resolved — the same ladder the run used.
        let dry = dwarves(
            bin,
            name,
            &[
                "--list".to_string(),
                "--build".to_string(),
                "kernel".to_string(),
                "--opt".to_string(),
                "kernel.preset=nuked".to_string(),
            ],
        );
        let plan = parse_plan(&dry.stderr);
        let donor_root = donor_step(&plan, edge).map(|s| s.donor_root.clone());
        match &donor_root {
            Err(e) => why.push(format!(
                "could not resolve the donor checkout after the run: {e}"
            )),
            Ok(root) => {
                let pin = pin_path(root, kernel);
                match std::fs::read_to_string(&pin) {
                    Err(e) => why.push(format!(
                        "the chain reported green and {} cannot be read: {e} — the kernel step \
                         filed no pin",
                        pin.display()
                    )),
                    Ok(text) => {
                        if !text.contains("nuked") {
                            why.push(format!(
                                "{} does not name the chosen preset `nuked`: {text}",
                                pin.display()
                            ));
                        }
                        if let Some(f) = &printed {
                            if !text.contains(f.as_str()) {
                                why.push(format!(
                                    "the console said fragment {f} and {} does not carry it — the \
                                     line and the pin disagree",
                                    pin.display()
                                ));
                            }
                        }
                    }
                }
            }
        }

        // ── 3. law D: the fragment IS the choice ─────────────────────────────
        match pin_verdict(&dry.stderr) {
            PinVerdict::ByPin(_) => {}
            other => why.push(format!(
                "after a green build with preset=nuked the pin does not serve that same choice: \
                 {other:?}"
            )),
        }
        let other = dwarves(
            bin,
            name,
            &[
                "--list".to_string(),
                "--build".to_string(),
                "kernel".to_string(),
                "--opt".to_string(),
                "kernel.preset=slim".to_string(),
            ],
        );
        match pin_verdict(&other.stderr) {
            PinVerdict::WouldBuild(_) => {}
            other => why.push(format!(
                "the pin written from preset=nuked ALSO serves preset=slim ({other:?}) — the \
                 fragment sha is not a function of the choice, which is law D"
            )),
        }

        let ok = why.is_empty();
        let detail = if ok {
            format!(
                "real: {} · pin {} · slim would rebuild · {stamp}",
                line.unwrap_or_default(),
                pin_path(
                    donor_root
                        .as_ref()
                        .map(PathBuf::as_path)
                        .unwrap_or(Path::new("")),
                    kernel
                )
                .display()
            )
        } else {
            format!("real: {} · {stamp}", why.join(" ; "))
        };
        eprintln!("  {name:<22} {detail}");
        if !ok {
            failures.push(format!("{name}: {detail}"));
        }
        rows.push((check, ok, detail));
    }

    for (check, ok, detail) in &rows {
        functional_status(COMPONENT, check, *ok, detail);
    }
    assert_eq!(
        rows.len(),
        consumers.len(),
        "one real-tier row per kernel-edge variant"
    );
    assert!(
        failures.is_empty(),
        "{} variant(s) failed the real --build chain:\n  {}",
        failures.len(),
        failures.join("\n  ")
    );
}