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

nordisk/edda

crates/nornir-build-thing/src/installer.rs

//! **Disk-installer build target** — the typed descriptor a ⚒ Build Thing cell
//! renders to master the korp **ISO disk-installer** and to boot it in KVM against a
//! blank disk (the install proof).
//!
//! Where an [`IsoBuild`](crate::iso::IsoBuild) masters a *live* bootable ISO, an
//! [`InstallerBuild`] describes the ISO that **installs the appliance onto a disk**:
//! it boots as the unattended `korp-installer` PID-1, lays a GPT + ESP, streams the
//! app `.znippy` onto the system partition, and reboots. This descriptor is *data*
//! (pure serde, dependency-light) — it materializes:
//!
//! * the [`IsoBuild`] the existing pure-Rust masterer produces for ▶ Build
//!   ([`InstallerBuild::installer_iso`]), so a repo can declare the installer as a
//!   normal `[[build.iso]]` cell the dwarves ISO cell already drives; and
//! * the **KVM boot seam** — the extra QEMU args that attach a blank target disk
//!   (and optionally a GPU) so the auto-install is testable end-to-end before any
//!   hardware ([`InstallerBuild::kvm_disk_args`]), plus the exact serial success
//!   marker the boot proof gates on ([`InstallerBuild::success_marker`]).
//!
//! It runs nothing itself; the host (the KVM e2e / a future dwarves installer view)
//! drives QEMU. It is content-addressable ([`InstallerBuild::content_id`]) like the
//! other build kinds.

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

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

use crate::iso::IsoBuild;
#[cfg(test)]
use crate::iso::IsoFormat;
use crate::variant::ArtifactKind;

/// Where the installer reads the app `.znippy` payload from — mirrors
/// `korp_installer::payload::PayloadSource` as build-thing data.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "kind")]
pub enum PayloadKind {
    /// **Default** — the `.znippy` is baked into the installer ISO (airgap/offline).
    EmbeddedIso,
    /// Alternate — pulled from S3/MinIO at install time.
    S3 {
        /// The S3 endpoint URL.
        endpoint: String,
        /// The bucket.
        bucket: String,
        /// The object key.
        key: String,
    },
}

impl Default for PayloadKind {
    fn default() -> Self {
        PayloadKind::EmbeddedIso
    }
}

impl PayloadKind {
    /// A short label for a surface / serial line (`embedded` / `s3://bucket/key`).
    pub fn label(&self) -> String {
        match self {
            PayloadKind::EmbeddedIso => "embedded".to_string(),
            PayloadKind::S3 { bucket, key, .. } => format!("s3://{bucket}/{key}"),
        }
    }
}

/// The **FAT-appliance container runtime** an installer lays down alongside korp —
/// the one selectable `ContainerBackend` a FAT install picks. A LEAN install carries
/// **no** backend (`InstallerBuild::container_backend == None`), pulls in ZERO
/// container 3pp, and boots korp as the single PID-1. Each FAT variant selects exactly
/// ONE of these (never several in one instance) — a customer who mandates podman gets
/// the podman variant, an os-less-minimal box gets youki or microVM.
///
/// This is *build-thing data* (which backend the mastered ISO installs); the actual
/// runtime linkage is korp-installer's cargo-feature-gated `fat-containers` concern.
/// A truly OS-less box (kernel + one PID-1, no daemon) only fits the DAEMONLESS
/// backends — [`Youki`](Self::Youki) (pure-Rust libcontainer) or the container-runtime-free
/// [`MicroVm`](Self::MicroVm); [`Podman`](Self::Podman)/[`Containerd`](Self::Containerd)
/// re-add a userland/service and target dev boxes / shops that mandate them.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ContainerBackend {
    /// Daemonless pure-Rust libcontainer (rootful) — THE os-less default: no daemon,
    /// minimal Rust-native 3pp. FAT sidecars (FalkorDB/MinIO/Spark) launched in-process.
    Youki,
    /// bollard → podman socket. Daemonless, lean-on-ecosystem; PROVEN today (+PONG on
    /// oden). A dev-box / not-strictly-os-less FAT path (re-adds a podman userland).
    Podman,
    /// containerd/CRI + runc/crun (or Docker) — the industry/k8s standard, for shops
    /// that mandate it. Re-adds a daemon, so not os-less-minimal.
    Containerd,
    /// tunnr QEMU **microVM per sidecar** — NO OCI runtime at all, each sidecar its own
    /// KVM microVM. All-ours, strongest isolation (defense/airgap); needs QEMU present.
    MicroVm,
}

impl ContainerBackend {
    /// The stable lower-kebab label used in a cell name / serial line / content id
    /// (`youki`, `podman`, `containerd`, `microvm`).
    pub fn label(self) -> &'static str {
        match self {
            ContainerBackend::Youki => "youki",
            ContainerBackend::Podman => "podman",
            ContainerBackend::Containerd => "containerd",
            ContainerBackend::MicroVm => "microvm",
        }
    }

    /// Parse a backend from its [`label`](Self::label) (case-insensitive); `None` for
    /// anything else (including the LEAN "no backend" sentinel).
    pub fn from_label(s: &str) -> Option<ContainerBackend> {
        match s.trim().to_ascii_lowercase().as_str() {
            "youki" => Some(ContainerBackend::Youki),
            "podman" => Some(ContainerBackend::Podman),
            "containerd" => Some(ContainerBackend::Containerd),
            "microvm" | "micro-vm" | "microvm-sidecars" => Some(ContainerBackend::MicroVm),
            _ => None,
        }
    }

    /// Whether this backend is **daemonless** — the property a truly OS-less appliance
    /// (kernel + one PID-1, no service) requires. `Youki` + `MicroVm` are; `Podman`
    /// (socket) + `Containerd` (daemon) re-add a userland/service.
    pub fn is_daemonless(self) -> bool {
        matches!(self, ContainerBackend::Youki | ContainerBackend::MicroVm)
    }
}

/// **The kernel this installer's medium boots, named by the checkout that
/// DECLARES it** — a cross-repo reference onto a `[[build.kernel]]` row.
///
/// Before this field existed the composed installer's bzImage came from an
/// environment variable or the build box's own `/boot/vmlinuz` — "a kernel
/// nobody in this repository chose", with no pin, no receipt and no way to ask
/// what is in it. The donor closes that seam as DATA: the cell names WHICH
/// checkout's `[[build.kernel]]` its medium boots, and both doors (the ⚒ pane's
/// BYGG and `dwarves buildthing … --variant <installer>`) resolve the same
/// declaration to the same built artifact, verified against its
/// [`KernelPin`](crate::kernel::KernelPin).
///
/// The reference is deliberately a NAME PAIR, not a path: the donor checkout is
/// resolved beside the consuming repo by the caller (the constellation layout
/// rule every cross-repo seam here uses), and
/// [`crate::kernel::resolve_built_kernel`] is the one reader — it refuses BY
/// NAME when the donor declares no such kernel, when the kernel is declared but
/// not built, and when the built artifact's pin disagrees with the chosen
/// preset/veto. A donor is a CONSUMER of the built kernel, never a second
/// builder: the kernel is built where it is declared
/// (`dwarves buildthing <repo> --variant <kernel>`), and its version pin stays a
/// product decision of the DONOR repo — the consumer's declaration says only
/// "that one".
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct KernelDonor {
    /// The donor CHECKOUT's name — its directory beside the consuming repo.
    pub repo: String,
    /// The `[[build.kernel]]` name inside that checkout.
    pub kernel: String,
}

impl KernelDonor {
    /// Both halves named, or the reference resolves nothing. Refused HERE, at
    /// declaration time, because an empty half would otherwise surface as a
    /// "no such checkout" error at compose time — true words about the wrong
    /// defect. `who` prefixes the refusal (`installer 'x'` / `iso 'x'`).
    pub fn validate(&self, who: &str) -> anyhow::Result<()> {
        if self.repo.trim().is_empty() {
            anyhow::bail!(
                "{who}: kernel_donor.repo is empty — the donor names WHICH checkout declares \
                 the kernel this medium boots"
            );
        }
        if self.kernel.trim().is_empty() {
            anyhow::bail!(
                "{who}: kernel_donor.kernel is empty — the donor names WHICH [[build.kernel]] \
                 in '{}' this medium boots",
                self.repo
            );
        }
        Ok(())
    }

    /// The edge this row spells: `kernel ↔ repo/kernel : kernel`.
    pub fn edge(&self) -> crate::tree::Edge {
        crate::tree::Edge::new(&self.repo, &self.kernel, crate::variant::ArtifactKind::Kernel)
    }
}

/// **How a firmware can start an installer medium off the device it lands on.**
///
/// An `.iso` is an El Torito medium: the boot information lives in a catalog a
/// firmware finds by reading the volume as optical media. Attach it as a cdrom
/// and every firmware boots it. Hand the identical bytes to a machine as a
/// **disk** — which is what every cloud that imports an image does, and what a
/// USB stick is — and a legacy BIOS reads LBA 0, finds no `55 AA` boot sector,
/// and parks. MEASURED 2026-08-30 on gunnar's 38 680 576 B medium: `LBA
/// 0[510..512] = 00 00`, the whole first sector zero, and `--target
/// ovh-public-cloud` left **zero bytes of serial** for 420 s.
///
/// The medium's own shape is the fact that decides this, so it is declared on
/// the medium, not on the machine: [`crate::target::Target`] says what the
/// MACHINE has, and every field of it is a kernel-config consequence, whereas
/// this one changes 512 bytes of an image and no kernel symbol at all.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum MediumBoot {
    /// **El Torito only** — the medium boots as a cdrom, and its first sector
    /// is all zeros. What every medium did before this word existed.
    #[default]
    ElTorito,
    /// **El Torito AND a hybrid boot sector**: LBA 0 carries real boot code
    /// that chainloads a disk-flavoured copy of the same `korp-boot-bios`
    /// stage, so ONE artifact boots from a cdrom, from a USB stick and from a
    /// cloud that imports it as a disk image. Both copies live in the ISO's
    /// reserved 32 KiB system area
    /// ([`SYSTEM_AREA_BYTES`](crate::iso_writer::SYSTEM_AREA_BYTES)), so the
    /// medium does not grow by a byte.
    ///
    /// Requires a real legacy-BIOS stage — `target.firmware` must be `seabios`
    /// or `both`, else the composer refuses by name: a `55 AA` that chainloads
    /// nothing is worse than none, because firmware stops looking once it finds
    /// one.
    Hybrid,
    /// **El Torito AND a GPT ESP partition, no boot code at LBA 0**: the ESP is
    /// reachable through a GPT in the same reserved system area
    /// ([`GptHybrid::Always`](crate::iso_writer::GptHybrid::Always)), so UEFI
    /// firmware boots the identical artifact off a cdrom and off a disk it was
    /// imported as — the word for a UEFI-only imported-disk cloud (Azure Gen2,
    /// 2026-09-05), where `hybrid` cannot be declared (it needs a legacy stage
    /// the machine has no firmware for) and `el-torito` alone parks on a
    /// zeroed LBA 0. A BIOS finds nothing to run and moves on, which is the
    /// truthful outcome on such a medium.
    Gpt,
}

impl MediumBoot {
    pub fn as_str(self) -> &'static str {
        match self {
            MediumBoot::ElTorito => "el-torito",
            MediumBoot::Hybrid => "hybrid",
            MediumBoot::Gpt => "gpt",
        }
    }
    /// Whether the medium carries a boot sector a BIOS can start off a disk.
    pub fn is_hybrid(self) -> bool {
        matches!(self, MediumBoot::Hybrid)
    }
    /// Whether the medium carries a GPT ESP partition whatever the ESP's size,
    /// so UEFI boots it off a disk.
    pub fn lays_gpt(self) -> bool {
        matches!(self, MediumBoot::Gpt)
    }
    /// An undeclared word is the old behaviour, so the emission stays silent.
    pub fn is_default(&self) -> bool {
        *self == MediumBoot::default()
    }
}

/// One concrete **disk-installer build point**.
///
/// Serializes as a `[[build.installer]]` table (or is constructed by the dwarves
/// forge for its ISO/installer cell):
/// ```toml
/// [[build.installer]]
/// name = "korp-installer"
/// variant = "korp-appliance-lean"
/// installer_variant = "korp-installer-pid1"   # the cargo build producing the PID-1
/// volume_id = "KORP_INSTALL"
/// target_disk_gib = 64
/// gpu = false
/// boot_params = ["console=ttyS0"]
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct InstallerBuild {
    /// Stable handle (`nornir build --installer <name>` / the KVM boot row).
    pub name: String,
    /// The appliance variant this installer lays down (e.g. `korp-appliance-lean`).
    #[serde(default = "default_variant")]
    pub variant: String,
    /// The cargo build variant that produces the `korp-installer` PID-1 binary
    /// riding the ISO as its boot payload — mastered via the ISO's `from_variant`.
    #[serde(default, skip_serializing_if = "String::is_empty")]
    pub installer_variant: String,
    /// The ISO-9660 volume id.
    #[serde(default = "default_volume_id")]
    pub volume_id: String,
    /// Where the app `.znippy` payload comes from.
    #[serde(default)]
    pub payload: PayloadKind,
    /// **Files packed into the app `.znippy` beside the app** (lane HOLGER-MEDIA,
    /// 2026-10-23): entry name inside the archive → the file's path relative to the
    /// repository root (`"sandbox/crun" = ".sandbox-out/crun"`). The forge packs them
    /// into the SAME archive korp-start verifies at every boot (one root pin, one
    /// signature), so the installer, the keep medium and the live app all carry them;
    /// a source that is missing refuses the compose by name. The app reads them out
    /// of its running slot's archive. What a product stages here is its own verb's
    /// (Holger: `cargo xtask scan-worker stage`); a name is one or more plain path
    /// segments and never the app's own entry.
    #[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
    pub payload_entries: std::collections::BTreeMap<String, String>,
    /// The FAT container backend this installer lays down, or `None` for a **LEAN**
    /// install (korp PID-1 only, no containers, zero container 3pp). A FAT variant
    /// selects exactly one [`ContainerBackend`]. This is the per-install selector the
    /// customer picks — the whole matrix is buildable, one backend per instance.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub container_backend: Option<ContainerBackend>,
    /// Size (GiB) of the blank target disk attached in the KVM install proof.
    #[serde(default = "default_disk_gib")]
    pub target_disk_gib: u32,
    /// Attach a virtio-gpu in the KVM test (mirrors an optional HW GPU).
    #[serde(default)]
    pub gpu: bool,
    /// Kernel/boot command-line parameters mastered onto the ISO.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub boot_params: Vec<String>,
    /// **The app's argv tail** — the metal twin of [`ContainerBuild::cmd`]
    /// (`crate::container::ContainerBuild::cmd`), so "how the product starts"
    /// is ONE word across both shapes. Rendered as a `korp.args=` boot token
    /// (comma-joined) by [`effective_boot_params`](Self::effective_boot_params);
    /// the installer carries it to the installed disk's cmdline and korp-start
    /// decodes it into the exec argv after argv[0]. MEASURED need (2026-08-14,
    /// the fleet first-boot proof): without it the metal shape execs the bare
    /// entry name — holger printed its help and PID 1 died — while the
    /// container cell declared its cmd and served. Comma-joined without
    /// escapes, so [`validate`](Self::validate) refuses an element carrying
    /// whitespace, a comma or a quote BY NAME at declaration time (the encode
    /// is total or the cell does not build).
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub cmd: Vec<String>,
    /// **The app's environment** — the metal twin of `ContainerBuild::env`
    /// (the second half of the shape-parity story `cmd` started). Rendered as
    /// `korp.env.<KEY>=<value>` boot tokens: dot-prefixed so the kernel never
    /// claims them for init's own env, `korp.`-prefixed so the installer's
    /// carry delivers them to the INSTALLED cmdline, decoded back into the
    /// exec's envp by korp-start. MEASURED need (2026-08-14): holger's
    /// appliance gate is `HOLGER_APPLIANCE=1` — an ENV var with no channel to
    /// installed metal, so the served appliance ran with its tunnel dark.
    /// Same encoding law as `cmd` (no whitespace/comma/quote, refused BY
    /// NAME), plus: a KEY that smells like a secret is refused outright — the
    /// cmdline is world-readable on the ESP forever.
    #[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
    pub env: std::collections::BTreeMap<String, String>,
    /// **The mark this installer's boot screen wears** — a path, relative to the
    /// declaring repo's root, to the image baked into the first stage's splash.
    ///
    /// The exact twin of [`IsoBuild::boot_logo`](crate::iso::IsoBuild::boot_logo),
    /// on the SAME `KORP_BOOT_LOGO` wire into [`korp_installer::splash`], and it
    /// exists because the installer cell had no way to say it. MEASURED 2026-08-16:
    /// gunnar, korp and holger all shipped the identical nordisk raven because
    /// `[[build.installer]]` carried no such field at all — not a rendering
    /// accident, a hole in the vocabulary.
    ///
    /// Omitted ⇒ nothing is exported and korp's own `assets/korp-logo.webp` is
    /// baked, which is the correct default and stays the default. Formats: the
    /// codec list `korp-installer` compiles `image` with (webp, png, jpeg, tiff,
    /// bmp); the picture is fitted into 520×360 and never upscaled.
    #[serde(rename = "boot-logo", alias = "boot_logo", default, skip_serializing_if = "Option::is_none")]
    pub boot_logo: Option<String>,
    /// **The declared kernel this medium boots** (`[build.installer.kernel_donor]`)
    /// — see [`KernelDonor`]. `None` keeps the pre-donor behaviour: the composer's
    /// environment knob or the host's own kernel, which is the honest spelling of
    /// "this cell has not chosen one".
    ///
    /// Cell-level and `#[serde(default)]`, so published 0.1.x readers (whose
    /// `InstallerBuild` lacks the field) still load the recipe — they ignore the
    /// table benignly. The CONSUMER of the field is the composing forge (dwarves),
    /// which path-deps this crate; a mirror crate on an older published version
    /// must guard the declaration by reading the raw toml (gunnar's
    /// `toml_parity` does exactly that) until its pin advances.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub kernel_donor: Option<KernelDonor>,
    /// **The machine this medium is built FOR** (`[build.installer.target]`):
    /// firmware (`uefi | seabios | both`), consoles (`serial`, `vga`, in
    /// `console=` order), buses, nic — see [`crate::target`]. Default = both
    /// firmwares, `tty0` then `ttyS0`, IDE cdrom + virtio-blk, virtio-net:
    /// exactly what the medium did before the group existed, so an undeclared
    /// row changes nothing and emits nothing. Published 0.1.x readers drop the
    /// table benignly; the CONSUMER is the composer (`korp-installer::isocompose`),
    /// which lays the real BIOS stage only when `firmware.lays_bios()` and the
    /// EFI section only when `firmware.lays_uefi()`.
    #[serde(default, skip_serializing_if = "crate::target::Target::is_default")]
    pub target: crate::target::Target,
    /// **What `/data` is laid with** (`[build.installer.data]`, `fs = "fat" |
    /// "xfs"`) — see [`crate::data`]. Default = FAT: exactly what every
    /// medium did before the group existed, so an undeclared row changes
    /// nothing and emits nothing. Consumers: the kernel edge
    /// ([`crate::requires::DataFacts`]), the installer PID 1 through
    /// [`DATA_FS_BOOT_PARAM`], and the composer's content id.
    #[serde(default, skip_serializing_if = "crate::data::Data::is_default")]
    pub data: crate::data::Data,
    /// **How a firmware can START this medium off the device it lands on**
    /// (`medium_boot = "el-torito" | "hybrid"`) — see [`MediumBoot`].
    #[serde(default, skip_serializing_if = "MediumBoot::is_default")]
    pub medium_boot: MediumBoot,
    /// **Every other consumption edge** (`[build.installer.edges.<feature>]`)
    /// — a root feature bound to `repo/variant : typ`, type-checked at
    /// declaration time. See [`EdgeDecl`](crate::tree::EdgeDecl).
    #[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
    pub edges: std::collections::BTreeMap<String, crate::tree::EdgeDecl>,
    /// **Declared PRESETS** (`[build.installer.presets.<name>]`, name → path →
    /// value): named bundles of choices over this variant's whole settings
    /// tree — `airgap = { wireguard = "off", dhcp = "off", "kernel.preset" =
    /// "nuked" }` — projected as the root `preset` val and chosen with
    /// `--opt preset=<name>`. Every path is proven against the tree at
    /// declaration time ([`SettingsTree::validate_presets`](crate::tree::SettingsTree::validate_presets)).
    #[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
    pub presets: crate::tree::Presets,
    /// The preset this medium was built with — bundle AND expansion — set by
    /// the door that expanded it; serialized into the attested descriptor.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub applied_preset: Option<crate::tree::AppliedPreset>,
    /// **Declared build parameters** (`[[build.installer.params]]`) — the
    /// per-site inputs (hub public key, endpoint, site name, a staged
    /// hub.conf) and where each lands. See [`crate::params`].
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub params: Vec<crate::params::ParamSpec>,
    /// **Declared fixtures** (`[build.installer.fixtures]`, parameter name →
    /// kind): which parameters the forge may fill from a fixture it mints
    /// itself (`hub_conf = "wg-hub"`), supplied as `--param name=@fixture`.
    /// Cell-level rather than a `ParamSpec` field so published 0.1.x readers
    /// (which `deny_unknown_fields` on the param row) still load the recipe;
    /// see [`crate::params`] ("Declared fixtures"). Validated by
    /// [`validate`](Self::validate).
    #[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
    pub fixtures: std::collections::BTreeMap<String, crate::params::FixtureKind>,
    /// **Declared build OPTIONS** (`[[build.installer.options]]`) — the
    /// opt-out axis: a checkbox whose OFF state leaves the code out of the
    /// packed app binary entirely. Each row names the cargo feature it
    /// decides, the parameters that come and go with it, the cmdline selector
    /// it bakes and the symbol families that must be ABSENT when it is off.
    /// See [`crate::options`].
    ///
    /// Cell-level, ignored benignly by published 0.1.x readers — including its
    /// own contents, so an option may use vocabulary those readers do not have
    /// (`param_kinds = { gateway = "ipv4" }`) without making the recipe
    /// unloadable for them. Validated by [`validate`](Self::validate).
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub options: Vec<crate::options::OptionSpec>,
    /// **What this medium needs of its sub build-things regardless of any
    /// option** (`[[build.installer.requires]]`, each row `edge = "kernel",
    /// surface = "kconfig", symbol/value/reason`) — the row-level half of the
    /// closure ([`crate::requires`]); the appliance-boot contract is implied by
    /// `kernel_donor` and need not be repeated here. Benign to published 0.1.x
    /// readers.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub requires: Vec<crate::requires::EdgeRequirement>,
    /// The applied `(name, value)` record — set by
    /// [`apply_cli_params`](InstallerBuild::apply_cli_params), forwarded onto
    /// the rendered ISO, serialized into the attested descriptor.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub applied_params: Vec<crate::params::AppliedParam>,
    /// The resolved OPTION states — which toggles produced this artifact, and
    /// whether the operator said so or the recipe's default stood. Set by
    /// [`apply_cli_options`](Self::apply_cli_options); serialized into the
    /// attested descriptor exactly like `applied_params`.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub applied_options: Vec<crate::options::AppliedOption>,
}

/// **The cmdline key of the firmware fact** — `korp.target.firmware=<uefi|seabios|both>`,
/// the way `target.firmware` reaches PID 1. Baked by
/// [`InstallerBuild::effective_boot_params`] beside the other `korp.*`
/// selectors ONLY when the `target` group is declared (an undeclared medium
/// stays byte-identical). `korp-installer::cmdline::TARGET_FIRMWARE_KEY` is the
/// same spelling on the reading side: the installer PID 1 lays the legacy-BIOS
/// path (MBR boot code + `korpbios.bin`) on the target disk only when the
/// firmware `lays_bios()`, and `carry_site_tokens` forwards the token to the
/// installed disk's own cmdline.
pub const FIRMWARE_BOOT_PARAM: &str = "korp.target.firmware";

/// The token [`FIRMWARE_BOOT_PARAM`] carries for `fw`.
pub fn firmware_boot_token(fw: crate::target::Firmware) -> String {
    format!("{FIRMWARE_BOOT_PARAM}={}", fw.as_str())
}

/// **The cmdline key of the data filesystem** — `korp.data.fs=<fat|xfs>`, the
/// way `data.fs` reaches the installer PID 1. Baked by
/// [`InstallerBuild::effective_boot_params`] beside `korp.target.firmware=`
/// ONLY when the `data` group is declared AND not `fat`: an absent token
/// already means FAT (every pre-group medium), so one fact has one spelling.
/// `korp-installer::cmdline::DATA_FS_KEY` reads it; `install` lays P3 with
/// `korp-mkfs-xfs` for `xfs` and `fatfs` otherwise, and `carry_site_tokens`
/// forwards the token to the installed disk's own cmdline.
pub const DATA_FS_BOOT_PARAM: &str = "korp.data.fs";

/// The token [`DATA_FS_BOOT_PARAM`] carries for `fs`.
pub fn data_fs_boot_token(fs: crate::data::DataFs) -> String {
    format!("{DATA_FS_BOOT_PARAM}={}", fs.as_str())
}

/// **The cmdline key of the store's writer arm** — `korp.data.io=<safe|uring>`,
/// the way `data.io` reaches the APP's PID 1 (it is a site token: the
/// installer carries it onto the installed disk's cmdline, the app reads it
/// off `/proc/cmdline`). Baked by [`InstallerBuild::effective_boot_params`]
/// beside `korp.data.fs=` ONLY when the group is declared AND not `safe`: an
/// absent token already means `safe` (every pre-`io` medium), one fact, one
/// spelling. `korp-installer::cmdline::DATA_IO_KEY` is the same spelling on
/// the reading side.
pub const DATA_IO_BOOT_PARAM: &str = "korp.data.io";

/// The token [`DATA_IO_BOOT_PARAM`] carries for `io`.
pub fn data_io_boot_token(io: crate::data::DataIo) -> String {
    format!("{DATA_IO_BOOT_PARAM}={}", io.as_str())
}

/// **The cmdline key of the reinstall policy** — `korp.reinstall=wipe|keep`,
/// how `--param reinstall=wipe` reaches the installer PID 1. A product's
/// recipe declares it as an OPTIONAL `Text` param with this `boot_param`
/// (gunnar: `reinstall`), the generic param plumbing lands it on the medium's
/// cmdline, and `korp-installer::cmdline::REINSTALL_KEY` is the same spelling
/// on the reading side: absent = keep = a korp-installed target is UPGRADED
/// (its `/data` kept, only the ESP + rootfs rewritten); `wipe` lays it fresh.
/// Never baked by `effective_boot_params` on its own — an undeclared medium
/// keeps `/data` by construction. See
/// korp-installer/.nornir/data-survives-upgrade.md.
pub const REINSTALL_BOOT_PARAM: &str = "korp.reinstall";

/// The one value of [`REINSTALL_BOOT_PARAM`] that wipes.
pub const REINSTALL_WIPE: &str = "wipe";

/// The `[[build.installer.params]]` row a product declares to expose the
/// policy — the ONE spelling, so gunnar's recipe and any other product's
/// carry the same name, label and key.
pub fn reinstall_param_spec() -> crate::params::ParamSpec {
    crate::params::ParamSpec {
        name: "reinstall".to_string(),
        label: "Reinstall policy for a disk that already carries data: keep (upgrade, /data survives) | wipe (lay it fresh)".to_string(),
        kind: crate::params::ParamKind::Text,
        required: false,
        boot_param: REINSTALL_BOOT_PARAM.to_string(),
        staged_file: String::new(),
        lands: crate::params::Landing::Data,
    }
}

fn default_variant() -> String {
    "korp-appliance-lean".to_string()
}
fn default_volume_id() -> String {
    "KORP_INSTALL".to_string()
}
fn default_disk_gib() -> u32 {
    64
}

impl Default for InstallerBuild {
    fn default() -> Self {
        InstallerBuild {
            name: String::new(),
            variant: default_variant(),
            installer_variant: String::new(),
            volume_id: default_volume_id(),
            payload: PayloadKind::default(),
            payload_entries: std::collections::BTreeMap::new(),
            container_backend: None,
            target_disk_gib: default_disk_gib(),
            gpu: false,
            boot_params: vec!["console=ttyS0".to_string()],
            cmd: Vec::new(),
            env: std::collections::BTreeMap::new(),
            boot_logo: None,
            kernel_donor: None,
            target: crate::target::Target::default(),
            data: crate::data::Data::default(),
            medium_boot: MediumBoot::default(),
            edges: std::collections::BTreeMap::new(),
            presets: crate::tree::Presets::new(),
            applied_preset: None,
            params: Vec::new(),
            fixtures: std::collections::BTreeMap::new(),
            options: Vec::new(),
            requires: Vec::new(),
            applied_params: Vec::new(),
            applied_options: Vec::new(),
        }
    }
}

impl InstallerBuild {
    /// This descriptor's artifact kind — an [`ArtifactKind::Iso`] (a bootable ISO),
    /// so its launch axis is a serial-console boot, uniform with the other bootable
    /// kinds. The installer *semantics* (attach a blank disk, gate on INSTALL-OK)
    /// ride the extra seam methods below.
    pub fn artifact_kind(&self) -> ArtifactKind {
        ArtifactKind::Installer
    }

    /// The cargo build variant that must be built first (the PID-1 payload).
    pub fn variant_deps(&self) -> Vec<&str> {
        let mut v = Vec::new();
        if !self.installer_variant.is_empty() {
            v.push(self.installer_variant.as_str());
        }
        v
    }

    /// Materialize the [`IsoBuild`] the pure-Rust masterer produces for ▶ Build —
    /// a UEFI installer ISO staging `iso-root` (which carries the `efiboot.img` ESP
    /// + the embedded app `.znippy`) and chaining the PID-1 via `from_variant`.
    pub fn installer_iso(&self) -> IsoBuild {
        IsoBuild {
            name: self.name.clone(),
            staging: "iso-root".to_string(),
            volume_id: self.volume_id.clone(),
            boot_params: self.effective_boot_params(),
            // The declared firmware decides the El-Torito shape: `uefi`/`both`
            // → the UEFI format (the BIOS default entry is laid BESIDE it by the
            // composer, or is the refusal stub); `seabios` → BIOS only.
            format: self.target.effective().firmware.iso_format(),
            from_variant: (!self.installer_variant.is_empty()).then(|| self.installer_variant.clone()),
            // The mark travels with the rendered ISO — without this line the field
            // would be declarable and silently dropped at `..Default::default()`,
            // which is worse than not having it.
            boot_logo: self.boot_logo.clone(),
            // The parameter DECLARATION and any applied values travel with the
            // rendered ISO: the mastering step lands staged-file params on the
            // staging tree, and the attested descriptor carries the record.
            params: self.params.clone(),
            fixtures: self.fixtures.clone(),
            // The OPTION declaration and the resolved states travel with the
            // rendered ISO for the same reason the parameters do: without
            // these two lines the field would be declarable and silently
            // dropped at `..Default::default()`, which is worse than not
            // having it (the test below watches exactly that).
            options: self.options.clone(),
            applied_options: self.applied_options.clone(),
            applied_params: self.applied_params.clone(),
            ..Default::default()
        }
    }

    /// **Apply CLI-supplied parameter values** — the installer-cell twin of
    /// [`IsoBuild::apply_cli_params`]: landed `boot_params` tokens append to
    /// this cell (and ride [`installer_iso`](InstallerBuild::installer_iso)
    /// via `effective_boot_params`), the record is set, staged-file contents
    /// ride the rendered ISO's `staged_param_files`. A `@fixture` value is
    /// refused here (no source can mint one) — see
    /// [`apply_cli_params_with`](Self::apply_cli_params_with).
    pub fn apply_cli_params(
        &mut self,
        supplied: &std::collections::BTreeMap<String, String>,
    ) -> Result<()> {
        self.apply_cli_params_with(supplied, &crate::params::NoFixtures)
    }

    /// [`apply_cli_params`](Self::apply_cli_params) with the source that mints
    /// this cell's DECLARED [`fixtures`](Self::fixtures) for a `@fixture`
    /// value — the composition's (`InstallerComposer::fixtures`).
    pub fn apply_cli_params_with(
        &mut self,
        supplied: &std::collections::BTreeMap<String, String>,
        source: &dyn crate::params::FixtureSource,
    ) -> Result<()> {
        self.apply_cli_selection_with(&std::collections::BTreeMap::new(), supplied, source)
    }

    /// **Apply the whole operator selection — the OPTION toggles and the
    /// parameter values — in the one order that can be correct.**
    ///
    /// 1. resolve the toggles (an unknown `--opt` name is refused here);
    /// 2. refuse, BY NAME, any parameter the resolved state forbids — with
    ///    WireGuard off a supplied `hub_conf` stops the build instead of
    ///    riding a medium whose binary could never read it;
    /// 3. apply the values against the EFFECTIVE spec list (the declared
    ///    roster minus what the toggles refuse, with the option's sharper
    ///    `param_kinds` shape applied), so a required parameter of an inactive
    ///    branch is not demanded and an active one still is;
    /// 4. bake each option's cmdline selector from the values that actually
    ///    landed (`korp.env.GUNNAR_NET=eth0:static:…`);
    /// 5. record both, so the attested descriptor and the content id carry the
    ///    toggles as well as the values.
    ///
    /// Steps 2 and 3 are why this is one method and not two calls a door could
    /// make in the wrong order: applying the values first would refuse a
    /// missing `static_ip` that DHCP-on makes meaningless.
    /// **Apply the DECLARED-GROUP choices of an `--opt` line** — the val
    /// bucket of [`SettingsTree::split_opts`](crate::tree::SettingsTree::split_opts),
    /// by canonical path.
    ///
    /// A group is a set of vals at reserved paths (`data.fs`, `data.io`), and
    /// this is the ONE writer that moves one onto the row — both doors call it
    /// (edda's `suite_cli::run_installer_selected` and the forge's
    /// `assemble_cell::resolve_cell_options`), so `--opt data.fs=fat` cannot
    /// mean one thing to the CLI and another to the pane.
    ///
    /// Lane N (2026-08-30) had to flip `fs = "fat"` in a scratch worktree to
    /// build a FAT medium — a whole lane's detour for a value the recipe
    /// already named — because a declared group was on no door at all.
    ///
    /// A path this does not know is REFUSED, never dropped: a val the tree
    /// offers and no door applies is a flag that changes nothing, which is the
    /// exact shape of lie this crate keeps closing.
    pub fn apply_group_choices(&mut self, choices: &std::collections::BTreeMap<String, String>) -> Result<()> {
        for (path, value) in choices {
            match path.as_str() {
                crate::tree::DATA_FS_PATH => {
                    self.data.fs = crate::data::DataFs::parse(value).ok_or_else(|| {
                        anyhow!("installer '{}': --opt {path}={value} is not a declared filesystem", self.name)
                    })?;
                }
                crate::tree::DATA_IO_PATH => {
                    self.data.io = crate::data::DataIo::parse(value).ok_or_else(|| {
                        anyhow!("installer '{}': --opt {path}={value} is not a declared writer arm", self.name)
                    })?;
                }
                other => {
                    return Err(anyhow!(
                        "installer '{}': --opt {other}={value} is a value the tree offers and no door applies — \
                         wire it where the group it belongs to is read, or stop offering it",
                        self.name
                    ));
                }
            }
        }
        Ok(())
    }

    pub fn apply_cli_selection_with(
        &mut self,
        options: &std::collections::BTreeMap<String, bool>,
        supplied: &std::collections::BTreeMap<String, String>,
        source: &dyn crate::params::FixtureSource,
    ) -> Result<()> {
        let name = self.name.clone();
        let resolved = crate::options::resolve_options(&self.options, &self.params, options)
            .map_err(|e| anyhow!("installer '{name}': {e:#}"))?;
        resolved.refuse_forbidden(supplied).map_err(|e| anyhow!("installer '{name}': {e:#}"))?;
        let specs = resolved.effective_specs(&self.options, &self.params);
        let fixtures = resolved.effective_fixtures(&self.fixtures);
        let landed = crate::params::apply_params_with(&specs, supplied, &fixtures, source)
            .map_err(|e| anyhow!("installer '{name}': {e:#}"))?;
        let baked = resolved
            .render_bakes(&self.options, &landed.record)
            .map_err(|e| anyhow!("installer '{name}': {e:#}"))?;
        self.boot_params.extend(landed.boot_params);
        // The baked selector wins over a hand-declared token with the same key:
        // a recipe that still spells `korp.env.GUNNAR_NET=eth0:dhcp` in its
        // `boot_params` while ALSO declaring the dhcp option would otherwise
        // ship both, and korp-start's `env_pairs` takes the first — the
        // operator's choice would lose to the leftover, silently.
        for token in baked {
            let key = token.split_once('=').map(|(k, _)| k.to_string()).unwrap_or_default();
            self.boot_params.retain(|p| !p.starts_with(&format!("{key}=")));
            self.boot_params.push(token);
        }
        self.applied_params = landed.record;
        self.applied_options = resolved.record;
        Ok(())
    }

    /// Resolve this cell's declared options against `supplied` WITHOUT
    /// applying anything — what a door needs to compute the app build's
    /// feature set (and what the audit must find absent) before it builds.
    pub fn resolved_options(
        &self,
        supplied: &std::collections::BTreeMap<String, bool>,
    ) -> Result<crate::options::ResolvedOptions> {
        crate::options::resolve_options(&self.options, &self.params, supplied)
            .map_err(|e| anyhow!("installer '{}': {e:#}", self.name))
    }

    /// The fixture the recipe declares for parameter `name`, if any — what a
    /// panel reads to offer the affordance only where it is declared.
    pub fn fixture_for(&self, name: &str) -> Option<crate::params::FixtureKind> {
        self.fixtures.get(name).copied()
    }

    /// The exact serial marker the KVM install proof gates on — the installer's
    /// terminal success line. Kept in lockstep with
    /// `korp_installer::serial::INSTALL_OK_MARKER`.
    pub fn success_marker(&self) -> &'static str {
        "INSTALLER: INSTALL-OK"
    }

    /// The absolute boot-logo path this cell declares, resolved against `repo_root`.
    /// `None` when the cell declares no mark. Delegates to
    /// [`IsoBuild::boot_logo_path`](crate::iso::IsoBuild::boot_logo_path) through the
    /// rendered ISO, so a relative `boot-logo` resolves exactly one way whether it was
    /// declared on the variant, on the ISO cell or here.
    pub fn boot_logo_path(&self, repo_root: &Path) -> Option<PathBuf> {
        self.installer_iso().boot_logo_path(repo_root)
    }

    /// The blank target-disk size in bytes.
    pub fn target_disk_bytes(&self) -> u64 {
        self.target_disk_gib as u64 * 1024 * 1024 * 1024
    }

    /// Whether this is a **FAT** install (lays down a container backend) vs **LEAN**
    /// (korp PID-1 only, no containers).
    pub fn is_fat(&self) -> bool {
        self.container_backend.is_some()
    }

    /// The honest appliance label for a surface / serial line / content id:
    /// `lean` (no backend) or `fat-<backend>` (e.g. `fat-youki`, `fat-microvm`).
    pub fn appliance_label(&self) -> String {
        match self.container_backend {
            None => "lean".to_string(),
            Some(b) => format!("fat-{}", b.label()),
        }
    }

    /// The kernel boot params this install bakes onto the ISO — the declared
    /// [`boot_params`](Self::boot_params) PLUS the honest appliance/backend selector the
    /// installed korp reads at boot: `korp.appliance=lean` or `korp.appliance=fat
    /// korp.container_backend=<backend>`. The selector is idempotent — never duplicated
    /// if the declared params already carry a `korp.appliance=` token.
    /// Refuse a `payload_entries` table that cannot mean what it says: an entry
    /// name with an empty, `.` or `..` segment, an absolute source, or the app's
    /// own entry (`korp.entry=`) named twice.
    pub fn check_payload_entries(&self) -> Result<(), String> {
        let app_entry = self.effective_boot_params().iter().find_map(|t| t.strip_prefix("korp.entry=").map(str::to_string));
        for (name, src) in &self.payload_entries {
            let ok = !name.is_empty()
                && !name.starts_with('/')
                && name.split('/').all(|c| !c.is_empty() && c != "." && c != "..");
            if !ok {
                return Err(format!("installer '{}': payload entry {name:?} is not a relative path of plain segments", self.name));
            }
            if app_entry.as_deref() == Some(name.as_str()) {
                return Err(format!("installer '{}': payload entry {name:?} is the app's own entry (korp.entry=)", self.name));
            }
            if src.is_empty() || src.starts_with('/') || src.split('/').any(|c| c == "..") {
                return Err(format!("installer '{}': payload entry {name:?} names {src:?}, not a path under the repository", self.name));
            }
        }
        Ok(())
    }

    pub fn effective_boot_params(&self) -> Vec<String> {
        let mut params = self.boot_params.clone();
        // `target.consoles`, when DECLARED, owns the `console=` tokens: the
        // declared list in its order (the last one is `/dev/console`), any
        // `console=` the cell spelled by hand removed first. Undeclared
        // (`Target::is_default()`) leaves the cell's own tokens untouched —
        // that is what every medium built before the group existed does, and
        // it must keep doing it byte for byte. A cell that names no `vga`
        // gets no `tty0`; one that names no `serial` gets no `ttyS0` — the
        // UpCloud fact (no UART) becomes a cmdline that does not point
        // `/dev/console` at a port that is not there.
        if !self.target.is_default() {
            params.retain(|p| !p.starts_with("console="));
            let mut with_consoles = self.target.effective().console_tokens();
            with_consoles.append(&mut params);
            params = with_consoles;
            // The firmware fact reaches PID 1 the way every other korp.* fact
            // does — as a token the installer reads back off /proc/cmdline —
            // so `korp-installer::install` can lay the legacy-BIOS path on the
            // installed disk only for a medium whose target boots BIOS. An
            // ABSENT token already means `both` (the pre-group medium), so a
            // declared `both` bakes nothing: one fact, one spelling.
            let fw = self.target.effective().firmware;
            if fw != crate::target::Firmware::Both
                && !params.iter().any(|p| p.starts_with(&format!("{FIRMWARE_BOOT_PARAM}=")))
            {
                params.push(firmware_boot_token(fw));
            }
        }
        // The data filesystem, the same way: declared and not the default,
        // one token, the cell's own spelling wins.
        if !self.data.is_default()
            && self.data.fs != crate::data::DataFs::Fat
            && !params.iter().any(|p| p.starts_with(&format!("{DATA_FS_BOOT_PARAM}=")))
        {
            params.push(data_fs_boot_token(self.data.fs));
        }
        // The writer arm, the same way: declared and not `safe`, one token.
        if !self.data.is_default()
            && self.data.io != crate::data::DataIo::Safe
            && !params.iter().any(|p| p.starts_with(&format!("{DATA_IO_BOOT_PARAM}=")))
        {
            params.push(data_io_boot_token(self.data.io));
        }
        // korp.appliance: only when the cell did not spell it itself.
        if !params.iter().any(|p| p.starts_with("korp.appliance=")) {
            match self.container_backend {
                None => params.push("korp.appliance=lean".to_string()),
                Some(b) => {
                    params.push("korp.appliance=fat".to_string());
                    params.push(format!("korp.container_backend={}", b.label()));
                }
            }
        }
        // The declared app argv tail rides as ONE comma-joined token — same
        // idempotence rule (a cell that spelled korp.args= itself keeps its
        // own). NOTE this must NOT hide behind the korp.appliance early-out:
        // the first rendering did, and the parity guard (holger build-space
        // R8) went red because a cell with its own korp.appliance= lost its
        // declared cmd entirely.
        if !self.cmd.is_empty() && !params.iter().any(|p| p.starts_with("korp.args=")) {
            params.push(format!("korp.args={}", self.cmd.join(",")));
        }
        // The declared env, one token per var (BTreeMap = deterministic
        // order), same idempotence rule per key.
        for (k, v) in &self.env {
            if !params.iter().any(|p| p.starts_with(&format!("korp.env.{k}="))) {
                params.push(format!("korp.env.{k}={v}"));
            }
        }
        params
    }

    /// The **extra QEMU args** that attach the blank writable target disk (and,
    /// when [`gpu`](Self::gpu) is set, a virtio-gpu) alongside the installer ISO —
    /// the seam a `BootSpec`'s `extra_qemu_args` carries, since tunnr's one-payload
    /// slot holds the ISO. `disk_path` is the blank raw/qcow2 image the harness
    /// created.
    /// **The container this row's cloud actually imports**, or `None` when the
    /// row does not say which machine it is for.
    ///
    /// A medium and its wrapper are two different facts. `gunnar` v1.3.0
    /// shipped three `.iso` files for three clouds of which exactly one has a
    /// door for an ISO — the bytes were right in all three and the wrapper was
    /// wrong in two, and nothing in the declaration could have caught it,
    /// because nothing in the declaration said which container the machine
    /// takes.
    ///
    /// Derived from [`Target::preset`], and ONLY from it, because the shape
    /// alone cannot answer this. FOUR roster rows present the identical device
    /// set — `upcloud`, `hetzner-cloud`, `ovh-public-cloud` and
    /// `scaleway-instance` are all virtio-blk + virtio-net — and they split two
    /// and two on the container: the first pair takes an attached ISO, the
    /// second an imported qcow2. A reverse lookup from `buses`/`nic` would have
    /// to pick one of four, and picking would be a guess wearing a
    /// derivation's clothes.
    ///
    /// So a row that wants its cloud's container names its machine. A row that
    /// hand-spells the same fields gets `None` — not because the fields are
    /// wrong, but because a shape is not an identity.
    #[cfg(not(target_arch = "wasm32"))]
    pub fn cloud_medium(&self) -> Option<crate::image_writer::CloudMedium> {
        let name = self.target.preset.as_deref()?;
        let p = crate::target::presets::preset(name)?;
        crate::image_writer::CloudMedium::for_preset(p)
    }

    /// **Why this row cannot yield an import container**, when it cannot.
    ///
    /// `None` means it can (or that it is an ISO-attach machine and does not
    /// need one). A `Some` is the sentence a composer prints instead of
    /// silently producing only the `.iso` — which is exactly what happened for
    /// AWS and Azure in v1.3.0 and was found by a human reading a roster
    /// afterwards.
    #[cfg(not(target_arch = "wasm32"))]
    pub fn cloud_medium_gap(&self) -> Option<String> {
        if self.cloud_medium().is_some() {
            return None;
        }
        match self.target.preset.as_deref() {
            None if self.medium_boot.is_default() => None,
            None => Some(format!(
                "installer `{}` lays a medium that boots off an imported disk (`medium_boot = \"{}\"`) \
                 but names no machine (`target.preset`), so no cloud container can be derived for it. \
                 The shape alone cannot decide: upcloud, hetzner-cloud, ovh-public-cloud and \
                 scaleway-instance share one device set and import two different containers. \
                 Name the preset.",
                self.name,
                self.medium_boot.as_str()
            )),
            // **A named ISO-attach machine owes no import container**, and that
            // is not an exemption — it is the answer. Its cloud takes the
            // `.iso` this compose already wrote (`crate::iso_writer`), so there
            // is no missing artifact to report; `image_writer` has no writer for
            // it because there is nothing left to write.
            //
            // Until 2026-09-07 this fell through to the last arm and answered
            // "no writer in `image_writer` produces that container" for
            // `upcloud` — contradicting this function's OWN doc comment ("or
            // that it is an ISO-attach machine and does not need one") and
            // `korp-installer`'s composer ("a cell that names no machine, or
            // names an ISO-attach one, gets exactly what it got before"). So
            // naming the machine did not silence the sentence the composer
            // printed; it swapped one gap for another that was never a gap.
            //
            // Keyed on `MediumBring::is_iso_attach` and NOTHING broader: a
            // machine whose platform imports a disk still owes its container.
            // `scaleway-instance` is `ImportQcow2` — one writer exists, so it
            // never reaches here — and `scaleway-elastic-metal` is `Metal`,
            // which no writer can serve and which is still refused BY NAME
            // below. That is the v1.3.0 defect (three `.iso` files mailed to
            // three clouds, two of which have no door for one) still caught.
            Some(n) if crate::target::presets::preset(n).is_some_and(|p| p.medium.is_iso_attach()) => None,
            Some(n) if crate::target::presets::preset(n).is_none() => Some(format!(
                "installer `{}` names machine `{n}`, which is not in the roster: {}",
                self.name,
                crate::target::presets::names().join(", ")
            )),
            Some(n) => Some(format!(
                "installer `{}` names machine `{n}`, whose medium arrives as {} — no writer in \
                 `image_writer` produces that container",
                self.name,
                crate::target::presets::preset(n)
                    .map(|p| p.medium.describe())
                    .unwrap_or_default()
            )),
        }
    }

    /// The format word comes from [`CloudMedium`](crate::image_writer::CloudMedium),
    /// which is the one place that knows what each artifact IS. This used to
    /// sniff it from the file extension —
    /// `if disk_path.ends_with(".qcow2") { "qcow2" } else { "raw" }` — and that
    /// was a silent wrong answer for every medium the extension does not name.
    /// A `.vhd` was attached `format=raw`, so the firmware read the `conectix`
    /// footer where a partition table should be and found nothing bootable.
    /// Harmless while raw and qcow2 were the only two disks this crate could
    /// produce; a defect the moment it could write a VHD (2026-09-05).
    ///
    /// An unrecognised extension still means `raw`, which is the honest default
    /// for a blank disk a harness just created — that is what this seam is
    /// usually handed, and a blank disk has no format to detect.
    #[cfg(not(target_arch = "wasm32"))]
    pub fn kvm_disk_args(&self, disk_path: &str) -> Vec<String> {
        let medium = crate::image_writer::CloudMedium::for_extension(disk_path);
        let fmt = medium.map_or("raw", |m| m.qemu_format());
        let mut a = vec![
            "-drive".to_string(),
            format!("file={disk_path},if=virtio,format={fmt}"),
        ];
        a.extend(self.kvm_gpu_args());
        a
    }

    /// The **GPU half** of [`kvm_disk_args`](Self::kvm_disk_args) alone — for a
    /// caller whose boot seam already attaches the target disk as the spec's own
    /// image (draupnir's `kvm_install_from_medium`), so a second `-drive` would be a
    /// duplicate. Empty when [`gpu`](Self::gpu) is off.
    pub fn kvm_gpu_args(&self) -> Vec<String> {
        if !self.gpu {
            return Vec::new();
        }
        // `-vga none` kills QEMU's implicit default std-VGA adapter so the virtio-vga
        // below is the SOLE / primary display — the one OVMF's GOP draws to AND the
        // one QMP `screendump` (no device arg) captures. Without it QEMU keeps the
        // default VGA as primary and the screendump comes back blank while the guest
        // actually renders on the secondary virtio adapter.
        vec!["-vga".to_string(), "none".to_string(), "-device".to_string(), "virtio-vga".to_string()]
    }

    /// Validate: a name, a volume id, a plausible target-disk size, and a payload.
    pub fn validate(&self) -> Result<()> {
        if self.name.trim().is_empty() {
            return Err(anyhow!("installer build: empty name"));
        }
        if self.volume_id.trim().is_empty() {
            return Err(anyhow!("installer '{}': empty volume_id", self.name));
        }
        if self.target_disk_gib == 0 {
            return Err(anyhow!("installer '{}': target_disk_gib must be > 0", self.name));
        }
        if let PayloadKind::S3 { bucket, key, .. } = &self.payload {
            if bucket.trim().is_empty() || key.trim().is_empty() {
                return Err(anyhow!("installer '{}': S3 payload needs a bucket + key", self.name));
            }
        }
        crate::params::validate_specs(&self.params)
            .map_err(|e| anyhow!("installer '{}': {e:#}", self.name))?;
        crate::params::validate_fixtures(&self.params, &self.fixtures)
            .map_err(|e| anyhow!("installer '{}': {e:#}", self.name))?;
        crate::options::validate_options(&self.options, &self.params)
            .map_err(|e| anyhow!("installer '{}': {e:#}", self.name))?;
        for r in &self.requires {
            r.what.validate(&format!("installer '{}' (edge '{}')", self.name, r.edge))?;
        }
        // The argv tail must survive the comma-joined boot token AND the
        // kernel's whitespace split — refused BY NAME here so the encode is
        // total: an element with a space would silently become a second
        // kernel token, a comma a second argument, a quote gets eaten by the
        // cmdline parser's trim_matches('"'), and an empty element vanishes.
        for (i, arg) in self.cmd.iter().enumerate() {
            if arg.is_empty() {
                return Err(anyhow!("installer '{}': cmd[{i}] is empty — an empty argv element cannot ride the korp.args token", self.name));
            }
            if let Some(bad) = arg.chars().find(|c| c.is_whitespace() || *c == ',' || *c == '"') {
                return Err(anyhow!(
                    "installer '{}': cmd[{i}] ({arg:?}) contains {bad:?} — korp.args is comma-joined                      on a whitespace-split kernel cmdline with no escapes; spell the argument without it",
                    self.name
                ));
            }
        }
        // The env map: the same total-encoding law as cmd, per key AND value —
        // and a key that smells like a secret is refused outright (the token
        // lands on the ESP's cmdline, world-readable forever).
        for (k, v) in &self.env {
            if k.is_empty() {
                return Err(anyhow!("installer '{}': env has an empty key", self.name));
            }
            for (what, text) in [("key", k.as_str()), ("value", v.as_str())] {
                if let Some(bad) = text.chars().find(|c| c.is_whitespace() || *c == ',' || *c == '"' || *c == '=') {
                    return Err(anyhow!(
                        "installer '{}': env {what} {text:?} contains {bad:?} — korp.env.* tokens \
                         ride a whitespace-split kernel cmdline with no escapes",
                        self.name
                    ));
                }
            }
            let upper = k.to_uppercase();
            for frag in ["SECRET", "TOKEN", "PASSWORD", "PRIVATE", "APIKEY", "API_KEY", "CREDENTIAL"] {
                if upper.contains(frag) {
                    return Err(anyhow!(
                        "installer '{}': env key {k:?} smells like a secret ({frag}) — the cmdline \
                         is world-readable on the ESP forever; secrets never ride korp.env.*",
                        self.name
                    ));
                }
            }
        }
        // The kernel donor: both halves named, or the reference resolves nothing.
        // Refused HERE, at declaration time, because an empty half would otherwise
        // surface as a "no such checkout" error at compose time — true words about
        // the wrong defect.
        let who = format!("installer '{}'", self.name);
        if let Some(d) = &self.kernel_donor {
            d.validate(&who)?;
        }
        self.target.validate(&who)?;
        crate::tree::validate_edge_rows(&who, self.kernel_donor.as_ref(), &self.edges, &self.options)?;
        // Every preset bundle names paths this variant's tree HAS, with values
        // the nodes take — proven here, against the tree of the default pick.
        crate::tree::SettingsTree::of_installer(self, None).validate_presets()?;
        // The mastered ISO must itself validate.
        self.installer_iso().validate()
    }

    /// The canonical bytes the content id is taken over.
    pub fn canonical_bytes(&self) -> Vec<u8> {
        let mut s = String::new();
        s.push_str(&self.installer_iso().canonical_bytes().iter().map(|b| *b as char).collect::<String>());
        s.push_str(&format!("#variant:{}", self.variant));
        s.push_str(&format!("#payload:{}", self.payload.label()));
        s.push_str(&format!("#backend:{}", self.appliance_label()));
        s.push_str(&format!("#disk:{}", self.target_disk_gib));
        s.push_str(&format!("#gpu:{}", self.gpu));
        // The machine shape, only when declared — two media built for two
        // firmwares are two artifacts; an undeclared row keeps every old id.
        s.push_str(&self.target.canonical_fragment());
        // The data filesystem, only when declared — an xfs /data and a FAT
        // /data are two media; an undeclared row keeps every old id.
        s.push_str(&self.data.canonical_fragment());
        // Only when declared, so every pre-cmd cell keeps its content id —
        // and two ISOs that start the product differently can never share one.
        if !self.cmd.is_empty() {
            s.push_str(&format!("#cmd:{}", self.cmd.join(",")));
        }
        if !self.env.is_empty() {
            for (k, v) in &self.env {
                s.push_str(&format!("#env:{k}={v}"));
            }
        }
        // Two media that boot DIFFERENT kernels are different artifacts, and the
        // donor is what decides which kernel this one boots. Only written when
        // declared, so every donor-less cell keeps the id it already had.
        if let Some(d) = &self.kernel_donor {
            s.push_str(&format!("#kernel-donor:{}/{}", d.repo, d.kernel));
        }
        for (f, e) in &self.edges {
            s.push_str(&format!("#edge:{f}={}:{}", e.coordinate(), e.typ.as_str()));
        }
        // The bundle a medium was built from is part of what it IS — recorded
        // beside the expanded choices below, which carry the actual decisions.
        if let Some(p) = &self.applied_preset {
            s.push_str(&format!("#preset:{}", p.bundle));
        }
        // Two media that differ only by a toggle carry DIFFERENT code, so they
        // may never share a content id. Only written when options resolved, so
        // every pre-options cell keeps the id it already had.
        for o in &self.applied_options {
            s.push_str(&format!("#opt:{}={}", o.name, if o.on { "on" } else { "off" }));
        }
        s.into_bytes()
    }

    /// A content-addressed id (lowercase-hex SHA-256 over [`canonical_bytes`]).
    pub fn content_id(&self) -> String {
        nornir_hash::sha256_hex(&self.canonical_bytes())
    }
}

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

    /// `payload_entries` (lane HOLGER-MEDIA): read from TOML, absent when empty,
    /// and refused by name when a name or a source cannot be what it says.
    #[test]
    fn payload_entries_read_emit_and_are_checked() {
        let text = "name = \"x\"\nboot_params = [\"korp.entry=holger\"]\n[payload_entries]\n\"sandbox/crun\" = \".sandbox-out/crun\"\n\"sandbox/manifest.toml\" = \".sandbox-out/manifest.toml\"\n";
        let i: InstallerBuild = toml::from_str(text).unwrap();
        assert_eq!(i.payload_entries.len(), 2);
        assert_eq!(i.payload_entries["sandbox/crun"], ".sandbox-out/crun");
        i.check_payload_entries().unwrap();
        let back = toml::to_string(&i).unwrap();
        assert!(back.contains("[payload_entries]"), "{back}");
        let none: InstallerBuild = toml::from_str("name = \"x\"\n").unwrap();
        assert!(none.payload_entries.is_empty());
        assert!(!toml::to_string(&none).unwrap().contains("payload_entries"), "absent when empty");
        for (name, src, why) in [
            ("../up", ".sandbox-out/x", "plain segments"),
            ("/abs", ".sandbox-out/x", "plain segments"),
            ("sandbox//x", ".sandbox-out/x", "plain segments"),
            ("holger", ".sandbox-out/x", "the app's own entry"),
            ("sandbox/x", "/etc/passwd", "under the repository"),
            ("sandbox/x", "../../x", "under the repository"),
        ] {
            let mut bad = i.clone();
            bad.payload_entries.clear();
            bad.payload_entries.insert(name.into(), src.into());
            let e = bad.check_payload_entries().unwrap_err();
            assert!(e.contains(why), "{name} = {src}: {e}");
        }
    }

    /// **`target.firmware` reaches the rendered ISO, and only a declared row
    /// is emitted.** Default = today's medium: the UEFI format (the BIOS stage
    /// is laid beside it by the composer), no `target` table in the toml, the
    /// content id unchanged. `seabios` → the BIOS format; `uefi` keeps the UEFI
    /// format and the composer lays no stage. Two media built for two firmwares
    /// are two artifacts.
    #[test]
    fn target_firmware_decides_the_iso_format_and_is_emitted_only_when_declared() {
        use crate::target::Firmware;
        let plain = holgerish();
        assert_eq!(plain.installer_iso().format, IsoFormat::Uefi);
        assert!(plain.target.firmware.lays_bios() && plain.target.firmware.lays_uefi());
        let emitted = toml::to_string(&plain).unwrap();
        assert!(!emitted.contains("[target]"), "an undeclared group emits no row:\n{emitted}");

        let mut bios = holgerish();
        bios.target.firmware = Firmware::SeaBios;
        assert_eq!(bios.installer_iso().format, IsoFormat::Bios);
        let mut uefi = holgerish();
        uefi.target.firmware = Firmware::Uefi;
        assert_eq!(uefi.installer_iso().format, IsoFormat::Uefi);
        assert!(!uefi.target.firmware.lays_bios());
        assert_ne!(plain.content_id(), bios.content_id());
        assert_ne!(plain.content_id(), uefi.content_id());
        assert_ne!(bios.content_id(), uefi.content_id());

        // The toml spelling a repo's declaration carries, read back through the
        // same struct — and the row is emitted when it was chosen.
        let read: InstallerBuild = toml::from_str(
            "name = \"x\"\nvolume_id = \"X\"\n[target]\nfirmware = \"uefi\"\nconsoles = [\"vga\"]\n",
        )
        .unwrap();
        assert_eq!(read.target.firmware, Firmware::Uefi);
        assert_eq!(read.target.console_tokens(), vec!["console=tty0"]);
        let emitted = toml::to_string(&read).unwrap();
        assert!(emitted.contains("[target]") && emitted.contains("firmware = \"uefi\""), "{emitted}");
        read.validate().unwrap();

        // An unseeable boot is refused by the installer's own validate.
        let mut blind = holgerish();
        blind.target.consoles.clear();
        let err = blind.validate().unwrap_err().to_string();
        assert!(err.contains("nobody can see"), "{err}");
    }

    /// **`target.consoles` owns the `console=` order once declared, and is
    /// inert while undeclared.** The UpCloud fact — no UART, VGA only — must
    /// become a cmdline whose `/dev/console` is `tty0`, not a `ttyS0` that is
    /// not there; and every medium that never declared the group keeps its
    /// hand-spelled tokens byte for byte.
    #[test]
    fn declared_consoles_own_the_console_order_and_an_undeclared_group_is_inert() {
        use crate::target::Console;
        let mut cell = holgerish();
        cell.boot_params = vec!["console=ttyS0".into(), "panic=10".into()];
        // Undeclared: exactly the cell's own tokens (plus the appliance selector).
        assert_eq!(
            cell.effective_boot_params(),
            vec!["console=ttyS0", "panic=10", "korp.appliance=lean"]
        );
        // Declared VGA only (UpCloud): the hand-spelled ttyS0 goes, tty0 leads.
        cell.target.consoles = vec![Console::Vga];
        assert_eq!(
            cell.effective_boot_params(),
            vec!["console=tty0", "panic=10", "korp.appliance=lean"]
        );
        // Declared serial-then-vga: the order is the declared order, tty0 last
        // = /dev/console on the graphics console.
        cell.target.consoles = vec![Console::Serial, Console::Vga];
        assert_eq!(
            cell.effective_boot_params()[..2],
            ["console=ttyS0,115200", "console=tty0"]
        );
        // The rendered ISO and the content id follow.
        assert_eq!(cell.installer_iso().boot_params[..2], ["console=ttyS0,115200", "console=tty0"]);
        assert_ne!(cell.content_id(), holgerish().content_id());
    }

    /// **`data.fs` reaches PID 1 as `korp.data.fs=xfs`, and only when
    /// declared and not FAT** — an undeclared row and a declared `fat` both
    /// bake nothing (one fact, one spelling: absent = FAT), the cell's own
    /// token wins, and the content id moves with the declaration. Seen red:
    /// the same cell with the group cleared carries no `korp.data.fs=`.
    #[test]
    fn data_fs_is_baked_as_a_korp_token_only_when_declared_and_not_fat() {
        use crate::data::{Data, DataFs, DataIo};
        let mut cell = InstallerBuild { name: "x".into(), ..Default::default() };
        let plain_id = cell.content_id();
        assert!(!cell.effective_boot_params().iter().any(|p| p.starts_with("korp.data.fs=")));
        cell.data = Data { fs: DataFs::Fat, io: DataIo::Safe };
        assert!(!cell.effective_boot_params().iter().any(|p| p.starts_with("korp.data.fs=")), "a declared fat bakes nothing");
        cell.data = Data { fs: DataFs::Xfs, io: DataIo::Safe };
        let params = cell.effective_boot_params();
        assert!(params.contains(&"korp.data.fs=xfs".to_string()), "{params:?}");
        assert_eq!(data_fs_boot_token(DataFs::Xfs), "korp.data.fs=xfs");
        assert_ne!(cell.content_id(), plain_id, "an xfs /data is another medium");
        // The declared row is emitted, a read-back row is the same struct.
        let read: InstallerBuild = toml::from_str("name = \"x\"\nvolume_id = \"X\"\n[data]\nfs = \"xfs\"\n").unwrap();
        assert_eq!(read.data.fs, DataFs::Xfs);
        let emitted = toml::to_string(&read).unwrap();
        assert!(emitted.contains("[data]") && emitted.contains("fs = \"xfs\""), "{emitted}");
        let plain: InstallerBuild = toml::from_str("name = \"x\"\nvolume_id = \"X\"\n").unwrap();
        assert!(!toml::to_string(&plain).unwrap().contains("[data]"));
        // The cell's own spelling wins over the bake.
        let mut spelled = cell.clone();
        spelled.boot_params = vec!["korp.data.fs=fat".into()];
        let p = spelled.effective_boot_params();
        assert_eq!(p.iter().filter(|t| t.starts_with("korp.data.fs=")).count(), 1);
        assert!(p.contains(&"korp.data.fs=fat".to_string()));
    }

    /// **`target.firmware` reaches PID 1 as `korp.target.firmware=`, and only
    /// when the group is declared.** The installer PID 1 lays the legacy-BIOS
    /// path on the installed disk off this token (`korp-installer::install`);
    /// an undeclared medium bakes no token, so its cmdline — and the disk it
    /// lays — stay byte-identical to before the group existed.
    /// **`data.io` reaches PID 1 as `korp.data.io=uring`, and only when
    /// declared and not `safe`** — beside the fs token, never instead of it;
    /// the cell's own spelling wins; the id moves; an fs-only row is unchanged.
    #[test]
    fn data_io_is_baked_as_a_korp_token_only_when_declared_and_not_safe() {
        use crate::data::{Data, DataFs, DataIo};
        let mut cell = InstallerBuild::default();
        cell.data = Data { fs: DataFs::Xfs, io: DataIo::Safe };
        let fs_only_id = cell.content_id();
        assert!(!cell.effective_boot_params().iter().any(|p| p.starts_with("korp.data.io=")), "safe bakes nothing");
        cell.data = Data { fs: DataFs::Xfs, io: DataIo::Uring };
        let params = cell.effective_boot_params();
        assert!(params.contains(&"korp.data.io=uring".to_string()), "{params:?}");
        assert!(params.contains(&"korp.data.fs=xfs".to_string()), "the fs token stays: {params:?}");
        assert_eq!(data_io_boot_token(DataIo::Uring), "korp.data.io=uring");
        assert_ne!(cell.content_id(), fs_only_id, "the arm is another medium");
        let read: InstallerBuild = toml::from_str("name = \"x\"\nvolume_id = \"X\"\n[data]\nfs = \"xfs\"\nio = \"uring\"\n").unwrap();
        assert_eq!(read.data.io, DataIo::Uring);
        let emitted = toml::to_string(&read).unwrap();
        assert!(emitted.contains("io = \"uring\""), "{emitted}");
        let mut spelled = cell.clone();
        spelled.boot_params = vec!["korp.data.io=safe".into()];
        let p = spelled.effective_boot_params();
        assert_eq!(p.iter().filter(|t| t.starts_with("korp.data.io=")).count(), 1);
        assert!(p.contains(&"korp.data.io=safe".to_string()), "the cell's own spelling wins");
    }

    #[test]
    fn target_firmware_is_baked_as_a_korp_token_only_when_declared() {
        use crate::target::Firmware;
        let plain = holgerish();
        assert!(
            !plain.effective_boot_params().iter().any(|p| p.starts_with(FIRMWARE_BOOT_PARAM)),
            "undeclared: no token: {:?}",
            plain.effective_boot_params()
        );
        for fw in [Firmware::Uefi, Firmware::SeaBios] {
            let mut cell = holgerish();
            cell.target.firmware = fw;
            let params = cell.effective_boot_params();
            assert!(params.contains(&format!("korp.target.firmware={}", fw.as_str())), "{fw:?}: {params:?}");
            assert_eq!(params.iter().filter(|p| p.starts_with(FIRMWARE_BOOT_PARAM)).count(), 1);
            assert_eq!(firmware_boot_token(fw), format!("korp.target.firmware={}", fw.as_str()));
        }
        // A declared `both` is what an absent token already means — the group
        // declared for its consoles alone bakes no firmware token.
        let mut consoles_only = holgerish();
        consoles_only.target.consoles = vec![crate::target::Console::Vga];
        assert!(
            !consoles_only.effective_boot_params().iter().any(|p| p.starts_with(FIRMWARE_BOOT_PARAM)),
            "{:?}",
            consoles_only.effective_boot_params()
        );
        // A preset declares the fact too (UpCloud = seabios).
        let mut preset = holgerish();
        preset.target.preset = Some("upcloud".into());
        assert!(preset.effective_boot_params().contains(&"korp.target.firmware=seabios".to_string()));
        // A hand-spelled token is not doubled.
        let mut spelled = holgerish();
        spelled.target.firmware = Firmware::Uefi;
        spelled.boot_params = vec!["korp.target.firmware=both".into()];
        assert_eq!(spelled.effective_boot_params().iter().filter(|p| p.starts_with(FIRMWARE_BOOT_PARAM)).count(), 1);
    }

    /// **`--param reinstall=wipe` reaches the medium as `korp.reinstall=wipe`,
    /// and ONLY when supplied.** The default medium bakes no reinstall token
    /// (so a korp-installed target is upgraded and its /data kept by
    /// construction); a declared, supplied `wipe` lands exactly once; the
    /// declared spec is the shared one.
    #[test]
    fn reinstall_wipe_is_a_declared_param_that_lands_only_when_supplied() {
        let mut cell = holgerish();
        cell.params.push(reinstall_param_spec());
        assert!(
            !cell.effective_boot_params().iter().any(|p| p.starts_with(REINSTALL_BOOT_PARAM)),
            "declared but not supplied: no token: {:?}",
            cell.effective_boot_params()
        );
        let mut supplied = std::collections::BTreeMap::new();
        supplied.insert("reinstall".to_string(), REINSTALL_WIPE.to_string());
        cell.apply_cli_params(&supplied).unwrap();
        let params = cell.effective_boot_params();
        assert_eq!(params.iter().filter(|p| p.starts_with(REINSTALL_BOOT_PARAM)).count(), 1, "{params:?}");
        assert!(params.contains(&"korp.reinstall=wipe".to_string()), "{params:?}");
        assert_eq!(cell.applied_params.len(), 1);
        // RED: the same word supplied to a medium that did NOT declare the
        // param is refused by name — a policy nobody declared never lands.
        let mut undeclared = holgerish();
        let err = undeclared.apply_cli_params(&supplied).unwrap_err().to_string();
        assert!(err.contains("not a declared parameter"), "{err}");
        assert_eq!(reinstall_param_spec().boot_param, "korp.reinstall");
    }

    fn holgerish() -> InstallerBuild {
        let mut b = InstallerBuild::default();
        b.name = "holger-installer".into();
        b.volume_id = "HOLGER".into();
        b
    }

    /// **The mark reaches the rendered ISO, and it changes the content id.**
    ///
    /// `installer_iso()` copies a fixed handful of fields and falls to
    /// `..Default::default()` for the rest, so a new field is one forgotten line away
    /// from being declarable and silently dropped — which is worse than not having it,
    /// because the declaration then LOOKS honoured. This asserts both halves: the
    /// rendered ISO carries the path, and two cells that differ only by their mark are
    /// not the same artifact.
    #[test]
    fn the_boot_logo_travels_to_the_rendered_iso_and_into_the_content_id() {
        let plain = holgerish();
        let mut marked = holgerish();
        marked.boot_logo = Some("assets/holger.webp".into());

        assert_eq!(plain.installer_iso().boot_logo, None);
        assert_eq!(marked.installer_iso().boot_logo.as_deref(), Some("assets/holger.webp"));
        assert_ne!(
            plain.content_id(),
            marked.content_id(),
            "two installers wearing different marks are two different artifacts",
        );

        // Resolved against the repo root, and relative exactly like the ISO cell's.
        let root = Path::new("/repo");
        assert_eq!(plain.boot_logo_path(root), None);
        assert_eq!(
            marked.boot_logo_path(root),
            Some(PathBuf::from("/repo/assets/holger.webp")),
        );
        // A blank declaration is not a mark — it must not become "/repo".
        let mut blank = holgerish();
        blank.boot_logo = Some("   ".into());
        assert_eq!(blank.boot_logo_path(root), None);
    }

    /// R1: the declared argv tail rides as ONE comma-joined korp.args token.
    #[test]
    fn declared_cmd_renders_as_one_korp_args_token() {
        let mut b = holgerish();
        b.cmd = vec!["start".into(), "--config".into(), "/data/holger.ron".into()];
        let params = b.effective_boot_params();
        assert!(
            params.iter().any(|p| p == "korp.args=start,--config,/data/holger.ron"),
            "the tail rides verbatim: {params:?}"
        );
        assert_eq!(
            params.iter().filter(|p| p.starts_with("korp.args=")).count(),
            1,
            "exactly one korp.args token"
        );
    }

    /// R2: elements that cannot survive the encoding are refused BY NAME at
    /// declaration — a space would become a second KERNEL token silently.
    #[test]
    fn cmd_elements_that_cannot_ride_the_token_refuse_by_name() {
        for (bad, why) in [
            ("/data/a b.ron", "whitespace"),
            ("a,b", "comma"),
            ("say\"hi\"", "quote"),
            ("", "empty"),
        ] {
            let mut b = holgerish();
            b.cmd = vec![bad.to_string()];
            let err = b.validate().expect_err(why).to_string();
            assert!(err.contains("cmd[0]"), "{why}: names the element: {err}");
        }
    }

    /// R3: a cell that already spells korp.args= in boot_params keeps its own
    /// (the korp.appliance idempotence rule, applied to the new token).
    #[test]
    fn a_cell_that_spelled_korp_args_itself_keeps_it() {
        let mut b = holgerish();
        b.boot_params = vec!["korp.args=serve".into()];
        b.cmd = vec!["start".into()];
        let params = b.effective_boot_params();
        assert_eq!(
            params.iter().filter(|p| p.starts_with("korp.args=")).count(),
            1
        );
        assert!(params.iter().any(|p| p == "korp.args=serve"), "the cell's own wins: {params:?}");
    }

    /// The env map rides as korp.env.* tokens; a secret-smelling key and an
    /// unencodable value refuse BY NAME; a cell's own token wins.
    #[test]
    fn declared_env_renders_refuses_secrets_and_respects_own_tokens() {
        let mut b = holgerish();
        b.env.insert("HOLGER_APPLIANCE".into(), "1".into());
        let params = b.effective_boot_params();
        assert!(params.iter().any(|p| p == "korp.env.HOLGER_APPLIANCE=1"), "{params:?}");

        let mut secret = holgerish();
        secret.env.insert("HUB_API_TOKEN".into(), "x".into());
        let err = secret.validate().expect_err("secret key").to_string();
        assert!(err.contains("HUB_API_TOKEN") && err.contains("secret"), "{err}");

        let mut spaced = holgerish();
        spaced.env.insert("A".into(), "b c".into());
        assert!(spaced.validate().is_err(), "whitespace value refuses");

        let mut own = holgerish();
        own.boot_params = vec!["korp.env.HOLGER_APPLIANCE=0".into()];
        own.env.insert("HOLGER_APPLIANCE".into(), "1".into());
        let params = own.effective_boot_params();
        assert_eq!(params.iter().filter(|p| p.starts_with("korp.env.HOLGER_APPLIANCE=")).count(), 1);
        assert!(params.iter().any(|p| p == "korp.env.HOLGER_APPLIANCE=0"), "the cell's own wins");
    }

    /// The installer KIND is distinct — round-trips as "installer", is never
    /// confused with `iso` in a record, and the cell's artifact_kind answers
    /// Installer.
    ///
    /// **And a variant MAY declare it.** This assertion is the 2026-08-15
    /// vocabulary ruling, and it is the exact inverse of what this test asserted
    /// before: `validate_kind` used to REFUSE `kind = "installer"` on a variant
    /// ("not an atom kind — declare an [[build.installer]] cell instead"). Under the
    /// ruling `kind` is a FIELD on a variant and `[[build.installer]]` is permanent
    /// sugar for it, so both spellings are legal and land in the same unified view.
    #[test]
    fn the_installer_kind_is_distinct_and_a_variant_may_declare_it() {
        use crate::variant::{ArtifactKind, BuildVariant, BuildVariantSet};
        assert_eq!(ArtifactKind::from("installer".to_string()), ArtifactKind::Installer);
        assert_eq!(ArtifactKind::Installer.as_str(), "installer");
        assert_ne!(ArtifactKind::Installer, ArtifactKind::Iso, "the record can tell them apart");
        assert_eq!(holgerish().artifact_kind(), ArtifactKind::Installer);

        let set = BuildVariantSet::default();
        let mut v = BuildVariant::default();
        v.name = "site-installer".into();
        v.kind = ArtifactKind::Installer;
        set.validate_kind(&v)
            .expect("a variant may declare kind = 'installer' (the ruling reverses the refusal)");

        // Both spellings reach the same unified row, with the same kind.
        let both = BuildVariantSet::from_toml_str(
            r#"
[build]
[[build.variant]]
name = "field-spelling"
kind = "installer"

[[build.installer]]
name = "section-spelling"
installer_variant = "pid1"
target_disk_gib = 64
"#,
        )
        .expect("both spellings parse");
        assert_eq!(both.declared("field-spelling").map(|d| d.kind), Some(ArtifactKind::Installer));
        assert_eq!(
            both.declared("section-spelling").map(|d| d.kind),
            Some(ArtifactKind::Installer)
        );
        assert_eq!(both.declared_count(), 2, "one variant + one installer = two declarations");
    }

    /// Provenance: two ISOs that start the product differently can never
    /// share a content id — and a pre-cmd cell keeps its id (the field only
    /// enters the canonical bytes when declared).
    #[test]
    fn cmd_reaches_the_content_id_only_when_declared() {
        let plain = holgerish();
        let mut with_cmd = holgerish();
        with_cmd.cmd = vec!["start".into()];
        assert_ne!(plain.content_id(), with_cmd.content_id());
        assert!(!String::from_utf8(plain.canonical_bytes()).unwrap().contains("#cmd:"));
    }


    fn korp() -> InstallerBuild {
        InstallerBuild {
            name: "korp-installer".into(),
            installer_variant: "korp-installer-pid1".into(),
            target_disk_gib: 64,
            ..Default::default()
        }
    }

    #[test]
    fn masters_a_uefi_installer_iso_chaining_the_pid1_variant() {
        let iso = korp().installer_iso();
        assert_eq!(iso.name, "korp-installer");
        assert_eq!(iso.format, IsoFormat::Uefi);
        assert_eq!(iso.from_variant.as_deref(), Some("korp-installer-pid1"));
        assert_eq!(iso.staging, "iso-root");
        // The masterer's argv carries an EFI boot image (a UEFI ISO).
        assert!(iso.master_args("x.iso").iter().any(|a| a == "-e"));
        assert_eq!(korp().variant_deps(), vec!["korp-installer-pid1"]);
    }

    #[test]
    fn kvm_disk_args_attach_a_blank_target_and_optional_gpu() {
        let a = korp().kvm_disk_args("/tmp/target.raw");
        assert!(a.windows(2).any(|w| w == ["-drive", "file=/tmp/target.raw,if=virtio,format=raw"]));
        assert!(!a.iter().any(|x| x == "-device"), "no GPU unless requested");

        let mut g = korp();
        g.gpu = true;
        let ga = g.kvm_disk_args("/tmp/t.qcow2");
        assert!(ga.iter().any(|x| x == "file=/tmp/t.qcow2,if=virtio,format=qcow2"), "qcow2 fmt detected");
        assert!(ga.windows(2).any(|w| w == ["-device", "virtio-vga"]), "GPU attached on request");
    }

    #[test]
    fn success_marker_and_disk_size_are_stable() {
        assert_eq!(korp().success_marker(), "INSTALLER: INSTALL-OK");
        assert_eq!(korp().target_disk_bytes(), 64 * 1024 * 1024 * 1024);
    }

    #[test]
    fn validate_and_content_id_round_trip() {
        let b = korp();
        b.validate().expect("a well-formed installer validates");
        let id = b.content_id();
        assert_eq!(id.len(), 64);

        let json = serde_json::to_string_pretty(&b).unwrap();
        let back: InstallerBuild = serde_json::from_str(&json).unwrap();
        assert_eq!(back, b, "InstallerBuild survives a JSON round-trip");
        assert_eq!(back.content_id(), id, "content id stable across round-trip");

        // A distinct disk size ⇒ a distinct id.
        let mut other = korp();
        other.target_disk_gib = 128;
        assert_ne!(other.content_id(), id);
    }

    #[test]
    fn empty_name_and_zero_disk_are_rejected() {
        let mut b = korp();
        b.name = String::new();
        assert!(b.validate().is_err());
        let mut b = korp();
        b.target_disk_gib = 0;
        assert!(b.validate().is_err());
    }

    #[test]
    fn payload_kind_defaults_to_embedded_and_labels_s3() {
        assert_eq!(PayloadKind::default(), PayloadKind::EmbeddedIso);
        assert_eq!(PayloadKind::default().label(), "embedded");
        let s3 = PayloadKind::S3 {
            endpoint: "http://minio:9000".into(),
            bucket: "korp".into(),
            key: "app.znippy".into(),
        };
        assert_eq!(s3.label(), "s3://korp/app.znippy");
    }

    #[test]
    fn lean_carries_no_backend_and_bakes_the_lean_selector() {
        let b = korp();
        assert!(!b.is_fat(), "default korp installer is LEAN");
        assert_eq!(b.appliance_label(), "lean");
        assert!(b.container_backend.is_none(), "LEAN links zero container 3pp");
        let params = b.effective_boot_params();
        assert!(
            params.iter().any(|p| p == "korp.appliance=lean"),
            "LEAN bakes korp.appliance=lean into the ISO cmdline: {params:?}"
        );
        assert!(
            !params.iter().any(|p| p.starts_with("korp.container_backend=")),
            "LEAN never names a container backend: {params:?}"
        );
    }

    #[test]
    fn each_fat_backend_bakes_its_selector_and_is_distinctly_content_addressed() {
        // RED if the backend stops flowing into the ISO cmdline or the content id — i.e.
        // if the whole matrix collapses back to one indistinguishable installer.
        let lean_id = korp().content_id();
        let mut ids = std::collections::BTreeSet::new();
        ids.insert(lean_id.clone());
        for backend in [
            ContainerBackend::Youki,
            ContainerBackend::Podman,
            ContainerBackend::Containerd,
            ContainerBackend::MicroVm,
        ] {
            let mut b = korp();
            b.container_backend = Some(backend);
            assert!(b.is_fat(), "a backend makes it FAT");
            assert_eq!(b.appliance_label(), format!("fat-{}", backend.label()));
            let params = b.effective_boot_params();
            assert!(
                params.iter().any(|p| p == "korp.appliance=fat"),
                "{}: FAT bakes korp.appliance=fat: {params:?}",
                backend.label()
            );
            assert!(
                params.iter().any(|p| p == &format!("korp.container_backend={}", backend.label())),
                "{}: FAT names its backend on the cmdline: {params:?}",
                backend.label()
            );
            let id = b.content_id();
            assert_ne!(id, lean_id, "{}: a FAT install is NOT the lean install", backend.label());
            assert!(ids.insert(id), "{}: each backend is a DISTINCT content id", backend.label());
        }
        assert_eq!(ids.len(), 5, "lean + 4 FAT backends = 5 distinct installers");
    }

    #[test]
    fn container_backend_label_round_trips_and_knows_daemonless() {
        for b in [
            ContainerBackend::Youki,
            ContainerBackend::Podman,
            ContainerBackend::Containerd,
            ContainerBackend::MicroVm,
        ] {
            assert_eq!(ContainerBackend::from_label(b.label()), Some(b));
        }
        assert_eq!(ContainerBackend::from_label("lean"), None, "lean is the no-backend sentinel");
        // The os-less-minimal property: only youki + microVM are daemonless.
        assert!(ContainerBackend::Youki.is_daemonless());
        assert!(ContainerBackend::MicroVm.is_daemonless());
        assert!(!ContainerBackend::Podman.is_daemonless(), "podman socket re-adds a userland");
        assert!(!ContainerBackend::Containerd.is_daemonless(), "containerd re-adds a daemon");
    }

    #[test]
    fn declared_appliance_boot_param_is_not_double_stamped() {
        // A cell that already declares korp.appliance= keeps its own — no duplicate.
        let mut b = korp();
        b.boot_params = vec!["console=ttyS0".into(), "korp.appliance=fat".into()];
        b.container_backend = Some(ContainerBackend::Youki);
        let params = b.effective_boot_params();
        assert_eq!(
            params.iter().filter(|p| p.starts_with("korp.appliance=")).count(),
            1,
            "the appliance selector is stamped exactly once: {params:?}"
        );
    }

    #[test]
    fn installer_from_toml() {
        let toml = r#"
name = "korp-installer"
installer_variant = "korp-installer-pid1"
target_disk_gib = 32
gpu = true
"#;
        let b: InstallerBuild = toml::from_str(toml).unwrap();
        assert_eq!(b.name, "korp-installer");
        assert_eq!(b.target_disk_gib, 32);
        assert!(b.gpu);
        assert_eq!(b.variant, "korp-appliance-lean", "defaulted variant");
        assert_eq!(b.payload, PayloadKind::EmbeddedIso, "defaulted embedded payload");
    }

    /// **The donor is declarable, validated, and part of the artifact's identity.**
    ///
    /// RED-FIRST 2026-08-26: written before `kernel_donor` existed. The seam it
    /// closes was measured the same day: `dwarves buildthing gunnar --variant
    /// gunnar-installer` composed a medium whose bzImage came from `/boot/vmlinuz`
    /// — the build box's kernel, with no pin and no receipt — while tunnr's
    /// declared, pinned `tunnr-kernel` sat built in `.kernel-out/` with nothing
    /// pointing at it.
    #[test]
    fn a_kernel_donor_is_declarable_and_rides_the_content_id() {
        let toml = r#"
name = "gunnar-installer"
variant = "gunnar-server"

[kernel_donor]
repo = "tunnr"
kernel = "tunnr-kernel"
"#;
        let b: InstallerBuild = toml::from_str(toml).unwrap();
        let d = b.kernel_donor.as_ref().expect("the declared donor is read, not dropped");
        assert_eq!(d.repo, "tunnr");
        assert_eq!(d.kernel, "tunnr-kernel");
        b.validate().expect("a fully named donor validates");

        // Identity: a medium that boots a CHOSEN kernel is not the medium that
        // boots whatever the host had.
        let mut plain = b.clone();
        plain.kernel_donor = None;
        assert_ne!(
            b.content_id(),
            plain.content_id(),
            "the donor changes what boots, so it must change the content id"
        );
        // …and a donor-less cell keeps the id it always had (no reformat tax).
        assert_eq!(plain.content_id(), plain.clone().content_id());
    }

    /// A half-named donor resolves nothing and is refused at declaration time,
    /// naming the missing half — not at compose time as a bogus "no such checkout".
    #[test]
    fn a_half_named_kernel_donor_is_refused_by_name() {
        let mut b = holgerish();
        b.kernel_donor = Some(KernelDonor { repo: String::new(), kernel: "k".into() });
        let e = format!("{:#}", b.validate().expect_err("empty repo half"));
        assert!(e.contains("kernel_donor.repo"), "{e}");

        b.kernel_donor = Some(KernelDonor { repo: "tunnr".into(), kernel: "  ".into() });
        let e = format!("{:#}", b.validate().expect_err("empty kernel half"));
        assert!(e.contains("kernel_donor.kernel"), "{e}");
    }

    /// **A `.vhd` target disk is attached as `format=vpc`, not `raw`.**
    ///
    /// The regression this closes: the format was sniffed from the extension
    /// with a two-way `if`, so every medium the `if` did not name became `raw`.
    /// A VHD attached that way shows the firmware a `conectix` footer where a
    /// partition table should be, and OVMF finds nothing bootable — a green
    /// writer and a dead boot.
    ///
    /// RED-when-broken: put the old `if disk_path.ends_with(".qcow2")` back and
    /// the vhd arm reads `format=raw`.
    #[test]
    fn a_target_disk_is_attached_with_the_format_its_medium_declares() {
        let b = korp();
        let fmt_of = |path: &str| {
            let a = b.kvm_disk_args(path);
            let i = a.iter().position(|x| x == "-drive").expect("a drive");
            a[i + 1].clone()
        };
        assert!(fmt_of("/t/d.vhd").ends_with("format=vpc"), "{}", fmt_of("/t/d.vhd"));
        assert!(fmt_of("/t/d.qcow2").ends_with("format=qcow2"), "{}", fmt_of("/t/d.qcow2"));
        assert!(fmt_of("/t/d.raw").ends_with("format=raw"), "{}", fmt_of("/t/d.raw"));
        assert!(fmt_of("/t/d.img").ends_with("format=raw"), "{}", fmt_of("/t/d.img"));
        // A blank disk a harness just made has no format to detect, and `raw`
        // is the honest default for it — the case this seam is usually handed.
        assert!(fmt_of("/t/target").ends_with("format=raw"), "{}", fmt_of("/t/target"));
        assert!(fmt_of("/t/d.VHD").ends_with("format=vpc"), "case-insensitive");
    }


    /// **A row that names its machine gets its cloud's container; one that
    /// hand-spells the same fields does not.**
    ///
    /// This is the whole of the v1.3.0 wrapper defect, in a test. The shape is
    /// not the identity, and the reason is concrete rather than pedantic:
    /// `ovh-public-cloud` and `gce-virtio` present the identical device set and
    /// import different containers, so no reverse lookup from `buses`/`nic`
    /// could ever answer.
    #[test]
    fn a_row_that_names_its_machine_yields_its_clouds_container() {
        use crate::image_writer::CloudMedium;
        use crate::target::presets;
        use crate::target::Target;

        // Named → derived.
        let mut named = korp();
        named.target = Target { preset: Some("azure-gen2".into()), ..Target::default() };
        assert_eq!(named.cloud_medium(), Some(CloudMedium::Vhd), "Azure Gen2 imports a fixed VHD");
        assert_eq!(named.cloud_medium_gap(), None);

        let mut aws = korp();
        aws.target = Target { preset: Some("aws-nitro".into()), ..Target::default() };
        assert_eq!(aws.cloud_medium(), Some(CloudMedium::Raw), "Nitro imports raw via import-snapshot");

        // The rows a reverse lookup could never separate. FOUR presets carry
        // one device set and split two-and-two on the container, which is the
        // whole argument for naming the machine rather than deriving it.
        let same_devices = ["upcloud", "hetzner-cloud", "ovh-public-cloud", "scaleway-instance"]
            .map(|n| presets::preset(n).unwrap());
        for p in &same_devices[1..] {
            assert_eq!(p.boot_disk.as_str(), same_devices[0].boot_disk.as_str(), "{} shares the bus", p.name);
            assert_eq!(p.nic.as_str(), same_devices[0].nic.as_str(), "{} shares the NIC", p.name);
        }
        let containers: std::collections::BTreeSet<_> =
            same_devices.iter().map(|p| CloudMedium::for_preset(p)).collect();
        assert!(
            containers.len() >= 2,
            "one device set, {} container(s) — if this ever became 1 the shape COULD decide and this \
             derivation could be relaxed: {containers:?}",
            containers.len()
        );
    }

    /// **An imported-disk medium that names no machine is refused BY NAME**,
    /// rather than quietly producing only the `.iso` — which is what shipped.
    #[test]
    fn an_imported_disk_row_with_no_machine_named_says_why() {
        use crate::target::Target;
        let mut row = korp();
        row.medium_boot = MediumBoot::Gpt;
        row.target = Target::default(); // no preset named
        let why = row.cloud_medium_gap().expect("a gpt medium with no machine cannot be contained");
        assert!(why.contains("names no machine"), "{why}");
        assert!(why.contains("target.preset"), "names the field to fix: {why}");
        assert!(why.contains("ovh-public-cloud"), "gives the reason, not just the rule: {why}");

        // An el-torito row is an ISO-attach machine and needs no container.
        let mut iso = korp();
        iso.medium_boot = MediumBoot::ElTorito;
        iso.target = Target::default();
        assert_eq!(iso.cloud_medium_gap(), None, "an ISO-attach row owes no import container");

        // A machine that is not in the roster is a typo, and says so.
        let mut typo = korp();
        typo.target = Target { preset: Some("azure-gen3".into()), ..Target::default() };
        let why = typo.cloud_medium_gap().expect("not a machine");
        assert!(why.contains("not in the roster"), "{why}");
    }

    /// **A NAMED ISO-attach machine owes no import container** — the arm this
    /// function's own doc comment and `korp-installer`'s composer both already
    /// promised, and the code did not have.
    ///
    /// RED until 2026-09-07: [`InstallerBuild::cloud_medium_gap`] answered
    /// *"names machine `upcloud`, whose medium arrives as an ISO attached as an
    /// IDE cdrom — no writer in `image_writer` produces that container"* for a
    /// machine that TAKES an attached ISO. Its own doc said `None` means the row
    /// "can (or that it is an ISO-attach machine and does not need one)", and
    /// `isocompose.rs` said "a cell that names no machine, **or names an
    /// ISO-attach one**, gets exactly what it got before". So naming the
    /// machine would not have silenced the sentence the composer printed — it
    /// would have swapped one gap for another, and the second one is not a gap
    /// at all: `crate::iso_writer` already produced the container that platform
    /// imports, and it is the `.iso` itself.
    ///
    /// The negative controls are the point of the arm being keyed on
    /// [`MediumBring::is_iso_attach`] and nothing broader: a machine whose
    /// platform imports a disk still owes one, whether a writer exists
    /// (`scaleway-instance` → qcow2, no gap) or no writer can exist
    /// (`scaleway-elastic-metal` → bare metal, gap BY NAME).
    #[test]
    fn a_named_iso_attach_machine_owes_no_import_container() {
        use crate::image_writer::CloudMedium;
        use crate::target::presets;
        use crate::target::Target;

        // The two ISO-attach clouds in the roster, on a medium that also boots
        // off an imported disk — the exact shape of gunnar-installer-metered.
        for machine in ["upcloud", "hetzner-cloud"] {
            assert!(presets::preset(machine).unwrap().medium.is_iso_attach());
            let mut row = korp();
            row.medium_boot = MediumBoot::Hybrid;
            row.target = Target { preset: Some(machine.into()), ..Target::default() };
            assert_eq!(row.cloud_medium(), None, "{machine}: an attached ISO is `iso_writer`'s artifact");
            assert_eq!(
                row.cloud_medium_gap(),
                None,
                "{machine} attaches the ISO this compose already wrote: there is nothing missing to report"
            );
        }

        // An imported-disk machine whose writer EXISTS: a container, no gap.
        let mut qcow = korp();
        qcow.medium_boot = MediumBoot::Hybrid;
        qcow.target = Target { preset: Some("scaleway-instance".into()), ..Target::default() };
        assert_eq!(qcow.cloud_medium(), Some(CloudMedium::Qcow2));
        assert_eq!(qcow.cloud_medium_gap(), None);

        // And one that owes a container no writer can produce, still refused BY
        // NAME — the v1.3.0 defect this whole seam was written for.
        let mut metal = korp();
        metal.medium_boot = MediumBoot::Hybrid;
        metal.target = Target { preset: Some("scaleway-elastic-metal".into()), ..Target::default() };
        let why = metal.cloud_medium_gap().expect("bare metal imports nothing this crate can write");
        assert!(why.contains("scaleway-elastic-metal") && why.contains("no writer"), "{why}");
    }

    /// **Naming the machine is LOSSLESS for the rows gunnar ships.**
    ///
    /// gunnar's `gunnar-installer-azure` hand-spells firmware/consoles/uarts/
    /// buses/nic/nics/cpu, and every one of those values is what
    /// `azure-gen2`'s own derivation produces. So the row is not choosing
    /// something the roster disagrees with — it is repeating the roster in a
    /// form that cannot be joined back to it. Swapping the seven fields for
    /// `preset = "azure-gen2"` changes no byte of the shape and gains the
    /// container.
    #[test]
    fn the_hand_spelled_azure_shape_is_exactly_the_presets_own() {
        use crate::target::{Bus, Console, Cpu, Firmware, Nic, Target, UartPort};
        let p = crate::target::presets::preset("azure-gen2").unwrap();
        let derived = p.target().expect("azure-gen2 has a spellable target group");

        // The shape gunnar's row spells out today, field for field.
        let hand = Target {
            preset: None,
            firmware: Firmware::Uefi,
            consoles: vec![Console::Vga, Console::Serial],
            uarts: vec![UartPort::Com1],
            buses: vec![Bus::HvStorvsc],
            nic: Nic::HvNetvsc,
            nics: 1,
            cpu: Cpu::Generic,
        };
        assert_eq!(derived, hand, "the hand-spelled shape IS the preset's derivation");
    }

}