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 ")
);
}