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

nordisk/gunnar

crates/gunnar-recipe/tests/toml_parity.rs

//! **`.nornir/build-variants.toml` IS the code's emission — and this test fails
//! for any reason it is not.**
//!
//! # What this file used to be, and why that was not enough
//!
//! Until 2026-08-30 it was a *parity* test in the literal sense: the
//! toml on disk was hand-maintained, `recipe.rs` was a hand-maintained mirror of
//! it, and this file loaded both and compared them. That comparison was green
//! for four pin generations while it was **comparing nothing**:
//!
//! * An audit measured **two whole groups** — `[build.installer.target]` (the
//!   machine) and `[build.installer.data]` (what /data is laid with) — present
//!   in the toml and absent from `recipe.rs`. Parity passed, because the pinned
//!   loader dropped them on the way in: a mirror compared against a reader that
//!   discards what it cannot parse agrees about nothing, loudly.
//! * A later audit measured the same file **refused outright** by the published 0.1.10
//!   reader over one additive `kernel_requires` key, and wrote its
//!   `[nornir-build-thing] schema` header by hand — which is what a file with no
//!   writer looks like.
//!
//! The law is that
//! there is ONE declaration and the file is its projection. So the assertions
//! here are:
//!
//! 1. [`the_committed_toml_is_the_codes_emission_byte_for_byte`] — the file is
//!    `cargo run -p gunnar-build-thing -- emit`'s output, byte for byte. This is
//!    the assertion that fails **for any reason** the two differ: a row added to
//!    the code and not re-emitted, a hand edit, a reordering, a comment typed
//!    into the output.
//! 2. [`an_edited_toml_has_zero_effect_on_what_the_recipe_declares`] — the file
//!    is inert. An edit is visible to a *reader* and invisible to the recipe,
//!    which is what "never read back as authority" means, measured.
//! 3. [`a_group_the_reader_would_drop_fails_instead_of_being_dropped`] — the
//!    exact defect the first audit found, put back and seen red: a group at a path the
//!    loader ignores is now a REFUSAL (R3, `schema::dropped_keys`), so it can
//!    neither be emitted nor read.
//!
//! Everything below those is a **value** assertion on load-bearing spellings —
//! the ceremony gate, the bootstrap admin, the kernel donors, the boot mark.
//! They are not implied by (1): (1) says the file matches the code, and these
//! say the code still declares the thing that was expensive to learn.

use std::path::PathBuf;

use gunnar_recipe::{emitted_toml, GunnarBuildThing};
use nornir_build_thing::emit::first_difference;
use nornir_build_thing::plugin::BuildThing;
use nornir_build_thing::variant::BuildVariantSet;

/// gunnar's repo root, from this crate's manifest directory.
fn repo_root() -> PathBuf {
    gunnar_recipe::emit_recipe::repo_root()
}

/// The committed bytes.
fn committed() -> String {
    let path = BuildVariantSet::conventional_path(&repo_root());
    std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {}: {e}", path.display()))
}

/// The set the toml on disk declares, loaded by `nornir-build-thing`'s own
/// loader rather than by a second parser written here.
fn from_toml() -> BuildVariantSet {
    let path = BuildVariantSet::conventional_path(&repo_root());
    BuildVariantSet::load_file(&path).unwrap_or_else(|e| panic!("load {}: {e:#}", path.display()))
}

// ── 1. THE FILE IS THE EMISSION ─────────────────────────────────────────────

/// **The one assertion that fails for any reason the file and the code differ.**
///
/// The fix is never to edit the toml. It is `cargo run -p gunnar-build-thing --
/// emit` and commit the result — and if the emission does not say what you
/// wanted the file to say, the recipe is missing something and *that* is the
/// bug.
#[test]
fn the_committed_toml_is_the_codes_emission_byte_for_byte() {
    let emitted = emitted_toml().expect("the recipe emits");
    let committed = committed();
    if let Some(diff) = first_difference(&committed, &emitted) {
        panic!(
            "{} is not the code's emission — the code is the source. Run \
             `cargo run -p gunnar-build-thing -- emit` and commit the result; never edit \
             the toml by hand. {diff}",
            BuildVariantSet::conventional_path(&repo_root()).display()
        );
    }
    // And the emission reads back, through the one loader, to the declaration —
    // R2 and R3 both run inside `from_toml_str`, so a file that is readable here
    // is readable by every door at or above the schema it declares.
    let back = BuildVariantSet::from_toml_str(&emitted).expect("the emission parses back");
    assert_eq!(back, GunnarBuildThing.variants());
    back.validate_all().expect("and it validates");
}

/// **The toml is inert.** An edit changes what a *reader* sees and changes
/// nothing about what the recipe declares.
///
/// The perturbation is asserted to be visible first: without that arm this test
/// would pass on a file the reader never opened, which is precisely the shape of
/// green the old parity test had.
#[test]
fn an_edited_toml_has_zero_effect_on_what_the_recipe_declares() {
    let declared = GunnarBuildThing.variants();
    let edited = emitted_toml().unwrap().replace(
        "name = \"gunnar-server\"",
        "name = \"gunnar-server-edited\"",
    );
    let read = BuildVariantSet::from_toml_str(&edited).expect("still parses");
    assert_ne!(
        read, declared,
        "the edit must be visible to a reader, else this test proves nothing"
    );
    assert_eq!(
        GunnarBuildThing.variants(),
        declared,
        "the recipe read the file — it must not"
    );
}

/// **The audited defect, put back: a group the reader would DROP now fails.**
///
/// `InstallerBuild` is not `deny_unknown_fields` — deliberately, because an
/// additive key must not make a whole recipe unloadable for an older reader
/// (R1/R2). That openness is exactly how `[build.installer.target]` and
/// `[build.installer.data]` went four generations unread while this test was
/// green. R3 closes it inside the one loader: `from_toml_str` reports every
/// `build.*` path serde ignored and refuses on any.
///
/// So: take the real emission, add a group at a path the loader has no field
/// for, and assert the load REFUSES and names the path. Delete
/// `schema::refuse_dropped` and this goes green with the group silently gone —
/// which is the whole of the old bug.
#[test]
fn a_group_the_reader_would_drop_fails_instead_of_being_dropped() {
    let emitted = emitted_toml().unwrap();
    BuildVariantSet::from_toml_str(&emitted).expect("the real emission loads");

    // A group at an OPEN type's path: `InstallerBuild` accepts unknown keys at
    // the serde level, so nothing but R3 can see this.
    let with_a_dropped_group =
        format!("{emitted}\n[build.installer.a_group_no_reader_has_a_field_for]\nfs = \"xfs\"\n");
    let err = BuildVariantSet::from_toml_str(&with_a_dropped_group)
        .expect_err("a group the reader would drop must be a refusal, not a silence")
        .to_string();
    assert!(
        err.contains("a_group_no_reader_has_a_field_for"),
        "the refusal must NAME the dropped path — an unnamed one is the silence \
         it replaces: {err}"
    );
}

// ── 2. THE LOADER IS READING WHAT THIS TEST THINKS IT IS ────────────────────

/// Measured, not asserted, on every comparison in this file.
///
/// Two empty sets compare equal. If `load_file` ever returned a default — a
/// renamed file, a moved repo root, a schema change that drops every key — the
/// assertions would go green having compared nothing to nothing. This pins both
/// sides to the shape they actually have, and watches the comparison fail on a
/// set that differs by exactly one field.
#[test]
fn the_comparison_reports_red_on_a_set_that_differs_by_one_field() {
    let toml = from_toml();
    // Fifteen since 2026-09-05 (`gunnar-installer-metered`, the first medium to
    // compose the metered binary); fourteen since 2026-09-04
    // (`gunnar-server-metered`, `gunnar-server-metered-thin`, the plan-enforcing
    // build points of the entitlement work); twelve since the AWS Nitro row
    // (`gunnar-installer-aws`).
    // Eleven since 2026-08-30 (`gunnar-installer-gce`, the virtio-scsi medium);
    // ten since 2026-08-28 (`gunnar-installer-open`, the keyless installer);
    // nine since 2026-08-27 (`gunnar-server-thin`, the build point
    // `--opt wireguard=off` arrives at). The literal is a loader check, not a
    // preference: a set that came back short would make every comparison below
    // green about a file it never read.
    // Sixteen since 2026-09-05 (`gunnar-installer-azure`, the Hyper-V medium).
    // Eighteen since 2026-09-05 (`gunnar-installer-metered-{aws,azure}`, the
    // metered twins of the two cloud rows).
    // Nineteen 2026-09-15 → 2026-09-17 (`gunnar-twin`, the twin appliance) —
    // and one FEWER for ever since, because a twin is a role of an appliance and
    // not a medium: the row's `variant` was `gunnar-server`, so the metered
    // appliance's twin was unmetered and a promotion would have served every
    // suspended tenant. OWNER RULING 2026-09-21: one medium, no twin ISO.
    //
    // TWENTY-ONE since 2026-09-20 (`gunnar-clock-musl`, the FRONT host's clock
    // daemon). It is the row `[build.allow_unbuilt]`'s `bin` entry had been
    // excusing the absence of — "no variant row fits it until a front-host atom
    // exists" — while `private-gunnar-ops` built that binary from a cargo line
    // on whatever box typed `cargo xtask front`. `Atom::GunnarClock` exists now,
    // so the excuse is deleted and this is the row that replaced it.
    // TWENTY-FIVE since 2026-09-25: the two FRONT media (vm1's own) and their
    // payloads — gunnar-front + gunnar-front-installer-open (lane D), and
    // gunnar-front-metered + gunnar-front-installer-metered (the paid front).
    // TWENTY-NINE since 2026-09-30: the four DEV-NARRATE `…-narrate` payload
    // rows, the build points the media's `dev-narrate` option (default OFF)
    // arrives at when a dev medium turns it ON.
    // THIRTY since 2026-10-11: `gunnar-musl`, the SIGNED client-only CLI every
    // site serves under `/download/` (E4).
    // TWENTY-NINE again since 2026-10-21 (lane L10): `gunnar-clock-musl` deleted, the
    // gunnar-clock crate moved to edda estate-clock and the front keeps its clock
    // as a thread of `gunnar-front`.
    // THIRTY-ONE since 2026-10-28 (OX-VARIANTS): `gunnar-open` and `gunnar-open-client`,
    // the crates.io package `gunnar` as `cargo install gunnar` builds it.
    // TWENTY-SEVEN since 2026-10-30 (GUN-PRODUCTS): the three products `gunnar`,
    // `gunnar-ui` and `gunnar-cli` and the estate's builds; seven rows retired
    // (`gunnar` the client-only git binary, the old `gunnar-cli`, `gunnar-musl`,
    // `gunnar-solo`, `gunnar-service`, `gunnar-open`, `gunnar-open-client`).
    assert_eq!(
        toml.variants.len(),
        27,
        "the toml parsed to {} variants; it declares twenty-seven (GUN-PRODUCTS 2026-10-30: \
         the three products and the estate's builds; thirty-one before), so the loader is \
         not reading what this test thinks it is",
        toml.variants.len()
    );
    assert!(
        !toml.offers.is_empty(),
        "the toml parsed to an empty feature space"
    );

    let mut perturbed = toml.clone();
    perturbed.variants.pop().expect("a variant to drop");
    assert_ne!(
        perturbed, toml,
        "BuildVariantSet compares equal after a variant was removed, so the \
         assertions here can never report red"
    );

    let mut renamed = toml.clone();
    renamed.variants[0].name.push('x');
    assert_ne!(
        renamed, toml,
        "BuildVariantSet compares equal after a rename"
    );

    let mut unfeatured = toml.clone();
    unfeatured.variants[0].features.clear();
    assert_ne!(
        unfeatured, toml,
        "BuildVariantSet compares equal after a variant lost its features"
    );

    let mut uninstalled = toml.clone();
    uninstalled.installers.clear();
    assert_ne!(
        uninstalled, toml,
        "BuildVariantSet compares equal after the installer variants were removed"
    );
}

// ── 3. THE LOAD-BEARING VALUES ──────────────────────────────────────────────

/// **The ceremony gate, the bootstrap admin, and every parameter's landing.**
///
/// These are values that were expensive to learn and cheap to lose. Assertion
/// (1) holds the file to the code; this holds the code to what was measured.
#[test]
fn the_installer_declares_the_gate_the_admin_and_no_secret_on_the_cmdline() {
    let toml = from_toml();
    assert_eq!(
        toml.installers.len(),
        10,
        "ten installer variants — the two FRONT media (gunnar-front-installer-open and -metered, 2026-09-25, vm1's own) and eight before them: the sealed one, its keyless twin (2026-08-28), the \
         twin's virtio-scsi twin for a GCE-shaped machine (2026-08-30), the AWS \
         Nitro twin (2026-09-02), the Azure Hyper-V twin (2026-09-05), the METERED twin \
         (2026-09-05) and the metered twins of the AWS and Azure rows (2026-09-05). NINE \
         between 2026-09-15 and 2026-09-17, when `gunnar-twin` was a row of its own — \
         deleted, because a twin is a ROLE of an appliance and a twin row cloned from the \
         KEYLESS lineage made the metered appliance's twin UNMETERED, which a promotion \
         would have shipped in silence (`tests/twin_is_the_same_medium.rs`); the \
         loader parsed {}",
        toml.installers.len()
    );
    let inst = toml
        .installers
        .iter()
        .find(|i| i.name == "gunnar-installer")
        .expect("the sealed installer is declared");
    // The `korp.env.` spelling, NEVER the bare one: `carry_site_tokens` forwards
    // only "korp."-prefixed tokens to the installed disk, so a bare
    // `GUNNAR_APPLIANCE=1` here would satisfy a laxer assertion while the
    // installed appliance boots ungated — the exact defect the 2026-08-15 drive
    // caught. This asserts the token that actually reaches the metal.
    assert!(
        inst.boot_params
            .iter()
            .any(|p| p == "korp.env.GUNNAR_APPLIANCE=1"),
        "the ceremony gate must survive the load — without it the installed \
         appliance mints no identity and prints no key: {:?}",
        inst.boot_params
    );
    assert!(
        !inst.boot_params.iter().any(|p| p == "GUNNAR_APPLIANCE=1"),
        "the BARE gate form is the measured 2026-08-15 defect — an env var for \
         the installer's PID 1 that never reaches the installed disk: {:?}",
        inst.boot_params
    );
    assert!(
        !inst
            .boot_params
            .iter()
            .any(|p| p.contains("PrivateKey") || p.starts_with("GUNNAR_WG=")),
        "no secret and no seeded tunnel on a WORLD-READABLE /proc/cmdline: {:?}",
        inst.boot_params
    );

    // ── The METERED medium's cmdline ────────────────────────────────────────
    //
    // The one row that puts KEY MATERIAL on a world-readable /proc/cmdline, so
    // it is the row this assertion exists for. Two things are measured on the
    // token that actually reaches the metal:
    //
    // 1. The key is THERE and is a 64-hex literal. A path would name a file
    //    nothing was allowed to create — `korp-installer` stages onto /data only
    //    under `korp.reinstall=wipe` and the ESP is read-only at runtime — so a
    //    medium carrying a path boots a metered gunnar that cannot verify a
    //    single fact. Asserting merely that `--monetize-pubkey` appears would
    //    pass on exactly that broken medium, so the VALUE is asserted too.
    // 2. It is a PUBLIC verifying key and nothing else. 64 hex characters is a
    //    32-byte Ed25519 public key; an OpenSSH private key, a `PrivateKey=`
    //    line or a base64 blob has no business here and could not ride a
    //    whitespace-split cmdline anyway.
    let metered = toml
        .installers
        .iter()
        .find(|i| i.name == gunnar_recipe::METERED_INSTALLER)
        .expect("the metered installer is declared");
    assert_eq!(
        metered.variant,
        gunnar_recipe::METERED_VARIANT,
        "the metered medium must compose the METERED binary — composed from the community \
         `gunnar-server` it would carry no entitlement code at all and serve every \
         namespace Free, which is the failure that has no symptom"
    );
    // Since 2026-09-20 the pair rides `korp.env.*` rather than the `korp.args`
    // argv — still the kernel command line, and still the only channel into a
    // booting gunnar on a keep-upgrade, but a token a composing door can
    // REPLACE, which an argv is not (clap's argv beats its own `env`, and a
    // landed cmdline parameter appends where `korp.args` must stay one token).
    // `gunnar_recipe::metered_env` carries the full measurement.
    let key = metered
        .env
        .get(gunnar_recipe::MONETIZE_PUBKEY_ENV)
        .unwrap_or_else(|| {
            panic!(
                "the metered medium declares a monetize key: {:?}",
                metered.env
            )
        });
    assert_eq!(
        key,
        gunnar_recipe::MONETIZE_PUBKEY,
        "the declared row must carry monetize's key ITSELF as its DEFAULT; a path \
         there names a file the installer was never allowed to write"
    );
    assert!(
        key.len() == 64 && key.chars().all(|c| c.is_ascii_hexdigit()),
        "the key on the cmdline must be the 64 hex characters of a 32-byte Ed25519 \
         PUBLIC key, and nothing longer: {key}"
    );
    assert_eq!(
        metered
            .env
            .get(gunnar_recipe::BILLING_URL_ENV)
            .map(String::as_str),
        Some(gunnar_recipe::recipe::METERED_BILLING_URL),
        "the renew URL is MANDATORY beside the key — a medium with one half of the \
         pair made gunnar refuse to start, and gunnar is PID 1: {:?}",
        metered.env
    );
    // WWW-CANON (owner 2026-10-30): the site is www.gunnar.rs; the medium's placeholder names it.
    assert_eq!(
        gunnar_recipe::recipe::METERED_BILLING_URL,
        "https://www.gunnar.rs/billing/{tenant}"
    );
    assert!(
        metered
            .params
            .iter()
            .any(|p| p.name == gunnar_recipe::MONETIZE_PUBKEY_PARAM),
        "the metered row must DECLARE the key as a parameter, or no door can hand it \
         the front's live one and the default above goes stale the day a new estate \
         is laid: {:?}",
        metered.params.iter().map(|p| &p.name).collect::<Vec<_>>()
    );
    assert!(
        !metered.boot_params.iter().any(|p| p.contains("PrivateKey")
            || p.contains("PRIVATE KEY")
            || p.starts_with("GUNNAR_WG=")),
        "no PRIVATE key on a world-readable /proc/cmdline, on this row least of all: {:?}",
        metered.boot_params
    );

    let names: Vec<&str> = inst.params.iter().map(|p| p.name.as_str()).collect();
    assert_eq!(
        names,
        [
            "hub_conf",
            "admin_key",
            "static_ip",
            "gateway",
            "log_udp",
            // 2026-09-14: where "now" comes from, a site value (`gunnar-clock`).
            "clock",
            "clock_key",
            "reinstall",
            // 2026-09-15: an orchestrated medium powers off after INSTALL-OK.
            "install_then",
            // 2026-10-12: the per-estate live-update switch (`korp.live_update`, part of the BASE).
            "live_app" // No `role` and no `data_disks` since 2026-09-22 (one medium; the role
                       // over the wire; the installer elects its own disks).
        ],
        "the declared parameters, in order — `params` is a Vec and the mirror \
         compares positionally"
    );
    // `admin_key` is REQUIRED, and that is the half the reachability gate cannot
    // see: the gate proves the argv NAMES `--admin-key`, this proves a build that
    // names it and supplies nothing is refused by the framework's own
    // missing-required rule. Measured under KVM 2026-08-25: an installed
    // appliance with neither logged "Nobody can reach this server" on every start
    // and served anyway.
    let admin = inst
        .params
        .iter()
        .find(|p| p.name == "admin_key")
        .expect("the installer declares a bootstrap admin");
    assert!(
        admin.required,
        "a PERSISTENT appliance may not be mastered without a bootstrap admin"
    );
    assert_eq!(
        admin.staged_file, "admin.pub",
        "an OpenSSH key line has spaces and cannot ride a whitespace-split cmdline; \
         it lands as a staged file, which is also where the argv's `@/data/admin.pub` \
         points"
    );
    assert!(
        admin.boot_param.is_empty(),
        "and NOT as a boot param, which would truncate it at the key type"
    );
    for p in &inst.params {
        assert!(
            p.validate().is_ok(),
            "parameter '{}' does not validate: {:?}",
            p.name,
            p.validate().err().map(|e| format!("{e:#}"))
        );
        // Every landing that is a boot param must be `korp.`-prefixed, or
        // `carry_site_tokens` drops it between the installer's boot and the
        // installed disk — the measured 2026-08-15 defect, arriving by a new road.
        assert!(
            p.boot_param.is_empty() || p.boot_param.starts_with("korp."),
            "parameter '{}' lands on the cmdline as {:?}, which `carry_site_tokens` \
             will not forward to the installed disk",
            p.name,
            p.boot_param
        );
    }
}

/// **The LIVE medium wears gunnar's mark too — not only the installer.**
///
/// MEASURED 2026-08-18 on the laptop: the live boot showed korp's raven on the
/// graphics console while gunnar's own banner went to serial. The installer
/// variant got its `boot-logo` on 2026-08-16; the live iso variant did not — and
/// edda's live composition additionally ignored the key (fixed edda-side
/// 2026-08-20, `korp_installer::forge::compose_first_stage`). This holds the
/// DECLARATION half: the live variant names the same mark the installer bakes,
/// and the file is on disk.
#[test]
fn the_live_iso_variant_wears_gunnars_mark_and_it_is_the_installers_mark() {
    let toml = from_toml();
    let iso = toml
        .isos
        .iter()
        .find(|i| i.name == "gunnar-appliance-iso")
        .expect("the live iso variant is declared");
    assert_eq!(
        iso.boot_logo.as_deref(),
        Some("assets/gunnar.webp"),
        "the LIVE medium must declare gunnar's mark — without it korp's raven is \
         what a live boot shows (measured on the laptop 2026-08-18): {:?}",
        iso.boot_logo
    );
    assert_eq!(
        iso.boot_logo, toml.installers[0].boot_logo,
        "one product, one mark: the live medium and the installer wear the same file"
    );
    let mark = repo_root().join(iso.boot_logo.as_deref().unwrap());
    assert!(
        mark.is_file(),
        "the declared mark must exist on disk (it is a build input, committed): {}",
        mark.display()
    );
}

/// **Every medium names its KERNEL DONOR, and there is exactly one donor.**
///
/// RED-FIRST 2026-08-26: before these declarations existed, a BYGG on
/// `gunnar-installer` composed a medium whose bzImage was the BUILD BOX's
/// `/boot/vmlinuz` — "a kernel nobody in this repository chose" — while tunnr's
/// declared, pinned `tunnr-kernel` sat built in `.kernel-out/` with nothing
/// pointing at it. The live ISO had the same hole for a day longer (wave 3).
///
/// Since the emitter landed this is a LOADED-VALUE assertion only. The raw-text arm this
/// test used to carry existed because the pinned loader was blind to the field
/// and a raw read was the honest guard; the pin is a path dep on 0.1.12 now
/// (see the audits above), the field loads, and the file is the code's emission — so a
/// spelling check would be checking `toml::to_string_pretty` rather than gunnar.
#[test]
fn both_media_declare_the_same_kernel_donor_and_a_client_declares_none() {
    let set = from_toml();
    let mirror = gunnar_recipe::iso_kernel_donor();

    let iso = set
        .isos
        .iter()
        .find(|i| i.name == "gunnar-appliance-iso")
        .expect("the live iso is declared");
    assert_eq!(
        iso.kernel_donor.as_ref(),
        Some(&mirror),
        "the live medium boots whatever kernel the build box has unless it declares a donor"
    );

    let installer_donor = set
        .installers
        .iter()
        .find(|i| i.name == "gunnar-installer")
        .and_then(|i| i.kernel_donor.clone())
        .expect("the installer declares its donor");
    assert_eq!(
        (
            installer_donor.repo.as_str(),
            installer_donor.kernel.as_str()
        ),
        ("tunnr", "tunnr-kernel")
    );
    assert_eq!(
        mirror, installer_donor,
        "two media, two kernels — the live iso drifted from the installer"
    );

    // One kernel per repository, answered by name for every medium and for
    // neither client.
    assert_eq!(
        gunnar_recipe::kernel_donor_of("gunnar-appliance-iso"),
        Some(mirror.clone())
    );
    assert_eq!(
        gunnar_recipe::kernel_donor_of("gunnar-installer"),
        Some(mirror.clone())
    );
    assert_eq!(
        gunnar_recipe::kernel_donor_of(gunnar_recipe::OPEN_INSTALLER),
        Some(mirror.clone()),
        "the keyless twin boots the same kernel"
    );
    assert_eq!(gunnar_recipe::kernel_donor_of("gunnar-cli"), None);
}

/// **A DEFAULT group is not written down, and that is the emission telling the
/// truth.**
///
/// `[build.installer.target]` was one of the two groups the audit found in the
/// toml and not in the code. It reaches `recipe.rs` now — and a later audit
/// measured what it actually says: `firmware = both`, `consoles = [vga, serial]`,
/// `buses = [ide-cdrom, virtio-blk]`, `nic = virtio`, which is `Target::default()`
/// **exactly**. `InstallerBuild::target` is
/// `skip_serializing_if = "Target::is_default"`, so the emission omits it.
///
/// That is correct and it is worth an assertion, because the omission looks
/// exactly like the loss it replaces: the group is DECLARED (the code carries
/// it, and `--list-options` prints it as RECIPE-ONLY), it simply changes nothing
/// for THOSE media.
///
/// **That day arrived on 2026-08-30** — exactly as the sentence that
/// used to end this comment predicted: `gunnar-installer-gce` declares
/// `buses = [virtio-blk, virtio-scsi]` because GCE's Persistent Disk is
/// virtio-scsi, the table appeared in the emission with nobody adding it by
/// hand, and this test now says BOTH halves: which media are still the default
/// machine, and which is not and why.
#[test]
fn the_appliance_media_declare_the_default_machine_and_the_gce_medium_does_not() {
    use nornir_build_thing::target::Bus;
    let set = from_toml();
    // The two UpCloud-shaped media: still exactly `Target::default()`, still
    // emitting nothing. This half is the ORIGINAL assertion, unchanged, and it
    // is what keeps the appliance's 2.4 MB kernel from paying for another
    // cloud's disk.
    for name in ["gunnar-installer", "gunnar-installer-open"] {
        let i = set.installers.iter().find(|i| i.name == name).expect(name);
        assert!(
            i.target.is_default(),
            "'{name}' must stay at the default machine"
        );
    }
    // And the day the doc comment above promised has come (2026-08-30):
    // `gunnar-installer-gce` declares a machine that DIFFERS, so the table is in
    // the file — one table, for one row, with the bus that is the whole reason.
    let gce = set
        .installers
        .iter()
        .find(|i| i.name == "gunnar-installer-gce")
        .expect("the virtio-scsi medium is declared");
    assert!(!gce.target.is_default());
    assert!(
        gce.target.buses.contains(&Bus::VirtioScsi),
        "{:?}",
        gce.target.buses
    );
    assert!(
        !gce.target.buses.contains(&Bus::IdeCdrom),
        "GCE attaches no cdrom; carrying ATA for a device it does not have is the bloat \
         this row exists to avoid"
    );
    assert_eq!(
        gce.kernel_donor.as_ref().map(|d| d.kernel.as_str()),
        Some("tunnr-kernel-scsi"),
        "the shared donor's `nuked` preset vetoes CONFIG_SCSI by name"
    );
    // And its AWS Nitro sibling: NVMe boot disk, ENA NIC, the
    // nvme donor, and no cdrom either.
    let aws = set
        .installers
        .iter()
        .find(|i| i.name == "gunnar-installer-aws")
        .expect("the NVMe/ENA medium is declared");
    assert!(!aws.target.is_default());
    let aws_t = aws.target.effective();
    assert!(aws_t.buses.contains(&Bus::Nvme), "{:?}", aws_t.buses);
    assert!(
        !aws_t.buses.contains(&Bus::IdeCdrom) && !aws_t.buses.contains(&Bus::VirtioScsi),
        "Nitro has neither cdrom nor scsi"
    );
    assert_eq!(aws_t.nic, nornir_build_thing::target::Nic::Ena);
    assert_eq!(
        aws.target.preset.as_deref(),
        Some("aws-nitro"),
        "declared by preset since 2026-09-05"
    );
    assert_eq!(
        aws.kernel_donor.as_ref().map(|d| d.kernel.as_str()),
        Some("tunnr-kernel-nvme"),
        "the slim preset vetoes neither CONFIG_BLK_DEV_NVME nor CONFIG_ENA_ETHERNET"
    );
    // And the Azure sibling (2026-09-05): Hyper-V synthetic SCSI + NIC over
    // VMBus, the hyperv donor, UEFI only (a Gen2 VM has no CSM), no cdrom.
    let azure = set
        .installers
        .iter()
        .find(|i| i.name == "gunnar-installer-azure")
        .expect("the Hyper-V medium is declared");
    assert!(!azure.target.is_default());
    let azure_t = azure.target.effective();
    assert_eq!(
        azure.target.preset.as_deref(),
        Some("azure-gen2"),
        "declared by preset since 2026-09-05"
    );
    assert_eq!(azure_t.buses, vec![Bus::HvStorvsc], "{:?}", azure_t.buses);
    assert_eq!(azure_t.nic, nornir_build_thing::target::Nic::HvNetvsc);
    assert_eq!(azure_t.firmware, nornir_build_thing::target::Firmware::Uefi);
    assert_eq!(
        azure.kernel_donor.as_ref().map(|d| d.kernel.as_str()),
        Some("tunnr-kernel-hyperv"),
        "the slim preset vetoes none of HYPERV / HYPERV_STORAGE / HYPERV_NET"
    );
    let tables = committed().matches("[build.installer.target]").count();
    let differing = set
        .installers
        .iter()
        .filter(|i| !i.target.is_default())
        .count();
    assert_eq!(
        tables, differing,
        "one emitted table per row whose machine differs, and no other"
    );
    assert_eq!(
        tables, 5,
        "five media now differ from the default machine: gce (virtio-scsi), aws (nvme/ena), \
         azure (hv-storvsc/hv-netvsc), and the metered twins of aws and azure, which keep \
         their open twin's machine"
    );
    // And the group is still a declaration, not an absence: the code carries it.
    let declared = GunnarBuildThing.variants();
    assert_eq!(
        declared.installers.len(),
        set.installers.len(),
        "the code and the file declare the same installers"
    );
}

/// Every variant the file declares became a variant, and none was invented.
///
/// Implied by the byte-for-byte assertion; stated separately because it is the
/// property the migration had to preserve, and a reader looking for that
/// guarantee should not have to derive it from a `PartialEq`.
#[test]
fn every_toml_variant_became_a_variant_and_no_variant_was_invented() {
    let toml = from_toml();
    for v in &toml.variants {
        let c = gunnar_recipe::by_name(&v.name)
            .unwrap_or_else(|| panic!("the toml declares '{}' and no variant carries it", v.name));
        assert_eq!(c.name, v.name);
    }
    for c in gunnar_recipe::VARIANTS {
        assert!(
            toml.get(c.name).is_some(),
            "variant '{}' is in no declaration",
            c.name
        );
    }
    assert_eq!(gunnar_recipe::VARIANTS.len(), toml.variants.len());
}

/// **The installer's KERNEL DONOR is declared — and this pin's loader is blind
/// to it, which is exactly why the guard reads the raw file.**
///
/// RED-FIRST 2026-08-26 (under the standing order to make the fixes already
/// known, the day the
/// donor vocabulary landed in edda): before the declaration below existed, a
/// BYGG on `gunnar-installer` composed a medium whose bzImage was the BUILD
/// BOX's `/boot/vmlinuz` — "a kernel nobody in this repository chose", this
/// file's own words — while tunnr's declared, pinned `tunnr-kernel` sat built
/// in `.kernel-out/` with nothing pointing at it. `[build.installer.kernel_donor]
/// repo=tunnr kernel=tunnr-kernel` is the wiring: the forge (edda ≥0.1.8)
/// resolves the donor's built, pin-verified bzImage on BOTH doors (⚒ BYGG and
/// `dwarves buildthing gunnar --variant gunnar-installer`), and `--opt
/// kernel.*` is judged against the DONOR's declaration.
///
/// Until 2026-08-26 this crate pinned `nornir-build-thing` **0.1.7 from the
/// registry**, whose `InstallerBuild` had no `kernel_donor` field — the loader
/// ignored the table benignly, the whole-struct parity assertion above could not
/// hold the declaration, and a raw read of the file was the honest guard. The
/// same day 0.1.9 (the first release carrying the field) reached crates.io and
/// the pin advanced: the mirror in `recipe.rs` now declares the donor too, the
/// parity assertion holds it like every other field, and what this test keeps
/// is the load-bearing spellings plus the LOADED value — a reformat that changes
/// them should be looked at, not absorbed.
#[test]
fn the_installer_declares_its_kernel_donor_and_the_raw_file_is_the_guard() {
    let path = BuildVariantSet::conventional_path(&repo_root());
    let raw =
        std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {}: {e}", path.display()));

    // The table, with both halves. Not a regex — the spellings are load-bearing
    // and a reformat that changes them should be looked at, not absorbed.
    assert!(
        raw.contains("[build.installer.kernel_donor]"),
        "the installer variant declares no [build.installer.kernel_donor] — the composed \
         medium is back to booting whatever kernel the build box has"
    );
    assert!(
        raw.contains("repo = \"tunnr\""),
        "the kernel donor names no repo — the reference resolves nothing"
    );
    assert!(
        raw.contains("kernel = \"tunnr-kernel\""),
        "the kernel donor names no [[build.kernel]] — the reference resolves nothing"
    );

    // …and the loader SEES it: the registry pin advanced to 0.1.9 on 2026-08-26
    // (the first `InstallerBuild` with the field), so the donor is a loaded fact,
    // not a raw-file spelling. From here the whole-struct parity assertion above
    // holds the two mirrors equal on this field like every other — this test keeps
    // only the spellings (load-bearing, reformat-visible) and the loaded value.
    let set = from_toml();
    let variant = set
        .installers
        .iter()
        .find(|i| i.name == "gunnar-installer")
        .expect("the installer variant is declared");
    let donor = variant
        .kernel_donor
        .as_ref()
        .expect("the loader carries the kernel donor");
    assert_eq!(
        (donor.repo.as_str(), donor.kernel.as_str()),
        ("tunnr", "tunnr-kernel")
    );
}

/// **The LIVE medium's KERNEL DONOR is declared — and this pin's loader is blind
/// to it, so the raw file is the guard, held to the Rust mirror.**
///
/// RED-FIRST 2026-08-26 (wave 3, the iso-edge work): wave 2 landed the iso edge in
/// edda (`61e0d53`, `IsoBuild.kernel_donor`) and dwarves (`0abb7a8`, the CLI
/// door reads it), and the acceptance said "`gunnar-appliance-iso` consumes
/// `tunnr/tunnr-kernel`" — but gunnar's own toml carried only the INSTALLER's
/// edge (`[build.installer.kernel_donor]`), so a live ISO composed on a box still
/// packed the box's `/boot/vmlinuz`. This test was written against that tree
/// and failed on its first assertion; the table below it is what turned it.
///
/// The shape is the installer's under the 0.1.7 pin, reused on purpose: this
/// crate pins `nornir-build-thing` 0.1.9 from the registry, whose `IsoBuild` has
/// no `kernel_donor` field — the loader ignores the table benignly and the
/// whole-struct parity assertion above cannot hold it. So the Rust mirror is
/// `recipe::iso_kernel_donor()`, and this test holds the toml's table to it in
/// BOTH directions: the table's `repo`/`kernel` must equal the mirror's, and the
/// mirror must name the same donor the installer names (one kernel per
/// repository, chosen once). Edit either half alone and this goes red. Since the
/// 0.1.10 pin the LOADED value is asserted too, and `tests/iso_edge_linked.rs`
/// is the test that was RED until it could be.
#[test]
fn the_live_iso_declares_its_kernel_donor_and_the_raw_file_is_the_guard() {
    let path = BuildVariantSet::conventional_path(&repo_root());
    let raw =
        std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {}: {e}", path.display()));

    // The table exists, once — a HEADER LINE, not a mention in a comment (the
    // file talks about the table by name several times; a table is a line that
    // is nothing but the header).
    let header = "[build.iso.kernel_donor]";
    let lines: Vec<&str> = raw.lines().collect();
    let starts: Vec<usize> = lines
        .iter()
        .enumerate()
        .filter(|(_, l)| l.trim() == header)
        .map(|(i, _)| i)
        .collect();
    assert_eq!(
        starts.len(),
        1,
        "the live iso variant declares {} [build.iso.kernel_donor] table(s); the composed \
         medium boots whatever kernel the build box has unless it declares exactly one",
        starts.len()
    );
    // Its body: the lines up to the next table header. Not a toml parser — the
    // one loader this crate trusts is the registry 0.1.9 one, which drops this
    // table, and a second parser here would be a second opinion.
    let body: Vec<&str> = lines[starts[0] + 1..]
        .iter()
        .copied()
        .take_while(|l| !l.trim_start().starts_with('['))
        .map(str::trim)
        .filter(|l| !l.is_empty() && !l.starts_with('#'))
        .collect();
    let mirror = gunnar_recipe::iso_kernel_donor();
    let want_repo = format!("repo = \"{}\"", mirror.repo);
    let want_kernel = format!("kernel = \"{}\"", mirror.kernel);
    assert!(
        body.contains(&want_repo.as_str()),
        "the iso's kernel donor table says {body:?}; the Rust mirror says {want_repo}"
    );
    assert!(
        body.contains(&want_kernel.as_str()),
        "the iso's kernel donor table says {body:?}; the Rust mirror says {want_kernel}"
    );
    assert_eq!(
        body.len(),
        2,
        "a kernel donor is exactly repo + kernel: {body:?}"
    );

    // One kernel per repository: the live medium and the installer name the SAME
    // donor, and `kernel_donor_of` answers both by name and neither for an exe.
    let set = from_toml();
    let installer_donor = set
        .installers
        .iter()
        .find(|i| i.name == "gunnar-installer")
        .and_then(|i| i.kernel_donor.clone())
        .expect("the installer declares its donor (the test above holds it)");
    assert_eq!(
        mirror, installer_donor,
        "two media, two kernels — the live iso drifted from the installer"
    );
    assert_eq!(
        gunnar_recipe::kernel_donor_of("gunnar-appliance-iso"),
        Some(mirror.clone())
    );
    assert_eq!(
        gunnar_recipe::kernel_donor_of("gunnar-installer"),
        Some(mirror.clone())
    );
    assert_eq!(
        gunnar_recipe::kernel_donor_of(gunnar_recipe::OPEN_INSTALLER),
        Some(mirror.clone()),
        "the keyless twin boots the same kernel"
    );
    assert_eq!(gunnar_recipe::kernel_donor_of("gunnar-cli"), None);

    // …and the loader carries it too, since the 0.1.10 pin.
    {
        let iso = set
            .isos
            .iter()
            .find(|i| i.name == "gunnar-appliance-iso")
            .expect("the live iso is declared");
        assert_eq!(
            iso.kernel_donor.as_ref(),
            Some(&mirror),
            "the loader carries the iso's donor"
        );
    }
}

// ── 8. THE METERED CLOUD TWINS ──────────────────────────────────────────────

/// **Each metered twin is its open twin with the binary swapped and the key
/// added — and NOTHING else moved.**
///
/// Three twins (2026-09-05): `gunnar-installer-metered` of `-open` (UpCloud),
/// `-metered-aws` of `-aws`, `-metered-azure` of `-azure`. The property that
/// matters is the same for all three and is spelled by FIELD NAME rather than
/// as `assert_ne!`: a twin that also lost its kernel donor, its preset or its
/// `medium_boot = gpt` would still be "different from its open twin" and would
/// still carry the key, and would park on the cloud it names. So the set of
/// fields that differ is asserted exactly, and within `boot_params` only the
/// `korp.args=` token — where the argv gains `--monetize-pubkey <key>` and
/// changes in no other way.
///
/// Rows are read from the TOML the loader parsed, not from the code, so this
/// also holds the emission: a twin the emitter flattened (a lost
/// `[build.installer.target]` table, say) fails here by name.
#[test]
fn every_metered_twin_differs_from_its_open_twin_by_binary_key_name_and_volume_only() {
    let toml = from_toml();
    let row = |name: &str| {
        toml.installers
            .iter()
            .find(|i| i.name == name)
            .unwrap_or_else(|| panic!("`{name}` is declared"))
    };
    let pairs = [
        (
            gunnar_recipe::METERED_INSTALLER,
            gunnar_recipe::OPEN_INSTALLER,
            gunnar_recipe::METERED_VOLUME_ID,
        ),
        (
            gunnar_recipe::METERED_AWS_INSTALLER,
            gunnar_recipe::AWS_INSTALLER,
            gunnar_recipe::METERED_AWS_VOLUME_ID,
        ),
        (
            gunnar_recipe::METERED_AZURE_INSTALLER,
            gunnar_recipe::AZURE_INSTALLER,
            gunnar_recipe::METERED_AZURE_VOLUME_ID,
        ),
    ];
    for (metered_name, open_name, volume_id) in pairs {
        let m = row(metered_name);
        let o = row(open_name);

        // The field-by-field diff, by name. Every field of `InstallerBuild` is
        // listed so a field added to the struct later is a compile-time nudge
        // to decide whether a twin may move it.
        let mut differ: Vec<&str> = Vec::new();
        macro_rules! field {
            ($f:ident) => {
                if m.$f != o.$f {
                    differ.push(stringify!($f));
                }
            };
        }
        field!(name);
        field!(variant);
        field!(installer_variant);
        field!(volume_id);
        field!(payload);
        field!(container_backend);
        field!(target_disk_gib);
        field!(gpu);
        field!(boot_params);
        field!(cmd);
        field!(env);
        field!(boot_logo);
        field!(kernel_donor);
        field!(target);
        field!(data);
        field!(medium_boot);
        field!(edges);
        field!(presets);
        field!(applied_preset);
        field!(params);
        field!(fixtures);
        assert_eq!(
            differ,
            ["name", "variant", "volume_id", "env", "params"],
            "`{metered_name}` must be `{open_name}` with the binary swapped, its own volume \
             id, the monetize pair in `env` and the parameter that replaces the key — and \
             nothing else. A metered twin that moves its donor, preset, firmware or \
             medium_boot is a medium for a machine that does not exist. Fields that differ: \
             {differ:?}"
        );

        // The three that moved, each to what it must be.
        assert_eq!(
            m.variant,
            gunnar_recipe::METERED_VARIANT,
            "{metered_name}: the METERED binary"
        );
        assert_eq!(m.volume_id, volume_id, "{metered_name}: its own volume id");
        assert_ne!(
            m.volume_id, o.volume_id,
            "{metered_name}: installtest resolves a medium to its cell by volume id FIRST"
        );

        // The argv is the open row's, byte for byte. What makes this medium the
        // metered one is the BINARY it packs and the `env` pair below — and the
        // pair is in `env` and not on the argv precisely so that a composing
        // door can replace the key (`gunnar_recipe::metered_env`).
        assert_eq!(
            m.boot_params, o.boot_params,
            "{metered_name}: the metered medium's argv IS the open one — metering is a \
             property of the binary and of the monetize pair, not of the door"
        );

        // The monetize pair, from the ONE writer. The URL is not decoration:
        // `gunnar-server` has no default for it and refuses at boot when given a
        // key without one, and gunnar is PID 1 — so a medium carrying only the
        // key kernel-panics on first boot. Measured 2026-09-08, as an outage.
        // `metered_env` writes both halves or neither, so it cannot recur.
        assert_eq!(
            m.env,
            gunnar_recipe::metered_env(gunnar_recipe::MONETIZE_PUBKEY),
            "{metered_name}: the key and the renew URL that key requires, out of one writer"
        );
        assert!(
            o.env.is_empty(),
            "{open_name}: the community medium names no monetize key"
        );

        // The settings the front's live key arrives through: the open row's
        // parameters plus exactly these two, appended in this order — the key's
        // door, and `monetize_vital` (owner ruling 2026-09-22, 180b067: default
        // true on every metered row). The test predates the second and read
        // red on gunnar-live from then until 2026-09-27.
        let mut want = o.params.clone();
        want.push(gunnar_recipe::monetize_pubkey_param());
        want.push(gunnar_recipe::monetize_vital_param());
        // …and the usage share's two (owner 2026-09-28): the budget, default
        // `none` in `metered_env`, and the window.
        want.push(gunnar_recipe::admit_limit_param());
        want.push(gunnar_recipe::admit_window_param());
        assert_eq!(
            m.params,
            want,
            "{metered_name}: the open row's parameters plus `{}` — the declared door a \
             composing ladder hands the front's MONETIZE_SIGNING_PUBKEY through, so the \
             default above can never go stale unnoticed — and `{}`",
            gunnar_recipe::MONETIZE_PUBKEY_PARAM,
            gunnar_recipe::MONETIZE_VITAL_PARAM
        );
    }
}

// ── the twin's memory bound rides the ROLE, and no medium ───────────────────

/// **The twin's memory bound is in CODE, and NO installer row — and no role — bakes it.**
///
/// MEASURED (`crates/gunnar-server/tests/twin_snapshot_memory.rs`, and lane T3's one-medium
/// run of 2026-09-21): with explode/graph off alone a 512 MiB snapshot install grew the heap
/// +133 MiB; two resolver workers, a 16 MiB lookahead and a 16 MiB explode budget brought it
/// to +36 MiB, under the 64 MiB ceiling — and a twin installed WITHOUT the explode token held
/// five `objects.exploded` files it may not hold. The bound is real; what changed is where it
/// is decided.
///
/// The five `korp.env.ZNIPPY_*` tokens were on a `gunnar-twin` installer ROW until the owner
/// ruling of 2026-09-21 (*"gunnar twin must be built EXACTLY like the main, it MUST use the
/// SAME iso"*) deleted the row, and then on the twin ROLE until `znippy-plugin-git` 0.1.5
/// shipped `set_explode_default` / `set_explode_budget_default` /
/// `set_resolve_workers_default` / `set_resolve_lookahead_default` beside
/// `set_graph_cache_default`. gunnar now calls them from `Engine::hold_twin_memory_bound`, so
/// **a twin needs nothing on its command line at all** — which is the only shape in which
/// "the same medium, no per-box parameter" is literally true.
///
/// So the assertion is now the empty one, in both places at once: the
/// code's declared rows and the committed toml. A token reappearing anywhere is either a
/// medium that is a twin or a per-box parameter, and both are the shape the ruling forbids.
#[test]
fn the_twin_memory_bound_is_on_the_role_and_no_row_bakes_it() {
    let spelled = [
        "korp.env.ZNIPPY_GIT_EXPLODE=off",
        "korp.env.ZNIPPY_GIT_GRAPH_CACHE=off",
        "korp.env.ZNIPPY_GIT_RESOLVE_WORKERS=2",
        "korp.env.ZNIPPY_GIT_RESOLVE_LOOKAHEAD_BYTES=16777216",
        "korp.env.ZNIPPY_GIT_EXPLODE_BUDGET=16777216",
    ];
    // And nothing is a twin by declaration.
    for (source, set) in [
        ("the recipe", GunnarBuildThing.variants()),
        ("the committed toml", from_toml()),
    ] {
        for row in &set.installers {
            for token in spelled {
                let key = token.split('=').next().unwrap();
                let carried: Vec<_> = row
                    .boot_params
                    .iter()
                    .filter(|p| p.split('=').next() == Some(key))
                    .collect();
                assert!(
                    carried.is_empty(),
                    "{source}: row {} bakes {carried:?}. gunnar sets this in code now; a row \
                     that carries it is a medium that is a twin, and there are no twin media",
                    row.name
                );
            }
            assert!(
                !row.boot_params
                    .iter()
                    .any(|p| p.contains("--role,twin") || p.starts_with("korp.env.GUNNAR_ROLE=")),
                "{source}: row {} declares itself a twin",
                row.name
            );
        }
    }
}