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

nordisk/dwarves

dwarves-gui/src/boot_run.rs

//! **The in-pane boot: ▶ Build & Boot a `kind=vm` launch cell → live serial console.**
//!
//! Clicking Build on the korp KVM row builds the REAL install-on-boot PID-1 and boots
//! it under QEMU/OVMF/KVM through the constellation's ONE VM boot path —
//! `jera::vm::BootSpec` → `jera::vm::default_vm_runner()` → `draupnir::kvm::KvmBoot` →
//! `tunnr_vm::boot_test` (the same seam the forge's `runa_backend::run_vm` drives) —
//! streaming the live serial console INTO the pane's console view — the `installed
//! korp-demo` / `started korp-demo` / `KORP-APPLIANCE BOOT-OK` lines appear as they
//! stream, ending on a BOOT PASS chip. Reuse, never twin (L5): the boot engine is
//! tunnr's, the spec and lifecycle are jera's; this only drains the console. tunnr is
//! still linked for HOST FACTS (is QEMU/OVMF there, which kernel, which appliance),
//! never to boot.
//!
//! **Honest + fail-safe.** The real boot lives behind the `vm-boot` feature (it links
//! `jera/vm-tunnr` + `tunnr-vm`). Without it, or on a box with no QEMU/OVMF, the button surfaces the
//! missing-precondition error into the console (naming exactly what to install) and
//! ends FAILED — never a fabricated BOOT-OK. The boot source is a trait
//! ([`SerialConsole`]) produced by a [`BootLauncher`], so a robot test injects a fake
//! that replays a REAL captured korp serial and proves the pump + console path without
//! a heavy KVM boot.

use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};

/// The terminal-or-live phase of an in-pane boot.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BootPhase {
    /// Compiling the appliance PID-1 (before QEMU starts).
    Building,
    /// QEMU is running; the BOOT-OK marker has not (yet) streamed past.
    Booting,
    /// The BOOT-OK marker appeared — the appliance reached runtime (smoke gate passed).
    Passed,
    /// The boot failed / was cancelled / hit a missing precondition. Carries why.
    Failed(String),
}

impl BootPhase {
    /// Stable label for the pane chip / `state_json`.
    pub fn label(&self) -> &'static str {
        match self {
            BootPhase::Building => "building",
            BootPhase::Booting => "booting",
            BootPhase::Passed => "passed",
            BootPhase::Failed(_) => "failed",
        }
    }
    /// Whether the boot is still in flight (Building/Booting).
    pub fn is_running(&self) -> bool {
        matches!(self, BootPhase::Building | BootPhase::Booting)
    }
}

/// **What kind of thing the RESULT console is showing** — the axis that picks the
/// console widget in the pane's result area: a `Container` streams a LOG window
/// ([`LogConsole`]), a `Vm`/`Iso` streams the QEMU SERIAL console. All three ride the
/// SAME [`SerialConsole`] sink + [`pump`], so the pane routes by this kind alone.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LaunchKind {
    /// A `[[build.vm]]` KVM appliance — serial console (+ a graphical console when a
    /// GPU is configured, see [`HwConfig`]).
    Vm,
    /// A `[[build.iso]]` bootable ISO — firmware-booted under OVMF, serial console.
    Iso,
    /// A `[[build.container]]` OCI image — its stdout/logs streamed into a LOG window.
    Container,
}

impl LaunchKind {
    /// Stable label for `state_json`.
    pub fn as_str(self) -> &'static str {
        match self {
            LaunchKind::Vm => "vm",
            LaunchKind::Iso => "iso",
            LaunchKind::Container => "container",
        }
    }
}

/// Whether an ISO launch cell is a **live-boot scaffold** — a `*-iso` cell that is NOT
/// a disk installer, and whose bootable UEFI ESP loader does not exist in the tree yet.
///
/// A live-boot ISO would need a UEFI Linux loader inside the ESP (`EFIBOOT.IMG`) —
/// systemd-boot / GRUB EFI / an EFI-stub bzImage — to hand OVMF's firmware control to
/// the kernel+initramfs. The two efistub fixtures in the tree are terminal (a
/// serial-marker stub and an LBA-0 writer), so no such loader exists. The pane marks
/// these cells "scaffold · not bootable (needs ESP)" and the real launcher refuses them
/// HONESTLY (never a faked boot). korp-installer ISOs are NOT scaffolds — their ESP
/// carries a real, self-contained UEFI installer app; korp-kvm boots via QEMU `-kernel`.
///
/// Feature-independent (no `vm-boot`) so the pane can render the chip either way.
pub fn iso_cell_is_bootable_scaffold(cell_name: &str) -> bool {
    cell_name.ends_with("-iso")
        && !cell_name.ends_with("-installer")
        // korp-live-iso is NOT a scaffold: its ESP carries the self-contained korp-live UEFI
        // stub (run-from-RAM), booted like the installer's self-contained ESP — no kernel
        // loader needed. It routes to `boot_live_iso`, never the scaffold refusal.
        && !cell_name.contains("-live")
}

/// The honest, host-independent reason a live-boot ISO cell cannot boot yet — what the
/// real launcher surfaces into the console instead of a cryptic masterer error (and
/// never a faked BOOT-OK).
pub fn iso_scaffold_reason(cell_name: &str) -> String {
    format!(
        "'{cell_name}' is a SCAFFOLD ISO cell: the pure-Rust UEFI ISO masters, but \
         booting the live kernel needs a UEFI Linux loader in the ESP (EFIBOOT.IMG) — \
         systemd-boot / GRUB EFI / an EFI-stub bzImage — that is not in the tree yet, so \
         there is nothing to hand OVMF's firmware control to the kernel+initramfs. Not \
         faked. Run BYGG first — an iso-kind variant WITH a composed medium boots that \
         medium (a self-contained ESP ISO) through the live leg — or boot the `-kvm` \
         cell (direct QEMU -kernel) / a `-installer` ISO (real self-contained ESP)."
    )
}

/// **The launch plan for an iso-kind variant** — the ONE routing rule the pane's ▶ and
/// the real launcher share, pure and feature-independent so a robot test proves it
/// without QEMU.
///
/// The rule closed gap #7 (dwarves-ui audit): a declared iso-kind variant whose BYGG
/// composed a REAL medium (isocompose output — a self-contained El Torito ESP ISO) was
/// still refused by the launcher ("no real appliance wired" / the scaffold refusal)
/// and offered the korp-installer efistub stand-in. A composed medium IS the product's
/// own bootable ISO, so it takes the live-ISO leg — firmware-boot off `-cdrom`,
/// run-from-RAM, GPU + QMP framebuffer per the hardware config — and it boots THE
/// COMPOSED FILE, never a re-mastered one. The stand-in remains ONLY for the
/// no-medium case, and keeps announcing itself.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum IsoLaunchPlan {
    /// BYGG composed a real medium: firmware-boot THAT file through the live-ISO leg.
    LiveComposed {
        /// The composed medium, verbatim — the file the finished build named.
        medium: PathBuf,
    },
    /// No composed medium on disk: the stand-in leg (an honest refusal or the
    /// self-announcing scaffold), which never quietly boots something else.
    Standin,
}

/// Decide the [`IsoLaunchPlan`] for an iso-kind variant: a medium that is really on
/// disk boots; anything else (no build yet, a failed build's empty artifact, a path
/// that has since vanished) is the stand-in leg.
pub fn plan_iso_launch(medium: Option<&Path>) -> IsoLaunchPlan {
    match medium.filter(|p| p.is_file()) {
        Some(p) => IsoLaunchPlan::LiveComposed {
            medium: p.to_path_buf(),
        },
        None => IsoLaunchPlan::Standin,
    }
}

/// **The serial marker a COMPOSED medium's boot gates on** — korp-start's own
/// `exec argv: ["<entry>"` line (`korp-installer/src/bin/korp-start.rs`, argv[0] from
/// `cmdline::app_argv`), printed through the `INSTALLER: `-prefixed serial sink as the
/// last thing before `fexecve` replaces the process. Reaching it proves the whole
/// composed chain ran — firmware → korp-boot → vmlinuz → korp-start → payload verified
/// — AND it names the product when the variant declares a `korp.entry=`. Shared by the
/// installer reboot leg and the iso-kind live boot so the two gates cannot drift.
pub fn composed_handoff_marker(entry: Option<&str>) -> String {
    match entry {
        Some(e) => format!("INSTALLER: exec argv: [\"{e}\""),
        None => "INSTALLER: exec argv: [".to_string(),
    }
}

// ── The live-ISO ssh forward: clone INTO the live-booted appliance (2026-08-20) ──────

/// **The guest TCP port the live-ISO ssh forward targets** — gunnar's convention: its
/// appliance sshd listens on `0.0.0.0:2222` (proven over a hostfwd by gunnar's own
/// forge test, `gunnar_appliance_iso`). korp/holger may listen elsewhere, and the
/// `[[build.iso]]`/`[[build.installer]]` descriptors declare NO per-variant
/// service-port token yet (checked 2026-08-20: name/volume_id/boot_params/params —
/// no port), so this is HONESTLY hardcoded to gunnar's convention. Follow-up: a
/// declared per-variant service-port token in the descriptor, read here.
pub const LIVE_SSH_GUEST_PORT: u16 = 2222;

/// The first host port the forward tries: `127.0.0.1:10022`.
pub const LIVE_SSH_HOST_PORT: u16 = 10022;

/// How many consecutive host ports the bounded pick-next-free walk probes
/// (10022..=10029) before failing BY NAME — never a silent rebind past the window,
/// never 0-means-anything.
pub const LIVE_SSH_PORT_TRIES: u16 = 8;

/// The planned QEMU netdev tokens for the live-ISO leg: SLIRP user networking on a
/// `virtio-net-pci` NIC — exactly the netdev the leg always had — plus a
/// LOOPBACK-bound host forward to the guest's service port, so
/// `git clone ssh://…@127.0.0.1:<host_port>:…` reaches the live-booted appliance.
///
/// Bound to `127.0.0.1` deliberately: tunnr's `NetConfig::User { hostfwd }` seam
/// spells `tcp::<h>-:<g>` (all interfaces), and a demo box must not expose the
/// guest's sshd on the LAN — so the leg suppresses tunnr's default NIC and authors
/// this netdev itself (`NetConfig::None` + these tokens). Folding a bind-address into
/// tunnr's `NetConfig` is the named follow-up. Pure and feature-independent so the
/// argv is asserted without QEMU.
pub fn live_ssh_netdev_args(host_port: u16) -> Vec<String> {
    vec![
        "-netdev".into(),
        format!("user,id=n0,hostfwd=tcp:127.0.0.1:{host_port}-:{LIVE_SSH_GUEST_PORT}"),
        "-device".into(),
        "virtio-net-pci,netdev=n0".into(),
    ]
}

/// The honest console line naming the forward's address — emitted the moment the
/// guest really starts booting, so the operator reads `127.0.0.1:<port>` off the
/// pane, never out of the source. A pick-next-free rebind says so LOUDLY.
pub fn ssh_forward_notice(host_port: u16) -> String {
    let rebound = if host_port == LIVE_SSH_HOST_PORT {
        String::new()
    } else {
        format!(" ({LIVE_SSH_HOST_PORT} was taken)")
    };
    format!(
        "[dwarves] ssh forward: host 127.0.0.1:{host_port} -> guest {LIVE_SSH_GUEST_PORT}{rebound}"
    )
}

/// Pick the forward's host port: [`LIVE_SSH_HOST_PORT`], or the next free port in the
/// bounded [`LIVE_SSH_PORT_TRIES`] window. Exhausting the window fails BY NAME.
pub fn pick_live_ssh_host_port() -> Result<u16, String> {
    pick_free_loopback_port(LIVE_SSH_HOST_PORT, LIVE_SSH_PORT_TRIES)
}

/// The bounded walk itself: probe-bind each candidate on `127.0.0.1` and take the
/// first that binds. The probe listener is dropped before QEMU starts, so a TOCTOU
/// window exists — and QEMU then fails by name on ITS bind rather than rebinding,
/// which keeps the no-silent-rebind rule either way.
pub fn pick_free_loopback_port(base: u16, tries: u16) -> Result<u16, String> {
    let end = base.saturating_add(tries.saturating_sub(1));
    for port in base..=end {
        if std::net::TcpListener::bind(("127.0.0.1", port)).is_ok() {
            return Ok(port);
        }
    }
    Err(format!(
        "no free ssh-forward host port on 127.0.0.1: {base}-{end} are all taken \
         (bounded walk, {tries} tries) — free one, or cancel the boot holding it"
    ))
}

/// **Which console WIDGET the result area renders** — the honest mode the pane is in.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ConsoleMode {
    /// A text console draining serial (vm/iso) or container stdout (container) lines.
    Serial,
    /// A GRAPHICAL framebuffer console — a KVM cell configured WITH a GPU exposes a
    /// real virtio-gpu framebuffer, so the pane shows the VM's screen, captured live via
    /// QMP `screendump` (see [`FramebufferCapture`]) and uploaded as an egui texture.
    /// Before the first frame the pane shows an honest placeholder over the live serial
    /// (never a faked graphical frame).
    Graphical,
}

impl ConsoleMode {
    /// Stable label for `state_json` / the pane chip.
    pub fn as_str(self) -> &'static str {
        match self {
            ConsoleMode::Serial => "serial",
            ConsoleMode::Graphical => "graphical",
        }
    }
}

/// **The KVM cell's hardware configuration** — a scratch DISK (always) and an
/// OPTIONAL GPU. Threaded into the jera `BootSpec`'s `extra_qemu_args` (as extra
/// `-drive`/`-device` tokens) by [`TunnrBootLauncher`], and surfaced in the cell UI as
/// a toggle. The GPU toggle also selects the RESULT console: GPU on → a graphical
/// framebuffer console ([`ConsoleMode::Graphical`]); GPU off → the serial-text console
/// ([`ConsoleMode::Serial`]) the distroless appliance streams BOOT-OK over.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct HwConfig {
    /// Attach a scratch data disk (a virtio-blk `-drive`). Always on for a KVM cell —
    /// a VM always has a disk device — but the size is configurable.
    pub disk: bool,
    /// Scratch disk size in MiB (a sparse raw file created pure-Rust, no `qemu-img`).
    pub disk_mb: u32,
    /// Attach a GPU (`-device virtio-gpu-pci`). OFF for the headless serial appliance;
    /// ON exposes a real framebuffer and switches the result console to graphical.
    pub gpu: bool,
    /// For an **installer ISO** cell: after `INSTALLER: INSTALL-OK`, ACTUALLY reboot the
    /// KVM guest OFF the installed disk (the owner — the reboot leg must EXECUTE, not just
    /// print). The flow lays a real FAT ESP (`EFI/BOOT/BOOTX64.EFI` = the PRODUCTION
    /// `korp-boot` first stage, which then chainloads `\vmlinuz` = the `korp-start`
    /// second-boot payload) into the installed disk's p1 in place, then boots a SECOND QEMU
    /// firmware-boot off that disk (cdrom detached), gating on `APP-ALIVE` — so the pane
    /// SHOWS korp come up. `true` by default (the live demo). Set `false` for the
    /// install-only fallback proof (INSTALL-OK, no second boot) so that guaranteed-green
    /// leg is decoupled from second-boot flakiness. Ignored for non-installer cells.
    pub reboot_into_installed: bool,
    /// **The live-ISO ssh forward's host port**, already picked
    /// ([`pick_live_ssh_host_port`]) by the spawn that announced it — `Some(p)` wires
    /// `hostfwd=tcp:127.0.0.1:<p>-:`[`LIVE_SSH_GUEST_PORT`] onto the leg's user netdev
    /// so the operator can `git clone` INTO the live-booted appliance. `None` (the
    /// default, and every non-iso leg today) keeps the plain outbound-only user net.
    /// LIVE-ISO ONLY for now: the installer's first boot runs the installer (no sshd),
    /// and the reboot-into-installed leg would advertise gunnar's 2222 for products
    /// that never declared a service port — wire those when the descriptor grows a
    /// per-variant service-port token.
    pub ssh_fwd_host_port: Option<u16>,
}

impl Default for HwConfig {
    fn default() -> Self {
        // A disk device always; GPU/graphics console ON by default (the owner) — the demo's
        // gfx wow-moment is the default, not opt-in, so a presenter clicking ▶ live lands
        // straight on the framebuffer. The checkbox stays toggleable (turn it off for a
        // pure serial boot); the copyable serial text is always shown alongside the frame.
        // reboot-into-installed ON by default so an installer boot really reboots into korp.
        HwConfig {
            disk: true,
            disk_mb: 64,
            gpu: true,
            reboot_into_installed: true,
            // No forward by default — the iso-kind spawn picks a real port and fills
            // this in, announcing it; nothing is forwarded silently.
            ssh_fwd_host_port: None,
        }
    }
}

impl HwConfig {
    /// The console mode this hardware selects: a GPU → graphical framebuffer, else the
    /// serial-text console.
    pub fn console_mode(&self) -> ConsoleMode {
        if self.gpu {
            ConsoleMode::Graphical
        } else {
            ConsoleMode::Serial
        }
    }
}

/// **One decoded framebuffer frame** captured from a running guest's GPU — raw RGBA8
/// pixels the pane uploads as an egui texture (the OS-less appliance's real screen).
/// Pure bytes, no egui dep here (the UI does the `ColorImage` conversion). Produced by
/// the QMP `screendump` capture in [`TunnrBootLauncher`] for a GPU (graphical) boot;
/// every other console produces none (serial cells keep the honest text placeholder).
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Frame {
    /// Width in pixels.
    pub width: usize,
    /// Height in pixels.
    pub height: usize,
    /// Row-major RGBA8 pixels (`width * height * 4` bytes).
    pub rgba: Vec<u8>,
}

/// **The one console sink** the pane's [`pump`] drains: a **cumulative** captured log
/// and a **non-blocking** phase. Output-neutral — the SAME trait backs BOTH the QEMU
/// SERIAL console (vm/iso, over tunnr's `BootHandle` in [`TunnrBootLauncher`]) AND a
/// container's STDOUT/LOGS ([`LogConsole`], over the jera→draupnir OCI engine) — so one
/// [`pump`] + one [`BootRun`] drive every launch kind, and a robot test faking either
/// proves the whole path. Faked in tests (replaying a captured serial/log).
pub trait SerialConsole: Send {
    /// The serial captured SO FAR (cumulative — safe to poll repeatedly).
    fn log(&self) -> String;
    /// The current phase (non-blocking).
    fn phase(&self) -> BootPhase;
    /// Tear the guest down (idempotent).
    fn kill(&mut self);
    /// The latest GRAPHICAL framebuffer frame, if this console has one (a GPU boot).
    /// Default `None` — serial cells, containers, and fakes produce no frame, so the
    /// pane keeps its honest placeholder rather than a fabricated screen. NEVER faked.
    fn frame(&self) -> Option<Frame> {
        None
    }
}

/// Produces a live boot for a launch cell. The real impl ([`TunnrBootLauncher`])
/// builds the appliance PID-1 and boots it through tunnr; a test injects a fake.
/// `Err(reason)` names a missing precondition — the pane surfaces it, never a fake boot.
pub trait BootLauncher: Send + Sync {
    /// Build the cell's payload + start booting it, returning a live console.
    fn launch(&self, cell_name: &str, repo_root: &Path) -> Result<Box<dyn SerialConsole>, String>;

    /// Boot the cell WITH a hardware config (a scratch disk + an optional GPU) and,
    /// for an installer or iso-kind variant, **the medium the finished build
    /// produced**. The default
    /// ignores both and delegates to [`launch`](Self::launch) — so a fake/replay
    /// launcher keeps working unchanged; the real [`TunnrBootLauncher`] overrides this
    /// to thread the disk/GPU into the jera `BootSpec`'s `extra_qemu_args` and to boot
    /// `medium` itself.
    ///
    /// `medium` is `None` for every variant that produces no bootable artifact (a vm
    /// cell, a scaffold ISO) and for a boot asked for before anything was built. It is NOT a
    /// hint: when it names a file, that file is what boots. When it does not, the
    /// launcher says so on the console — see [`TunnrBootLauncher::boot_installer`].
    fn launch_hw(
        &self,
        cell_name: &str,
        repo_root: &Path,
        _hw: &HwConfig,
        _medium: Option<&Path>,
    ) -> Result<Box<dyn SerialConsole>, String> {
        self.launch(cell_name, repo_root)
    }
}

/// Produces a live LOG console for a `[[build.container]]` cell — the container mirror
/// of [`BootLauncher`]. The real impl ([`DraupnirContainerLauncher`], behind the
/// `container-run` feature) RUNS the image through jera→draupnir's ONE bollard/OCI
/// engine and follows its stdout; without the feature the honest stub reports it is not
/// wired (never fakes logs). A robot test injects a fake that replays a captured log.
/// `Err(reason)` names a missing precondition — the pane surfaces it, never a fake run.
pub trait ContainerLauncher: Send + Sync {
    /// Run the cell's container + start following its logs, returning a live console.
    fn launch(&self, cell_name: &str, repo_root: &Path) -> Result<Box<dyn SerialConsole>, String>;
}

/// The CODED error for a launch that could not be STARTED, routed by launch kind: a
/// container RUN that never came up is `dwarves-launch-2`; a vm/iso BOOT that never came
/// up is `dwarves-launch-5`. Both carry the launcher's own reason verbatim as the human
/// half, so the operator text is unchanged and the code is additive.
fn launch_start_error(
    kind: LaunchKind,
    cell: &str,
    reason: &str,
) -> dwarves::errcode::DwarvesError {
    match kind {
        LaunchKind::Container => {
            dwarves::dwarves_err!(launch, 2, "cannot start container {cell}: {reason}")
        }
        _ => dwarves::dwarves_err!(launch, 5, "cannot start {cell}: {reason}"),
    }
}

/// The CODED error for a run that was CANCELLED (`dwarves-launch-6`) — a kill is never a
/// passed gate, and a robot tells "cancelled" from "failed to start" by the code.
pub fn cancelled_error(cell: &str) -> dwarves::errcode::DwarvesError {
    dwarves::dwarves_err!(launch, 6, "{cell}: the run was cancelled")
}

/// One in-pane boot in flight (or the last that finished): a worker thread builds the
/// PID-1 then boots it, draining the live serial into `console` line by line.
pub struct BootRun {
    /// The launch cell being booted/run (e.g. `korp-kvm`, `tunnr-hello-world`).
    pub cell: String,
    /// Which launch kind this run is — the axis the pane routes the console on.
    kind: LaunchKind,
    /// The KVM hardware config this run was launched with (disk + GPU); default for a
    /// container/iso. Drives the [`ConsoleMode`] the result area renders.
    hw: HwConfig,
    console: Arc<Mutex<Vec<String>>>,
    phase: Arc<Mutex<BootPhase>>,
    /// The latest GPU framebuffer frame (graphical boots only), copied out of the live
    /// console by [`pump`]; `None` for serial/container runs. The pane uploads it as an
    /// egui texture — the appliance's real screen.
    frame: Arc<Mutex<Option<Frame>>>,
    kill: Arc<AtomicBool>,
    _handle: Option<std::thread::JoinHandle<()>>,
}

impl BootRun {
    /// Spawn the build-and-boot worker for a `kind=vm` `cell`, driven by `launcher`
    /// with the DEFAULT hardware (a scratch disk, no GPU → serial console).
    pub fn spawn(cell: &str, repo_root: PathBuf, launcher: Arc<dyn BootLauncher>) -> BootRun {
        Self::spawn_hw(cell, repo_root, launcher, HwConfig::default())
    }

    /// Spawn a `kind=vm`/`kind=iso` boot worker WITH a hardware config. The intro/phase
    /// wording matches the kind; the disk/GPU are threaded into the QEMU boot by the
    /// launcher's [`launch_hw`](BootLauncher::launch_hw).
    pub fn spawn_hw(
        cell: &str,
        repo_root: PathBuf,
        launcher: Arc<dyn BootLauncher>,
        hw: HwConfig,
    ) -> BootRun {
        let kind = LaunchKind::Vm; // vm/iso share this ctor; iso uses spawn_iso below.
        Self::spawn_with(
            cell,
            kind,
            hw,
            boot_intro(cell, kind, None),
            None,
            Duration::from_secs(120),
            {
                let (name, root, hw) = (cell.to_string(), repo_root, hw);
                move || launcher.launch_hw(&name, &root, &hw, None)
            },
        )
    }

    /// **Spawn an INSTALLER boot off the medium a finished build produced.**
    ///
    /// The one constructor that carries `medium`, and the reason it exists: until
    /// 2026-08-16 an installer cell went through [`spawn_hw`](Self::spawn_hw), which
    /// knows nothing about artifacts, and the launcher below therefore re-mastered a
    /// korp-shaped STAND-IN into its own temp root and booted that. Driving holger, a
    /// `BYGG` that failed and produced no artifact at all still reached
    /// `INSTALLER: INSTALL-OK` — and gunnar's, korp's and holger's captured
    /// framebuffers came home **md5-identical** (`32b65f91ac759010ecfcbd4d7ce88b11`,
    /// six frames each), because it was one image every time.
    ///
    /// `medium` is `Outcome::artifact` — the `.iso` `compose_installer_row` wrote at
    /// `<repo>/.iso-out/<cell>.iso`. `None` means the operator hit ▶ with no successful
    /// build behind it; the launcher then says STAND-IN on the console rather than
    /// quietly booting something else.
    pub fn spawn_installer(
        cell: &str,
        repo_root: PathBuf,
        launcher: Arc<dyn BootLauncher>,
        hw: HwConfig,
        medium: Option<PathBuf>,
    ) -> BootRun {
        let kind = LaunchKind::Iso;
        // Composing nothing, but installing a real 64 GiB-declared target and then
        // rebooting off it: the install itself is seconds, the second boot is the long
        // pole, and a timeout that kills the guest mid-reboot reports a failure the
        // product did not have.
        Self::spawn_with(
            cell,
            kind,
            hw,
            boot_intro(cell, kind, medium.as_deref()),
            // No ssh forward on the installer legs (see HwConfig::ssh_fwd_host_port):
            // the install boot runs the installer, not the product's sshd.
            None,
            Duration::from_secs(900),
            {
                let (name, root, hw, m) = (cell.to_string(), repo_root, hw, medium);
                move || launcher.launch_hw(&name, &root, &hw, m.as_deref())
            },
        )
    }

    /// Spawn a `kind=iso` boot worker WITH a hardware config and **the medium the
    /// finished build composed**: firmware-boot it under OVMF, streaming the SAME
    /// serial console as a vm — and, when the GPU toggle is on, the SAME live QMP
    /// framebuffer graphics console. Threads the disk/GPU through `launch_hw` so the
    /// pane's GPU toggle reaches the launcher.
    ///
    /// `medium` is `Outcome::artifact` — the self-contained ESP `.iso` BYGG composed
    /// (gap #7: until 2026-08-20 this constructor carried no medium at all, so a
    /// composed iso-kind variant like gunnar-appliance-iso was refused as unwired and
    /// offered the efistub stand-in). `None` means ▶ was hit with no successful build
    /// behind it; the intro then announces the stand-in rather than staying silent.
    pub fn spawn_iso(
        cell: &str,
        repo_root: PathBuf,
        launcher: Arc<dyn BootLauncher>,
        hw: HwConfig,
        medium: Option<PathBuf>,
    ) -> BootRun {
        let kind = LaunchKind::Iso;
        // Pick the ssh-forward host port BEFORE anything boots: the forward rides the
        // hardware config into the launcher's netdev (the real leg wires
        // `hostfwd=tcp:127.0.0.1:<p>-:2222`, see `wire_live_ssh_forward`), and the
        // console announces the address the moment the guest really starts. An
        // exhausted pick window fails the run BY NAME before the launcher runs —
        // never a silent rebind, never a forward-less boot pretending otherwise.
        let picked = pick_live_ssh_host_port();
        let (hw, notice) = match &picked {
            Ok(p) => (
                HwConfig {
                    ssh_fwd_host_port: Some(*p),
                    ..hw
                },
                Some(ssh_forward_notice(*p)),
            ),
            Err(_) => (hw, None),
        };
        Self::spawn_with(
            cell,
            kind,
            hw,
            boot_intro(cell, kind, medium.as_deref()),
            notice,
            Duration::from_secs(300),
            {
                let (name, root, hw, m) = (cell.to_string(), repo_root, hw, medium);
                move || {
                    let _port = picked?; // no free port in the window → fail by name
                    launcher.launch_hw(&name, &root, &hw, m.as_deref())
                }
            },
        )
    }

    /// Spawn a `kind=container` RUN worker: run the OCI image through the container
    /// engine and follow its stdout into a [`LogConsole`], draining it via the SAME
    /// [`pump`]. Ends on the container's readiness/exit (exit 0 → passed).
    pub fn spawn_container(
        cell: &str,
        repo_root: PathBuf,
        launcher: Arc<dyn ContainerLauncher>,
    ) -> BootRun {
        let (kind, hw) = (LaunchKind::Container, HwConfig::default());
        Self::spawn_with(
            cell,
            kind,
            hw,
            container_intro(cell),
            None,
            Duration::from_secs(120),
            {
                let (name, root) = (cell.to_string(), repo_root);
                move || launcher.launch(&name, &root)
            },
        )
    }

    /// The shared worker: emit the intro lines, run `produce` to open a live
    /// [`SerialConsole`] sink (or surface its error), then [`pump`] it to a terminal
    /// phase. One body for boot/iso/container so the console contract is identical.
    /// `notice` is an extra honest line pushed only once the console is REALLY open
    /// (today: the iso leg's ssh-forward address) — never printed for a boot that
    /// failed to start.
    fn spawn_with<F>(
        cell: &str,
        kind: LaunchKind,
        hw: HwConfig,
        intro: Vec<String>,
        notice: Option<String>,
        timeout: Duration,
        produce: F,
    ) -> BootRun
    where
        F: FnOnce() -> Result<Box<dyn SerialConsole>, String> + Send + 'static,
    {
        let console = Arc::new(Mutex::new(Vec::new()));
        let phase = Arc::new(Mutex::new(BootPhase::Building));
        let frame = Arc::new(Mutex::new(None));
        let kill = Arc::new(AtomicBool::new(false));
        let (c, p, fr, k, name) = (
            console.clone(),
            phase.clone(),
            frame.clone(),
            kill.clone(),
            cell.to_string(),
        );
        let handle = std::thread::Builder::new()
            .name(format!("dwarves-run-{cell}"))
            .spawn(move || {
                {
                    let mut g = c.lock().unwrap();
                    for line in intro {
                        g.push(line);
                    }
                }
                let mut src = match produce() {
                    Ok(s) => s,
                    Err(e) => {
                        // A launcher that cannot START is CODED (dwarves::errcode) — the
                        // code rides both the console line a human reads and the Failed
                        // phase a robot reads, so the failure is keyed on the code and not
                        // on this wording. `dwarves-launch-2` for a container run;
                        // `dwarves-launch-5` for a vm/iso boot that never came up.
                        let coded = launch_start_error(kind, &name, &e);
                        c.lock()
                            .unwrap()
                            .push(format!("[dwarves] {}", coded.log_line()));
                        *p.lock().unwrap() = BootPhase::Failed(coded.to_string());
                        return;
                    }
                };
                *p.lock().unwrap() = BootPhase::Booting;
                {
                    let mut g = c.lock().unwrap();
                    g.push(running_line(&name, kind));
                    if let Some(n) = notice {
                        g.push(n);
                    }
                }
                // A GPU boot captures the live framebuffer → let the pump linger for the
                // real final frame after a terminal phase (see `FRAME_GRACE`).
                pump(src.as_mut(), &k, &c, &p, &fr, timeout, hw.gpu);
            })
            .ok();
        BootRun {
            cell: cell.to_string(),
            kind,
            hw,
            console,
            phase,
            frame,
            kill,
            _handle: handle,
        }
    }

    /// The launch kind (the pane routes its console widget on this).
    pub fn kind(&self) -> LaunchKind {
        self.kind
    }

    /// The hardware config this run was launched with (disk + GPU).
    pub fn hw(&self) -> HwConfig {
        self.hw
    }

    /// Which console widget the result area should render for this run.
    pub fn console_mode(&self) -> ConsoleMode {
        match self.kind {
            LaunchKind::Vm | LaunchKind::Iso => self.hw.console_mode(),
            // A container is always a text LOG window (no framebuffer).
            LaunchKind::Container => ConsoleMode::Serial,
        }
    }

    /// The current phase.
    pub fn phase(&self) -> BootPhase {
        self.phase.lock().unwrap().clone()
    }

    /// The console lines captured so far (the dwarves-visible serial view).
    pub fn log(&self) -> Vec<String> {
        self.console.lock().unwrap().clone()
    }

    /// The latest GPU framebuffer frame, if this is a graphical boot that has produced
    /// one — the pane uploads it as an egui texture (the appliance's real screen).
    /// `None` for serial/container runs and before the first frame arrives.
    pub fn frame(&self) -> Option<Frame> {
        self.frame.lock().unwrap().clone()
    }

    /// Whether the boot is still in flight.
    pub fn is_running(&self) -> bool {
        self.phase().is_running()
    }

    /// Whether the boot passed (the BOOT-OK marker streamed past).
    pub fn passed(&self) -> bool {
        self.phase() == BootPhase::Passed
    }

    /// **The stable error CODE for a failed run** ([`dwarves::errcode`]) — `None` while
    /// running or once passed. Read straight out of the `Failed(reason)` phase, whose
    /// reason string carries the code (every failure path in this module raises a coded
    /// error), with `dwarves-launch-5` as the honest fallback for a failure that reached
    /// terminal without one. This is the field a robot asserts instead of matching the
    /// failure prose.
    pub fn error_code(&self) -> Option<&'static str> {
        match self.phase() {
            BootPhase::Failed(reason) => Some(
                dwarves::errcode::parse(&reason)
                    .map(|e| e.code)
                    .unwrap_or("dwarves-launch-5"),
            ),
            _ => None,
        }
    }

    /// The full dual-channel error for a failed run — the code AND the human reason.
    pub fn error(&self) -> Option<dwarves::errcode::DwarvesError> {
        let BootPhase::Failed(reason) = self.phase() else {
            return None;
        };
        let code = self.error_code()?;
        let e = dwarves::errcode::lookup(code)?;
        Some(dwarves::errcode::DwarvesError::new(
            e.subsystem,
            e.n,
            e.code,
            reason,
        ))
    }

    /// Request cancellation — the guest is powered off and the run ends FAILED
    /// (never mistaken for a passed smoke gate).
    pub fn cancel(&self) {
        self.kill.store(true, Ordering::SeqCst);
    }
}

/// The console line that says, BEFORE anything boots, WHICH medium is about to boot —
/// the composed one or the stand-in.
///
/// It is a line and not a comment because silence is exactly what let the stand-in hide:
/// a pane that boots a korp-shaped scaffold and a pane that boots the product's own ISO
/// looked identical on screen for months. Whichever is true, the operator reads it here.
pub const STANDIN_NOTICE: &str = "[dwarves] STAND-IN: no composed medium — booting the \
     korp-installer efistub SCAFFOLD, which is NOT this product. Run BYGG first.";

/// The intro lines a vm/iso boot worker emits before the launcher opens the console.
/// `medium` is the artifact a finished build produced, when there is one.
fn boot_intro(cell: &str, kind: LaunchKind, medium: Option<&Path>) -> Vec<String> {
    match kind {
        LaunchKind::Iso => {
            let mut lines = vec![format!("[dwarves] ▶ Build & Boot ISO: {cell}")];
            match medium.filter(|p| p.is_file()) {
                Some(p) => {
                    let bytes = std::fs::metadata(p).map(|m| m.len()).unwrap_or(0);
                    lines.push(format!(
                        "[dwarves] medium: {} ({bytes} bytes) — the artifact BYGG composed",
                        p.display()
                    ));
                }
                None => lines.push(STANDIN_NOTICE.to_string()),
            }
            lines
        }
        _ => vec![
            format!("[dwarves] ▶ Build & Boot: {cell}"),
            format!("[dwarves] building the appliance PID-1 for {cell}…"),
        ],
    }
}

/// The intro lines a container RUN worker emits before the engine brings the image up.
fn container_intro(cell: &str) -> Vec<String> {
    vec![
        format!("[dwarves] ▶ Run container: {cell}"),
        format!("[dwarves] bringing up the OCI image for {cell} (jera→draupnir) …"),
    ]
}

/// The "now streaming" line, once the console is open — worded per launch kind.
fn running_line(cell: &str, kind: LaunchKind) -> String {
    match kind {
        LaunchKind::Vm => format!("[dwarves] booting {cell} under QEMU/OVMF/KVM…"),
        LaunchKind::Iso => format!("[dwarves] firmware-booting {cell} .iso under QEMU/OVMF…"),
        LaunchKind::Container => format!("[dwarves] following {cell} container logs…"),
    }
}

/// **The guest size an installer boot and its reboot get**, overriding
/// `BootSpec::smoke`'s 512 MiB.
///
/// The whole payload lives in RAM twice on the second boot — korp-start reads the
/// `.znippy` off the partition, extracts it into a memfd and `fexecve`s that — so the
/// floor is set by the product, not by the installer. MEASURED 2026-08-16 driving korp,
/// whose appliance binary is 236 518 296 B packed to 65 395 644 B: the install ran to
/// INSTALL-OK, the reboot reached korp-start's handoff, and the guest then died with
/// `Kernel panic - not syncing: System is deadlocked on memory` at 1.46 s. gunnar
/// (23 MB) and holger (32 MB) fit 512 MiB and never showed it.
///
/// 2 GiB is not a fix for an unbounded payload; it is a number large enough for every
/// appliance in this constellation with the extraction copy on top, on a box where the
/// guest is transient. A payload that outgrows it should say so, and it will — loudly,
/// on the serial, exactly as korp's did.
#[cfg(feature = "vm-boot")]
const INSTALL_GUEST_MB: u32 = 2048;

/// How long a GRAPHICAL boot's pump lingers after a terminal phase, still capturing the
/// framebuffer, to land the appliance's real final screen before returning (the guest
/// stays alive in its hlt loop; a fast INSTALL-OK can beat the QMP screendump warm-up).
const FRAME_GRACE: Duration = Duration::from_secs(8);

/// **The ONE console pump.** Drain `src`'s cumulative serial into `console` line by
/// line as it grows, until the boot passes (BOOT-OK), fails, is cancelled, or times
/// out; set `phase` terminally. Returns `true` iff the boot passed. Kept a free fn
/// over the [`SerialConsole`] trait so the real tunnr boot AND a fake replay drive
/// the SAME pump — a regression that drops a line or swallows the marker goes red.
/// `graphical` = a GPU boot: the pump lingers (up to [`FRAME_GRACE`]) after a terminal
/// phase until a real framebuffer frame is captured, so the pane shows the true screen.
pub fn pump(
    src: &mut dyn SerialConsole,
    kill: &AtomicBool,
    console: &Arc<Mutex<Vec<String>>>,
    phase: &Arc<Mutex<BootPhase>>,
    frame: &Arc<Mutex<Option<Frame>>>,
    timeout: Duration,
    graphical: bool,
) -> bool {
    let start = Instant::now();
    let mut sent = 0usize;
    // Copy the live framebuffer out of the console into the shared slot the pane reads,
    // throttled (the guest's screen changes on human timescales, the QMP capture already
    // paces itself ~2 fps) so a multi-MB frame is not memcpy'd every 20 ms serial poll.
    let mut last_frame_poll = start - Duration::from_secs(1);
    // Latch a terminal phase but DON'T return yet on a graphical boot until a real frame
    // has been captured — a fast install (INSTALL-OK ~1.4s) can finish before the QMP
    // screendump warms up, and the guest stays alive in its hlt loop afterwards, so we
    // keep capturing to show the appliance's REAL final screen (never the placeholder).
    let mut terminal: Option<bool> = None;
    let mut terminal_at = start;
    loop {
        drain_tail(src, &mut sent, console);
        if last_frame_poll.elapsed() >= Duration::from_millis(200) {
            if let Some(fr) = src.frame() {
                *frame.lock().unwrap() = Some(fr);
            }
            last_frame_poll = Instant::now();
        }
        if kill.load(Ordering::SeqCst) {
            src.kill();
            drain_tail(src, &mut sent, console);
            *phase.lock().unwrap() = BootPhase::Failed(cancelled_error("run").to_string());
            return false;
        }
        // Resolve (or re-confirm) the phase. Once terminal, hold the result while a
        // graphical boot waits out the frame-capture grace window.
        if terminal.is_none() {
            match src.phase() {
                BootPhase::Passed => {
                    drain_tail(src, &mut sent, console);
                    *phase.lock().unwrap() = BootPhase::Passed;
                    terminal = Some(true);
                    terminal_at = Instant::now();
                }
                BootPhase::Failed(e) => {
                    drain_tail(src, &mut sent, console);
                    *phase.lock().unwrap() = BootPhase::Failed(e);
                    terminal = Some(false);
                    terminal_at = Instant::now();
                }
                _ => {}
            }
        }
        if let Some(passed) = terminal {
            // A serial boot returns at once. A graphical boot lingers for the WHOLE grace
            // window (guest still in its hlt loop, QMP still up), the loop above copying
            // the latest screendump each tick — so the pane ends on the genuine FINAL
            // screen (the splash), never an early mid-boot frame captured before it drew.
            let grace_over = terminal_at.elapsed() > FRAME_GRACE;
            if !graphical || grace_over {
                return passed;
            }
        }
        if start.elapsed() > timeout {
            src.kill();
            drain_tail(src, &mut sent, console);
            *phase.lock().unwrap() =
                BootPhase::Failed(dwarves::dwarves_err!(launch, 5, "boot timed out").to_string());
            return false;
        }
        std::thread::sleep(Duration::from_millis(20));
    }
}

/// Emit the serial lines that appeared since the last drain (the cumulative-log tail).
fn drain_tail(src: &mut dyn SerialConsole, sent: &mut usize, console: &Arc<Mutex<Vec<String>>>) {
    let full = src.log();
    let lines: Vec<&str> = full.lines().collect();
    if lines.len() > *sent {
        let mut c = console.lock().unwrap();
        for l in &lines[*sent..] {
            c.push((*l).to_string());
        }
        *sent = lines.len();
    }
}

// ── The REAL launcher: boot the appliance through tunnr (feature `vm-boot`). ─────────

/// The real boot launcher — builds the appliance PID-1 and boots it under
/// QEMU/OVMF/KVM through tunnr's `boot_test` primitive, streaming its live serial.
/// Behind the `vm-boot` feature (it links `tunnr-vm`); the no-feature build ships the
/// honest stub below (Build & Boot then reports the feature is off — never a fake boot).
#[cfg(feature = "vm-boot")]
pub struct TunnrBootLauncher;

// ── The installer's PATH answers, in BOTH configurations ────────────────────────
//
// These three are pure `std` path + env math: no `tunnr_vm`, no `fatfs`, no `jera`.
// They sat inside the `vm-boot` impl below purely by placement, and that gate
// CONTRADICTED `installer_root`'s own doc, which calls it "a public seam so a robot
// e2e can read the installed target disk back afterwards" — a seam a default build
// could not reach. `robot_gunnar_installer.rs` is compiled in EVERY build (three of
// its tests are not `#[ignore]`d and are meant to run without QEMU), so the gate was
// a hard `E0599: no associated function named installer_disk_path` on a plain
// `cargo test -p dwarves-gui`, i.e. that whole test binary never compiled.
//
// Ungated `impl` on a type that is declared once per configuration: one writer for
// "where does the installer stage", answered identically whichever half is linked.
impl TunnrBootLauncher {
    /// The per-cell installer staging root
    /// (`<tmp>/dwarves-installer-<cell>-<pid>`) — public seam so a robot e2e can read the
    /// installed target disk back afterwards. The **process id** isolates concurrent boots
    /// of the SAME cell across separate test binaries (cargo runs integration tests in
    /// parallel), so two e2e processes never race on the same `target.raw` (a QEMU write
    /// lock). Read-back stays correct: the test and the boot share the process, so the PID
    /// matches.
    pub fn installer_root(cell_name: &str) -> PathBuf {
        Self::installer_scratch().join(format!(
            "dwarves-installer-{cell_name}-{}",
            std::process::id()
        ))
    }

    /// Where the installer boot's scratch lives. `DWARVES_INSTALLER_SCRATCH` wins;
    /// otherwise the temp dir, as before.
    ///
    /// The knob exists because a composed medium installs onto the DECLARED target size
    /// — gunnar declares 64 GiB — and on this box `/tmp` is a 100 GiB tmpfs, i.e. RAM.
    /// The image is sparse, so only what the installer writes is ever paid for, but a
    /// FAT format of a 47 GiB data partition writes real tables and there is no reason
    /// to hold them in memory. Set it to a path on a real disk for a real install.
    fn installer_scratch() -> PathBuf {
        std::env::var_os("DWARVES_INSTALLER_SCRATCH")
            .map(PathBuf::from)
            .filter(|p| !p.as_os_str().is_empty())
            .unwrap_or_else(std::env::temp_dir)
    }

    /// The blank target disk the installer writes its GPT + install signature onto —
    /// `<installer_root>/target.raw`. A robot e2e reads this back to prove the REAL
    /// on-disk install (GPT/signature), not just the serial claim.
    pub fn installer_disk_path(cell_name: &str) -> PathBuf {
        Self::installer_root(cell_name).join("target.raw")
    }
}

#[cfg(feature = "vm-boot")]
impl TunnrBootLauncher {
    /// Map a launch-cell name to its real bootable appliance (the PID-1 the boot rides).
    fn appliance_for(cell_name: &str) -> Result<tunnr_vm::Appliance, String> {
        Ok(match cell_name {
            "korp-kvm" | "korp" | "korp-iso" => tunnr_vm::Appliance::korp(),
            "demo-container-kvm" | "demo-container" | "demo-container-iso" => {
                tunnr_vm::Appliance::demo_container()
            }
            "tunnr-osless-server" | "tunnr-osless-client" | "tunnr" => tunnr_vm::Appliance::tunnr(),
            other => {
                // The list SHRANK on 2026-08-20 (gap #7): korp-iso / demo-container-iso
                // were named "bootable" here while the scaffold gate refused them, and
                // a COMPOSED iso-kind variant no longer arrives here at all — it boots
                // its own medium through the live leg. This refusal is now only the
                // no-medium case, so it says what actually helps: run BYGG.
                return Err(dwarves::dwarves_err!(
                    launch,
                    7,
                    "no real appliance wired for launch cell '{other}' \
                     (bootable: korp-kvm, demo-container-kvm, tunnr-osless-*; an iso-kind \
                     variant boots the medium BYGG composed — run BYGG first)"
                )
                .to_string());
            }
        })
    }

    /// Fail-safe preconditions shared by the vm + iso boot — name exactly what is
    /// missing (QEMU / OVMF), never a fabricated boot.
    /// **PREFLIGHT** the genuine host runtime deps for the KVM *test view* (the appliance
    /// itself needs none; ISO mastering is toolchain-free via the embedded stub). Emits a
    /// FAT, actionable, NAMED message — the missing tool + the EXACT install command — so
    /// the user instantly knows what to install, never a raw/cryptic failure (brief
    /// §FALLBACK). Checked BEFORE any master/boot work runs.
    fn require_boot_host() -> Result<(), String> {
        if !tunnr_vm::qemu_available() {
            return Err(format!(
                "PREFLIGHT: MISSING QEMU on this host (looked for `{}`) — the KVM test view \
                 needs it. Install with:\n    Debian/Ubuntu: sudo apt install qemu-system-x86\n    \
                 Fedora/RHEL:   sudo dnf install qemu-system-x86\n(or set TUNNR_QEMU_BIN to a \
                 qemu-system-x86_64 binary). The mastered ISO itself needs no host toolchain.",
                tunnr_vm::qemu_binary()
            ));
        }
        if tunnr_vm::locate_ovmf().is_none() {
            return Err(
                "PREFLIGHT: MISSING OVMF/UEFI firmware on this host — the KVM test view boots \
                 under UEFI. Install with:\n    Debian/Ubuntu: sudo apt install ovmf\n    \
                 Fedora/RHEL:   sudo dnf install edk2-ovmf\n(expects OVMF_CODE_4M.fd + \
                 OVMF_VARS_4M.fd under /usr/share/OVMF or /usr/share/edk2)."
                    .to_string(),
            );
        }
        Ok(())
    }

    /// Create a **scratch raw disk** as a sparse file (pure-Rust `set_len`, no
    /// `qemu-img`, no subprocess) and return the `-drive` token attaching it as a
    /// virtio-blk device — the "a disk device always" half of the KVM hardware config.
    fn scratch_disk_arg(cell: &str, mb: u32) -> Result<String, String> {
        let path = std::env::temp_dir().join(format!("dwarves-scratch-{cell}.raw"));
        let f = std::fs::OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(false)
            .open(&path)
            .map_err(|e| format!("scratch disk create {}: {e}", path.display()))?;
        f.set_len((mb as u64) * 1024 * 1024)
            .map_err(|e| format!("scratch disk size {}: {e}", path.display()))?;
        Ok(format!("file={},if=virtio,format=raw", path.display()))
    }

    /// The extra QEMU tokens the hardware config threads into `boot_test` — a scratch
    /// virtio disk (always) and an optional `virtio-gpu-pci` device (the GPU toggle).
    fn hw_qemu_args(cell: &str, hw: &HwConfig) -> Result<Vec<String>, String> {
        let mut args = Vec::new();
        if hw.disk {
            args.push("-drive".into());
            args.push(Self::scratch_disk_arg(cell, hw.disk_mb)?);
        }
        if hw.gpu {
            // A real framebuffer device — the guest gets a GPU. The pane renders its
            // LIVE screen via a QMP `screendump` capture (see `boot_vm`), a genuine
            // frame off this device, never a faked one.
            args.push("-device".into());
            args.push("virtio-gpu-pci".into());
        }
        Ok(args)
    }

    /// The per-cell QMP control-socket path (`<tmp>/dwarves-qmp-<cell>.sock`) — the
    /// channel the [`FramebufferCapture`] drives `screendump` over for a GPU boot.
    fn qmp_sock_path(cell: &str) -> PathBuf {
        std::env::temp_dir().join(format!("dwarves-qmp-{cell}.sock"))
    }

    /// Boot a `kind=vm` appliance under QEMU/OVMF/KVM with the hardware config. For a
    /// GPU boot a QMP control socket is attached and a live [`FramebufferCapture`]
    /// renders the guest's real screen into the pane (the serial console stays live too).
    fn boot_vm(cell_name: &str, hw: &HwConfig) -> Result<Box<dyn SerialConsole>, String> {
        let app = Self::appliance_for(cell_name)?;
        Self::require_boot_host()?;
        let initramfs = app.build_artifact()?;
        let kernel = tunnr_vm::locate_kernel(&std::env::temp_dir())
            .ok_or("no readable kernel — set TUNNR_KERNEL or make /boot/vmlinuz readable")?;
        let mut spec = Self::guest_spec(
            jera::vm::BootSpec::kernel_rootfs(
                Self::guest_name(cell_name),
                path_str(&kernel),
                path_str(&initramfs),
            ),
            &app.boot_ok_marker,
            120,
        );
        spec.cmdline = format!("quiet {}", app.smoke_flag);
        // Thread the KVM hardware config (scratch disk + optional GPU) into the QEMU
        // args — always a disk, a GPU only when the cell's toggle is on.
        spec.extra_qemu_args = Self::hw_qemu_args(cell_name, hw)?;

        // A GPU boot gets a QMP control socket so we can `screendump` its framebuffer.
        // server=on,wait=off ⇒ QEMU creates + listens and does NOT block on a client, so
        // the boot proceeds whether or not the pane is watching.
        let qmp_sock = if hw.gpu {
            let p = Self::qmp_sock_path(cell_name);
            let _ = std::fs::remove_file(&p); // clear a stale socket from a prior run
            spec.extra_qemu_args.push("-qmp".into());
            spec.extra_qemu_args
                .push(format!("unix:{},server=on,wait=off", p.display()));
            Some(p)
        } else {
            None
        };

        let proc = Self::boot_through_jera(&spec, "boot spawn failed")?;
        // Start the live framebuffer capture only for a GPU boot.
        let fb = qmp_sock.map(|sock| FramebufferCapture::start(sock, cell_name.to_string()));
        Ok(Box::new(HandleConsole::new(proc, fb)))
    }

    /// The guest name jera/draupnir know this boot by (the `Machine` id prefix).
    fn guest_name(cell_name: &str) -> String {
        format!("dwarves-{cell_name}")
    }

    /// The pane's guest defaults on a fresh jera spec: the success marker the run
    /// gates on, its wall-clock budget, and the 2 vCPUs every boot here always had
    /// (tunnr's smoke default; jera's `kernel_rootfs` says 1).
    fn guest_spec(
        mut spec: jera::vm::BootSpec,
        marker: &str,
        timeout_secs: u64,
    ) -> jera::vm::BootSpec {
        spec.cores = 2;
        spec.success_marker = marker.to_string();
        spec.timeout_secs = timeout_secs;
        spec
    }

    /// **The one boot call in this file**: hand the spec to jera's VM seam
    /// (`default_vm_runner` = `DraupnirVmBootRunner` under `vm-tunnr` → draupnir's
    /// `KvmBoot` → `tunnr_vm::boot_test`) and get back the live [`jera::boot::BootProc`]
    /// the [`HandleConsole`] drains. `what` names the leg in the spawn error.
    fn boot_through_jera(
        spec: &jera::vm::BootSpec,
        what: &str,
    ) -> Result<Box<dyn jera::boot::BootProc>, String> {
        jera::vm::default_vm_runner()
            .boot(spec)
            .map_err(|e| format!("{what}: {e}"))
    }

    /// Master a bootable ISO around the appliance PID-1 (pure-Rust `PureRustIsoBuilder`,
    /// no `xorriso`) and firmware-boot it under OVMF off `-cdrom`, streaming the SAME
    /// serial console. Reuses the `nornir-build-thing` ISO masterer + tunnr's `-cdrom`
    /// boot path (the proven `iso_boot.rs` primitives).
    ///
    /// Since 2026-08-20 (gap #7) this is the NO-MEDIUM leg only: an iso-kind variant
    /// with a composed medium routes through [`boot_live_iso`](Self::boot_live_iso) and
    /// never arrives here.
    fn boot_iso(cell_name: &str, repo_root: &Path) -> Result<Box<dyn SerialConsole>, String> {
        use nornir_build_thing::iso::{IsoBuild, IsoFormat};
        use nornir_build_thing::{build_iso, PureRustIsoBuilder};

        let app = Self::appliance_for(cell_name)?;

        // HONEST scaffold gate (host-independent, checked FIRST). A *live-boot* ISO
        // (demo-container-iso, korp-iso) needs a UEFI Linux loader INSIDE the ESP
        // (`EFIBOOT.IMG`) — systemd-boot / GRUB EFI / an EFI-stub bzImage — to hand
        // OVMF's firmware control to the kernel+initramfs. No such loader exists in the
        // tree yet: both efistub fixtures are terminal (serial-marker / LBA-0 write), so
        // the pure-Rust masterer would build a UEFI ISO with nothing in the ESP to boot
        // the kernel (that surfaced as the cryptic `boot image 'EFIBOOT.IMG' was not
        // staged` masterer error). We refuse HONESTLY here — a SCAFFOLD, never a faked
        // BOOT-OK. korp-installer ISOs DO boot (their ESP carries a real, self-contained
        // UEFI installer app); korp-kvm boots via QEMU `-kernel` (no ESP needed).
        if iso_cell_is_bootable_scaffold(cell_name) {
            return Err(iso_scaffold_reason(cell_name));
        }

        Self::require_boot_host()?;

        // Build the appliance PID-1 initramfs + locate a kernel, then stage them into an
        // ISO tree the pure-Rust masterer seals into a real `.iso`.
        let initramfs = app.build_artifact()?;
        let kernel = tunnr_vm::locate_kernel(&std::env::temp_dir())
            .ok_or("no readable kernel — set TUNNR_KERNEL or make /boot/vmlinuz readable")?;
        let staging = std::env::temp_dir().join(format!("dwarves-iso-{cell_name}"));
        std::fs::create_dir_all(&staging)
            .map_err(|e| format!("iso staging {}: {e}", staging.display()))?;
        std::fs::copy(&kernel, staging.join("bzImage"))
            .map_err(|e| format!("stage kernel: {e}"))?;
        std::fs::copy(&initramfs, staging.join("rootfs.cpio.gz"))
            .map_err(|e| format!("stage initramfs: {e}"))?;

        let root = if repo_root.as_os_str().is_empty() {
            std::env::temp_dir()
        } else {
            repo_root.to_path_buf()
        };
        let iso = IsoBuild {
            name: cell_name.to_string(),
            kernel: format!("dwarves-iso-{cell_name}/bzImage"),
            rootfs: format!("dwarves-iso-{cell_name}/rootfs.cpio.gz"),
            staging: format!("dwarves-iso-{cell_name}"),
            volume_id: cell_name.to_uppercase().replace('-', "_"),
            boot_params: vec!["console=ttyS0".into(), app.smoke_flag.clone()],
            // UEFI so OVMF firmware-boots it; the ESP payload assembly (efistub) is the
            // feat/pure-rust-iso work — a missing ESP errors honestly rather than faking.
            format: IsoFormat::Uefi,
            from_variant: None,
            boot_logo: None,
            // A SCAFFOLD ISO takes no per-site parameters and therefore records none.
            // These two fields arrived on `IsoBuild` with the `--param` work and this
            // initializer did not follow, because `vm-boot` is off by default and
            // nothing compiled this path — the exact rot a feature nobody builds grows.
            // Empty is the honest value here, not a placeholder: this cell is assembled
            // from a stock kernel + initramfs to prove the boot chain, so there is no
            // declared parameter to accept and no applied value to attest. A DECLARED
            // installer cell (gunnar-installer, korp-installer) goes through
            // `installer_build_for`, which carries the recipe's own params.
            params: Vec::new(),
            applied_params: Vec::new(),
            // Same story for `fixtures` (nornir-build-thing 8331ff2, the declared
            // `[build.iso.fixtures]` map): a scaffold ISO declares none.
            fixtures: Default::default(),
            // …and for the OPT-OUT axis (nornir-build-thing 0.1.8): a scaffold
            // ISO is assembled from a stock kernel + initramfs to prove the boot
            // chain, so there is no capability to leave out and no toggle to
            // record. Empty is the honest value, not a placeholder — and this
            // initializer is why the LAW says to build through the door: the
            // whole path is behind `vm-boot`, which is off by default, so a
            // plain `cargo test` compiles none of it and the field would have
            // gone missing here again.
            options: Vec::new(),
            applied_options: Vec::new(),
            // Every field the model grows after this literal was written (the consumption
            // edges, the kernel donor) takes its default: a live ISO scaffold consumes nothing.
            ..Default::default()
        };
        let base = std::env::temp_dir();
        // Master relative to a temp root so the staging path resolves; land the .iso
        // under <base>/.iso-out/<cell>.iso.
        let out = build_iso(&PureRustIsoBuilder::new(), &iso, cell_name, &base).map_err(|e| {
            format!(
                "pure-Rust ISO master failed for '{cell_name}': {e:#} \
                 (a bootable UEFI ESP payload is the feat/pure-rust-iso work — not faked)"
            )
        })?;
        let _ = root;
        // `Artifact.path` is repo-root-relative (or absolute); resolve it under `base`.
        let iso_path = out
            .artifacts
            .first()
            .map(|a| {
                let p = PathBuf::from(&a.path);
                if p.is_absolute() {
                    p
                } else {
                    base.join(p)
                }
            })
            .unwrap_or_else(|| base.join(".iso-out").join(format!("{cell_name}.iso")));

        // Firmware-boot the .iso: jera's LIVE-medium shape (no kernel, no target disk)
        // is draupnir's `iso_boot`, which tunnr renders `-cdrom … -boot d`.
        let spec = Self::guest_spec(
            jera::vm::BootSpec::live_iso(Self::guest_name(cell_name), path_str(&iso_path)),
            &app.boot_ok_marker,
            180,
        );
        let proc = Self::boot_through_jera(&spec, "iso boot spawn failed")?;
        Ok(Box::new(HandleConsole::new(proc, None)))
    }

    /// Whether `cell_name` names a **disk-installer** ISO cell (korp-installer). An
    /// installer is NOT a live-boot ISO: it masters through
    /// [`nornir_build_thing::InstallerBuild`], attaches a **blank target disk**, and
    /// gates on `INSTALLER: INSTALL-OK` (the install proof) rather than an appliance's
    /// `BOOT-OK`. Routed to [`boot_installer`](Self::boot_installer), never through
    /// `appliance_for` (an installer has no `tunnr_vm::Appliance`).
    fn is_installer_cell(cell_name: &str) -> bool {
        cell_name == "korp-installer"
            || cell_name.ends_with("-installer")
            || cell_name.contains("-installer-")
    }

    /// The [`ContainerBackend`](nornir_build_thing::ContainerBackend) a FAT installer
    /// cell selects, decoded from its name suffix (`korp-installer-fat-youki` → Youki),
    /// or `None` for a **LEAN** installer (`korp-installer` / `…-installer-lean`). This
    /// is the seam that makes the atom matrix REAL: each cell masters a DISTINCT,
    /// content-addressed installer that bakes its own `korp.appliance=…` selector, never
    /// one indistinguishable ISO for every row.
    fn backend_for_cell(cell_name: &str) -> Option<nornir_build_thing::ContainerBackend> {
        use nornir_build_thing::ContainerBackend as B;
        let n = cell_name.to_ascii_lowercase();
        if n.contains("youki") {
            Some(B::Youki)
        } else if n.contains("podman") {
            Some(B::Podman)
        } else if n.contains("containerd") {
            Some(B::Containerd)
        } else if n.contains("microvm") {
            Some(B::MicroVm)
        } else {
            None // lean (or an unqualified korp-installer)
        }
    }

    /// Materialize the [`InstallerBuild`](nornir_build_thing::InstallerBuild) for a
    /// launch cell, **honoring the discovered `[[build.iso]]` descriptor** (its
    /// `volume_id` + declared `boot_params`) and the FAT backend its name selects — so
    /// the ⚒ pane masters the cell the repo actually declared, not a hardcoded stand-in.
    /// A missing/undiscoverable descriptor falls back to name-derived defaults (keeps a
    /// bare inline-TOML cell working). `target_disk_gib` stays small (1) for a fast KVM
    /// proof; the GPU mirrors the cell's hardware toggle.
    fn installer_for_cell(
        cell_name: &str,
        repo_root: &Path,
        hw: &HwConfig,
    ) -> nornir_build_thing::InstallerBuild {
        use nornir_build_thing::{BuildVariantSet, InstallerBuild};

        // The variant's OWN declaration first, and WHOLE. A `[[build.installer]]` lands
        // in `set.installers`, not `set.isos` — reading only the ISO list meant gunnar's
        // declared `volume_id = "GUNNAR_INSTALL"` and its `boot_params` were silently
        // replaced by the name-derived defaults below, and the pane booted a medium the
        // recipe never described. It is CLONED rather than rebuilt field-by-field for
        // the same reason: a cell reassembled from two of its fields drops
        // `target_disk_gib`, `variant`, `cmd`, `env` and every future one silently, and
        // the drop looks exactly like a declaration nobody made. The `[[build.iso]]`
        // lookup stays as the second door (an installer declared as an ISO row), and the
        // derived default stays as the third, for a bare inline-TOML row.
        let set = BuildVariantSet::discover(repo_root).ok().flatten();
        let declared: Option<InstallerBuild> = set.as_ref().and_then(|s| {
            s.installers
                .iter()
                .find(|i| i.name == cell_name)
                .cloned()
                .or_else(|| {
                    s.isos
                        .iter()
                        .find(|i| i.name == cell_name)
                        .map(|i| InstallerBuild {
                            name: i.name.clone(),
                            volume_id: i.volume_id.clone(),
                            boot_params: i.boot_params.clone(),
                            ..Default::default()
                        })
                })
        });

        let mut cell = declared.unwrap_or_else(|| InstallerBuild {
            name: cell_name.to_string(),
            volume_id: cell_name.to_uppercase().replace('-', "_"),
            boot_params: vec!["console=ttyS0".to_string()],
            ..Default::default()
        });
        cell.container_backend = cell
            .container_backend
            .or_else(|| Self::backend_for_cell(cell_name));
        cell.gpu = hw.gpu;
        cell
    }

    /// **The product the installed disk will start** — the `korp.entry=` token off the
    /// cell's own effective boot params, which is what `korp-start` resolves inside the
    /// payload and what it prints as `argv[0]` right before `fexecve`. `None` for a cell
    /// that declares none (the scaffold; `compose_installer_row` refuses to compose one).
    fn entry_of(cell: &nornir_build_thing::InstallerBuild) -> Option<String> {
        cell.effective_boot_params()
            .iter()
            .find_map(|t| t.strip_prefix("korp.entry=").map(str::to_string))
    }

    /// **Master the korp INSTALLER ISO and boot it under QEMU/OVMF with a BLANK target
    /// disk attached, gating on `INSTALLER: INSTALL-OK`.** The in-pane peer of
    /// korp-installer's `tests/kvm_install.rs`, reusing the SAME seams (never twinning
    /// their logic): the ISO is mastered 100% pure-Rust through
    /// [`InstallerBuild::installer_iso`](nornir_build_thing::InstallerBuild::installer_iso)
    /// → [`PureRustIsoBuilder`](nornir_build_thing::PureRustIsoBuilder); the blank disk
    /// is attached via [`InstallerBuild::kvm_disk_args`]; the boot rides tunnr's
    /// `boot_test` primitive (empty kernel ⇒ OVMF firmware-boots the `-cdrom`); and the
    /// success marker is [`InstallerBuild::success_marker`] (`INSTALLER: INSTALL-OK`),
    /// so the SAME [`pump`] latches BOOT PASS the instant the installer reports a done
    /// install. The bootable ESP payload is korp-installer's own UEFI stub fixture,
    /// compiled by the Rust compiler for `x86_64-unknown-uefi` — a missing fixture /
    /// target / QEMU / OVMF surfaces honestly (never a fabricated INSTALL-OK).
    fn boot_installer(
        cell_name: &str,
        repo_root: &Path,
        hw: &HwConfig,
        medium: Option<&Path>,
    ) -> Result<Box<dyn SerialConsole>, String> {
        Self::require_boot_host()?;

        // A per-cell staging root (mirrors kvm_install.rs's tempdir). Deterministic per
        // cell so a robot test can read the installed target disk back.
        let root = Self::installer_root(cell_name);
        std::fs::create_dir_all(&root)
            .map_err(|e| format!("installer staging {}: {e}", root.display()))?;

        let installer = Self::installer_for_cell(cell_name, repo_root, hw);

        // ── WHICH MEDIUM ────────────────────────────────────────────────────────────
        // The one the build composed, when there is one. It is opened here and nowhere
        // else re-mastered: `compose_installer_row` already built the real appliance
        // binary, packed it with `pack_znippy` under the cell's `korp.entry=`, bundled a
        // real kernel, built `korp-boot` + both musl PID-1s and sealed the ISO. Booting
        // anything else after that is booting a different product.
        let composed = medium.filter(|p| p.is_file());
        let (iso_path, disk_bytes) = match composed {
            Some(p) => (p.to_path_buf(), installer.target_disk_bytes()),
            // The SCAFFOLD, and it says so on the console (`STANDIN_NOTICE`) before it
            // boots. Kept reachable because a `[[build.iso]]` scaffold cell and a ▶
            // pressed before any BYGG have nothing else to show — but never silent.
            None => (
                Self::master_standin_iso(cell_name, &root, &installer)?,
                Self::STANDIN_DISK_BYTES_INNER,
            ),
        };

        // A BLANK writable target disk at the DECLARED size (raw sparse, pure Rust — no
        // qemu-img). gunnar declares 64 GiB; the file is sparse, so only the bytes the
        // installer actually writes cost anything.
        let disk_path = Self::installer_disk_path(cell_name);
        Self::create_blank_disk(&disk_path, disk_bytes)?;

        // Boot the ISO with the blank disk attached; gate on INSTALL-OK. jera's INSTALL
        // shape (iso + disk, no kernel) is draupnir's `kvm_install_from_medium`: the
        // blank target is the spec's own image — first virtio drive, so it is `vda` —
        // with `persist_disk` (the installer's writes SURVIVE), and the medium rides
        // `-cdrom … -boot d`. Only the GPU half of the installer's QEMU args is still
        // ours to add (the disk half would be a duplicate `-drive`).
        let disk_str = disk_path
            .to_str()
            .ok_or("target disk path is not valid UTF-8")?;
        let mut spec = Self::guest_spec(
            jera::vm::BootSpec::install_from_medium(
                Self::guest_name(cell_name),
                path_str(&iso_path),
                disk_str,
            ),
            installer.success_marker(),
            300,
        );
        spec.mem_mb = INSTALL_GUEST_MB;
        spec.extra_qemu_args = installer.kvm_gpu_args();

        // **THE COMPOSED MEDIUM RIDES VIRTIO TOO, and that is not belt-and-braces.**
        // OVMF boots El Torito off the `-cdrom`, but the composed medium's PID-1 finds
        // its payloads by probing block devices for the ISO-9660 `CD001` magic, and the
        // appliance kernel carries no ATA/AHCI/SCSI at all — there is no `sr0` in that
        // guest. MEASURED in gunnar's own drive (`installtest/tests/installer_drive.rs`,
        // 2026-08-15): cdrom-only died with `INSTALLER: volume: no block device carries
        // the ISO-9660 magic (CD001) … (probed: loop…, vda)`. The target is attached
        // FIRST (vda) and the ISO second (vdb), read-only, so `hwdetect` can never pick
        // the medium as the install target. Same shape, same writer, as
        // `korp_installer::isoproof::attach_iso_as_virtio` — that harness is a test-only
        // dev-dependency of the products, not something the pane links.
        if composed.is_some() {
            spec.extra_qemu_args.push("-drive".into());
            spec.extra_qemu_args.push(format!(
                "file={},if=virtio,format=raw,readonly=on",
                iso_path.display()
            ));
        }

        // A GPU boot attaches a QMP control socket + a live FramebufferCapture — the SAME
        // graphics-console wiring as `boot_vm`, so the medium's REAL screen renders in the
        // pane, not just serial text. server=on,wait=off ⇒ the boot never blocks on a QMP
        // client.
        let fb = if hw.gpu {
            let sock = Self::qmp_sock_path(cell_name);
            let _ = std::fs::remove_file(&sock);
            spec.extra_qemu_args.push("-qmp".into());
            spec.extra_qemu_args
                .push(format!("unix:{},server=on,wait=off", sock.display()));
            Some(sock)
        } else {
            None
        };

        let proc = Self::boot_through_jera(&spec, "installer boot spawn failed")?;
        let fb = fb.map(|sock| FramebufferCapture::start(sock, cell_name.to_string()));
        let install_console = HandleConsole::new(proc, fb);

        // ★ the owner: after INSTALL-OK, ACTUALLY reboot the guest OFF the installed disk
        // and watch the product come up (default on). The install-only path (reboot off)
        // is the guaranteed-green fallback leg — INSTALL-OK, no second boot, decoupled
        // from any second-boot flakiness.
        if hw.reboot_into_installed {
            let plan = RebootPlan::for_medium(composed.is_some(), &installer);
            Ok(Box::new(RebootConsole::start(
                cell_name,
                install_console,
                disk_path,
                *hw,
                plan,
            )))
        } else {
            Ok(Box::new(install_console))
        }
    }

    /// **Master the korp-installer efistub SCAFFOLD** — the medium there is when the
    /// build produced none.
    ///
    /// It is a real, self-contained UEFI ISO: the committed `korp-installer-efistub`
    /// blob, which EMBEDS its own demo `app.znippy`, streams it onto p2 via Block I/O
    /// and blake3-verifies the read-back. What it is not is any product: it lays korp's
    /// demo payload down whatever cell asked for it, which is why every caller of this
    /// function announces [`STANDIN_NOTICE`] on the console first.
    fn master_standin_iso(
        cell_name: &str,
        root: &Path,
        installer: &nornir_build_thing::InstallerBuild,
    ) -> Result<PathBuf, String> {
        use nornir_build_thing::{build_iso, iso_output_path, PureRustIsoBuilder};

        let efi_app = Self::build_installer_stub(root)?;
        let staging = root.join("iso-root");
        std::fs::create_dir_all(&staging)
            .map_err(|e| format!("installer iso-root {}: {e}", staging.display()))?;
        Self::build_esp(&staging.join("efiboot.img"), &efi_app)?;

        let iso = installer.installer_iso();
        build_iso(&PureRustIsoBuilder::new(), &iso, "korp", root)
            .map_err(|e| format!("pure-Rust installer ISO master failed: {e:#}"))?;
        let iso_path = iso_output_path(root, &iso);
        if !iso_path.is_file() {
            return Err(format!(
                "mastered installer ISO not found at {}",
                iso_path.display()
            ));
        }
        let _ = cell_name;
        Ok(iso_path)
    }

    // ── reboot-into-korp: the SECOND boot off the installed disk (the owner, ★P0) ──────────
    /// The **PRODUCTION first-stage loader** `korp-boot`
    /// (`tunnr/crates/korp-installer/boot/korp-boot-efi`), embedded via `include_bytes!`
    /// (regenerable with `cargo xtask gen-boot-stage1`).
    ///
    /// This is what the reboot flow writes as the installed ESP's `EFI/BOOT/BOOTX64.EFI`,
    /// and it is the whole point of the second boot: until it existed, dwarves laid a
    /// *terminal* stub there — a PE that printed a marker and halted — so the "reboot into
    /// korp" leg proved only that the firmware can start ONE image. The stand-in WAS the
    /// second boot; the chainload it was supposed to stand in for never happened.
    ///
    /// `korp-boot` is the real chain: it locates its own ESP through
    /// `EFI_LOADED_IMAGE_PROTOCOL` (no NVRAM `Boot####` entry needed), reads `\korp\cmdline`,
    /// `LoadImage`s `\vmlinuz` on a device path rooted at THAT ESP (the form that keeps
    /// `DeviceHandle` on the ESP, so a Linux EFI stub can still resolve `initrd=`), installs
    /// the command line as UCS-2 `LoadOptions`, prints `KORP-BOOT: HANDOFF -> kernel` and
    /// `StartImage`s. Every failure is a loud `KORP-BOOT: FAILED …` + halt — it never falls
    /// through to something that resembles success.
    const EMBEDDED_BOOT_STAGE1: &'static [u8] = include_bytes!("embedded/korp-boot-stage1.efi");

    /// The `korp-start` second-boot UEFI payload, **embedded** into the dwarves binary via
    /// `include_bytes!` (regenerable via `cargo xtask gen-start-stub`). The reboot flow lays
    /// THESE bytes onto the installed disk's FAT ESP as **`\vmlinuz`** — the image the
    /// first stage chainloads, standing in for the appliance kernel — so the second boot
    /// needs **zero host toolchain** (the same self-contained charter as the installer
    /// stub). Once started it reads the installed app.znippy back off korp-data, checks the
    /// root-of-trust payload, and emits `APP-ALIVE`.
    const EMBEDDED_START_STUB: &'static [u8] = include_bytes!("embedded/korp-start-bootx64.efi");

    /// The `korp-live` run-from-RAM UEFI stub, **embedded** (regen: `cargo xtask
    /// gen-live-stub`). The live-boot flow lays THESE bytes on a bootable ISO's ESP so the
    /// ISO runs korp straight from RAM (install NOTHING) — deployment type #3 (live-ISO /
    /// airgap / tamper-evident). Same zero-host-toolchain charter as the other stubs.
    const EMBEDDED_LIVE_STUB: &'static [u8] = include_bytes!("embedded/korp-live-bootx64.efi");

    /// The blake3 root-of-trust pin of the demo `app.znippy` the installer streams onto p2
    /// (= `blake3(korp-installer-efistub::APP_ZNIPPY)`; verified equal in the gfx e2e). The
    /// reboot flow writes this as the `korp/app.blake3` FILE on the FAT ESP (the pin the
    /// real korp-start reads on every boot) so the installed ESP carries the honest layout.
    const DEMO_APP_BLAKE3_HEX: &'static str =
        "4236942f847ab29eb0a0f37ce16a3e3f7dbf0622381aeddcf644693771d99442";

    /// The installed ESP's on-disk span, matching the installer efistub's fixed geometry:
    /// p1 (ESP) starts at LBA 34 (after the protective MBR + GPT header + 128-entry array)
    /// and is 1 MiB (2048 × 512-byte sectors). The reboot flow overlays a real FAT ESP over
    /// exactly this span, leaving LBA 0/1/2..33 (MBR/GPT) and p2 (korp-data) intact.
    const ESP_FIRST_LBA: u64 = 34;
    const ESP_SPAN_BYTES: u64 = 1024 * 1024;

    /// Cheap "is this really a UEFI PE?" check (`MZ` … `PE\0\0`) — mirrors
    /// `korp_installer::firststage::is_uefi_pe`. A firmware handed a non-PE simply refuses
    /// to boot with no diagnostic at all, so a corrupt embedded blob must fail LOUDLY here,
    /// on the host, rather than as a black screen in the guest.
    fn is_uefi_pe(bytes: &[u8]) -> bool {
        if bytes.len() < 0x40 || &bytes[..2] != b"MZ" {
            return false;
        }
        let off = u32::from_le_bytes([bytes[0x3C], bytes[0x3D], bytes[0x3E], bytes[0x3F]]) as usize;
        bytes.len() >= off + 4 && &bytes[off..off + 4] == b"PE\0\0"
    }

    /// Build the **FAT EFI System Partition image** the second boot boots off — the
    /// `korp_installer::esp::korp_esp_files` layout, laid pure-Rust with `fatfs`, in
    /// memory, zero shell:
    ///
    /// ```text
    ///   /EFI/BOOT/BOOTX64.EFI  ← korp-boot, the PRODUCTION first stage (what firmware runs)
    ///   /vmlinuz               ← the image the first stage chainloads
    ///   /korp/cmdline          ← the command line it installs as UCS-2 LoadOptions
    ///   /korp/app.blake3       ← the root-of-trust pin korp-start verifies against
    /// ```
    ///
    /// **The fake green this closes.** BOOTX64.EFI used to be the `korp-start` stub itself:
    /// a terminal PE that printed `APP-ALIVE` and halted. The e2e went green on a chain that
    /// was one link long — the stand-in *was* the whole second boot, and the loader it stood
    /// in for did not exist. Now the firmware runs `korp-boot`, and `korp-start` sits BEHIND
    /// it as `\vmlinuz`, so the second boot exercises the real `LoadImage`/`StartImage`
    /// chainload: `KORP-BOOT: chainload \vmlinuz via=device-path` → `HANDOFF -> kernel` →
    /// `APP-ALIVE`. On a real appliance `\vmlinuz` is a Linux EFI-stub kernel whose initramfs
    /// runs `/korp-start`; here the KVM stand-in occupies the kernel slot directly (a second
    /// 755 KiB copy under `/korp-start` would not fit the 1 MiB ESP and would prove nothing
    /// extra — the *chainload*, not the payload, is what was missing).
    fn build_reboot_esp_bytes() -> Result<Vec<u8>, String> {
        use std::io::{Cursor, Write as _};

        let stage1 = Self::EMBEDDED_BOOT_STAGE1;
        if !Self::is_uefi_pe(stage1) {
            return Err(
                "embedded korp-boot-stage1.efi is not a UEFI PE image — the committed \
                 first-stage loader is corrupt; regenerate with `cargo xtask gen-boot-stage1`"
                    .to_string(),
            );
        }
        let start_efi = Self::EMBEDDED_START_STUB;
        if !Self::is_uefi_pe(start_efi) {
            return Err(
                "embedded korp-start bootx64.efi is not a UEFI PE image — the committed stub \
                 is corrupt; regenerate with `cargo xtask gen-start-stub`"
                    .to_string(),
            );
        }
        let need = stage1.len() + start_efi.len();
        if need as u64 >= Self::ESP_SPAN_BYTES {
            return Err(format!(
                "the first stage ({} B) + \\vmlinuz ({} B) do not fit the installed ESP's \
                 {} B span — the installer's p1 geometry and these payloads have drifted",
                stage1.len(),
                start_efi.len(),
                Self::ESP_SPAN_BYTES
            ));
        }

        let mut image = Cursor::new(vec![0u8; Self::ESP_SPAN_BYTES as usize]);
        fatfs::format_volume(
            &mut image,
            fatfs::FormatVolumeOptions::new().volume_label(*b"KORP-ESP   "),
        )
        .map_err(|e| format!("format reboot ESP FAT: {e}"))?;
        {
            let fs = fatfs::FileSystem::new(&mut image, fatfs::FsOptions::new())
                .map_err(|e| format!("mount reboot ESP: {e}"))?;
            {
                let root = fs.root_dir();
                let boot = root
                    .create_dir("EFI")
                    .and_then(|d| d.create_dir("BOOT"))
                    .map_err(|e| format!("reboot ESP EFI/BOOT: {e}"))?;
                // The firmware's removable-media fallback runs THIS: the production first
                // stage, not the payload. It is what makes the next line matter.
                let mut f = boot
                    .create_file("BOOTX64.EFI")
                    .map_err(|e| format!("reboot ESP BOOTX64.EFI: {e}"))?;
                f.write_all(stage1)
                    .map_err(|e| format!("write BOOTX64.EFI: {e}"))?;
                f.flush().ok();
                // …and THIS is what the first stage chainloads off the same ESP.
                let mut k = root
                    .create_file("vmlinuz")
                    .map_err(|e| format!("reboot ESP vmlinuz: {e}"))?;
                k.write_all(start_efi)
                    .map_err(|e| format!("write vmlinuz: {e}"))?;
                k.flush().ok();
                // The pin the real korp-start reads (honest installed-ESP layout).
                let korp = root
                    .create_dir("korp")
                    .map_err(|e| format!("reboot ESP korp/: {e}"))?;
                let mut pin = korp
                    .create_file("app.blake3")
                    .map_err(|e| format!("reboot ESP korp/app.blake3: {e}"))?;
                pin.write_all(Self::DEMO_APP_BLAKE3_HEX.as_bytes())
                    .map_err(|e| format!("write app.blake3: {e}"))?;
                pin.flush().ok();
                let mut cmd = korp
                    .create_file("cmdline")
                    .map_err(|e| format!("reboot ESP korp/cmdline: {e}"))?;
                cmd.write_all(b"console=ttyS0 korp.appliance=lean")
                    .map_err(|e| format!("write cmdline: {e}"))?;
                cmd.flush().ok();
            }
            fs.unmount()
                .map_err(|e| format!("unmount reboot ESP: {e}"))?;
        }
        Ok(image.into_inner())
    }

    /// Overlay the FAT ESP image over the installed disk's p1 span IN PLACE — a seek+write
    /// at `ESP_FIRST_LBA × 512`, leaving the protective MBR/GPT and p2 (korp-data) intact.
    /// After this the disk is a genuinely bootable korp appliance: OVMF's removable-media
    /// fallback (`\EFI\BOOT\BOOTX64.EFI`) runs the `korp-boot` first stage on the next boot,
    /// which chainloads `\vmlinuz` off the same ESP.
    fn overlay_reboot_esp(disk_path: &Path) -> Result<(), String> {
        use std::io::{Seek, SeekFrom, Write as _};
        let esp = Self::build_reboot_esp_bytes()?;
        let mut f = std::fs::OpenOptions::new()
            .read(true)
            .write(true)
            .open(disk_path)
            .map_err(|e| format!("open installed disk {}: {e}", disk_path.display()))?;
        f.seek(SeekFrom::Start(Self::ESP_FIRST_LBA * 512))
            .map_err(|e| format!("seek to p1 ESP offset: {e}"))?;
        f.write_all(&esp)
            .map_err(|e| format!("overlay FAT ESP into p1: {e}"))?;
        f.flush()
            .map_err(|e| format!("flush installed disk: {e}"))?;
        Ok(())
    }

    /// Boot a SECOND QEMU firmware-boot OFF the installed disk (no cdrom): OVMF runs the
    /// `korp-boot` first stage off the FAT ESP, which chainloads `\vmlinuz` → korp comes up
    /// → `APP-ALIVE`. Mirrors the GPU +
    /// QMP wiring of the installer boot so the pane's graphics console shows korp's running
    /// splash. A distinct QMP socket avoids colliding with the (now dead) installer boot.
    fn boot_installed_disk(
        cell_name: &str,
        disk_path: &Path,
        hw: &HwConfig,
        marker: &str,
    ) -> Result<HandleConsole, String> {
        // Empty kernel ⇒ firmware boot; `rootfs_or_disk` is a .raw disk ⇒ build_qemu_args
        // emits ONE `-drive …,if=virtio,format=raw` and OVMF firmware-boots the ESP off it
        // (the removable-media fallback \EFI\BOOT\BOOTX64.EFI = the korp-boot first stage,
        // which then LoadImage/StartImage's \vmlinuz off that same ESP). No cdrom is
        // attached, so this is genuinely "boot off the installed disk".
        // jera's INSTALLED-disk shape (disk, no kernel, no medium) is draupnir's
        // `kvm_boot_installed_disk`: persisted, `-boot c`, no cdrom.
        let mut spec = Self::guest_spec(
            jera::vm::BootSpec::installed_disk(
                format!("{}-reboot", Self::guest_name(cell_name)),
                path_str(disk_path),
            ),
            marker,
            240,
        );
        spec.mem_mb = INSTALL_GUEST_MB;
        let fb_sock = if hw.gpu {
            let sock = Self::qmp_sock_path(&format!("{cell_name}-reboot"));
            let _ = std::fs::remove_file(&sock);
            spec.extra_qemu_args.push("-device".into());
            spec.extra_qemu_args.push("virtio-gpu-pci".into());
            spec.extra_qemu_args.push("-qmp".into());
            spec.extra_qemu_args
                .push(format!("unix:{},server=on,wait=off", sock.display()));
            Some(sock)
        } else {
            None
        };
        let proc = Self::boot_through_jera(&spec, "reboot-into-installed boot spawn failed")?;
        let fb = fb_sock.map(|sock| FramebufferCapture::start(sock, format!("{cell_name}-reboot")));
        Ok(HandleConsole::new(proc, fb))
    }

    // ── live-ISO run-from-RAM (deployment type #3, the owner 2b) ───────────────────────────
    /// Whether a cell is a **live-ISO** run-from-RAM cell (`korp-live`, `*-live-iso`) — it
    /// boots the ISO's ESP payload straight from RAM and installs NOTHING (no target disk),
    /// the ephemeral / airgap / tamper-evident deployment. Distinct from an installer cell.
    fn is_live_cell(cell_name: &str) -> bool {
        cell_name.contains("-live")
    }

    /// **Firmware-boot a LIVE bootable ISO run-from-RAM.** The self-contained twin of
    /// [`boot_installer`](Self::boot_installer), and since 2026-08-20 (gap #7) ALSO the
    /// leg every composed iso-kind variant boots through:
    ///
    /// - `medium = Some(file)` — **THE COMPOSED MEDIUM boots, verbatim.** BYGG's
    ///   isocompose output is already a self-contained El Torito ESP ISO (app.znippy
    ///   pinned, kernel bundled, ESP mastered), so nothing is re-mastered here — booting
    ///   anything else would be booting a different product. The gate is korp-start's
    ///   own exec-argv handoff ([`composed_handoff_marker`]), naming the declared
    ///   `korp.entry=` when there is one.
    /// - `medium = None` — the stand-in: stage the embedded `korp-live` UEFI stub as
    ///   the ISO's ESP (`build_esp` → `efiboot.img`), master the SAME pure-Rust UEFI
    ///   ISO ([`InstallerBuild::installer_iso`] is generic over the ESP contents), and
    ///   gate on `APP-ALIVE` (korp came up, live).
    ///
    /// Either way it firmware-boots under OVMF off `-cdrom` with **NO target disk** —
    /// nothing is installed — with the SAME optional virtio-gpu + QMP graphics console.
    /// A missing QEMU/OVMF surfaces via the preflight.
    /// Wire the live-ISO leg's guest networking for the picked ssh forward: suppress
    /// tunnr's default plain user NIC (its `NetConfig::User { hostfwd }` seam spells
    /// `tcp::<h>-:<g>` — all interfaces, no bind address) and author the SAME
    /// user-mode virtio-net netdev with a LOOPBACK-bound hostfwd instead
    /// ([`live_ssh_netdev_args`]). Folding a bind address into tunnr's `NetConfig` is
    /// the named follow-up; until then this leg owns its netdev line. The seam the
    /// wiring test pins without QEMU.
    fn wire_live_ssh_forward(spec: &mut jera::vm::BootSpec, host_port: u16) {
        spec.net_none = true;
        spec.extra_qemu_args.extend(live_ssh_netdev_args(host_port));
    }

    fn boot_live_iso(
        cell_name: &str,
        repo_root: &Path,
        hw: &HwConfig,
        medium: Option<&Path>,
    ) -> Result<Box<dyn SerialConsole>, String> {
        use nornir_build_thing::{build_iso, iso_output_path, PureRustIsoBuilder};

        Self::require_boot_host()?;

        let (iso_path, marker) = match medium.filter(|p| p.is_file()) {
            Some(composed) => {
                // The medium BYGG composed — opened here and nowhere re-mastered. The
                // declaration's `korp.entry=` (read off the variant's own descriptor)
                // names what korp-start must hand control to.
                let cell = Self::installer_for_cell(cell_name, repo_root, hw);
                let marker = composed_handoff_marker(Self::entry_of(&cell).as_deref());
                (composed.to_path_buf(), marker)
            }
            None => {
                let root = Self::installer_root(cell_name);
                std::fs::create_dir_all(&root)
                    .map_err(|e| format!("live iso staging {}: {e}", root.display()))?;

                // 1. Materialize the embedded korp-live stub as the ESP boot payload
                //    (MZ-checked).
                let bytes = Self::EMBEDDED_LIVE_STUB;
                if bytes.len() < 2 || &bytes[0..2] != b"MZ" {
                    return Err(
                        "embedded korp-live bootx64.efi is not a PE (MZ) image — regenerate \
                         with `cargo xtask gen-live-stub`"
                            .to_string(),
                    );
                }
                let efi_app = root.join("bootx64.efi");
                std::fs::write(&efi_app, bytes).map_err(|e| {
                    format!("write embedded live stub to {}: {e}", efi_app.display())
                })?;

                // 2. Stage the ISO tree: the ESP boot payload (no app.znippy — run-from-RAM).
                let staging = root.join("iso-root");
                std::fs::create_dir_all(&staging)
                    .map_err(|e| format!("live iso-root {}: {e}", staging.display()))?;
                Self::build_esp(&staging.join("efiboot.img"), &efi_app)?;

                // 3. Master the UEFI ISO for the discovered variant (its boot_params bake
                //    korp.mode=live).
                let installer = Self::installer_for_cell(cell_name, repo_root, hw);
                let iso = installer.installer_iso();
                build_iso(&PureRustIsoBuilder::new(), &iso, "korp", &root)
                    .map_err(|e| format!("pure-Rust live ISO master failed: {e:#}"))?;
                let iso_path = iso_output_path(&root, &iso);
                if !iso_path.is_file() {
                    return Err(format!(
                        "mastered live ISO not found at {}",
                        iso_path.display()
                    ));
                }
                (iso_path, "APP-ALIVE".to_string())
            }
        };

        // 4. Firmware-boot the ISO off -cdrom with NO target disk; gate on the marker
        //    (jera's LIVE-medium shape → draupnir `iso_boot`).
        let mut spec = Self::guest_spec(
            jera::vm::BootSpec::live_iso(
                format!("{}-live", Self::guest_name(cell_name)),
                path_str(&iso_path),
            ),
            &marker,
            120,
        );
        // The ssh forward the spawn picked and announced: the guest's sshd (gunnar's
        // convention, 0.0.0.0:2222) becomes dialable at 127.0.0.1:<p> on the host, so
        // the operator can `git clone` INTO the live-booted appliance off the pane.
        if let Some(p) = hw.ssh_fwd_host_port {
            Self::wire_live_ssh_forward(&mut spec, p);
        }
        if medium.is_some() {
            // A composed medium runs the product from RAM — the packed payload plus its
            // memfd extraction copy — so it gets the same guest floor as the installer
            // legs (see INSTALL_GUEST_MB: korp panicked at 512 MiB, MEASURED 2026-08-16).
            spec.mem_mb = INSTALL_GUEST_MB;
        }
        let fb = if hw.gpu {
            let sock = Self::qmp_sock_path(&format!("{cell_name}-live"));
            let _ = std::fs::remove_file(&sock);
            spec.extra_qemu_args.push("-device".into());
            spec.extra_qemu_args.push("virtio-gpu-pci".into());
            spec.extra_qemu_args.push("-qmp".into());
            spec.extra_qemu_args
                .push(format!("unix:{},server=on,wait=off", sock.display()));
            Some(sock)
        } else {
            None
        };
        let proc = Self::boot_through_jera(&spec, "live iso boot spawn failed")?;
        let fb = fb.map(|sock| FramebufferCapture::start(sock, cell_name.to_string()));
        Ok(Box::new(HandleConsole::new(proc, fb)))
    }

    /// The blank target the SCAFFOLD installs onto. The efistub stub writes a GPT +
    /// install signature and nothing else, so a declared 64 GiB would be 64 GiB of
    /// nothing; the composed medium uses `target_disk_bytes()` instead.
    const STANDIN_DISK_BYTES_INNER: u64 = 256 * 1024 * 1024;

    /// The korp-installer UEFI stub, **embedded** into the dwarves binary via
    /// `include_bytes!` — a `bootx64.efi` (PE32+ EFI application) pre-built ONCE from the
    /// `korp-installer-efistub` fixture and committed, regenerable via
    /// `cargo xtask gen-installer-stub`. Mastering the installer ISO writes THESE BYTES —
    /// so it needs **zero host toolchain**: no `rustup target add x86_64-unknown-uefi`, no
    /// `cargo build --target`, no shell, on ANY box (incl. the customer's). This is the
    /// self-contained charter (brief §SELF-CONTAINED): the only genuine runtime deps are
    /// QEMU + OVMF for the KVM *test view*, checked by an actionable preflight, never a
    /// raw compiler error.
    const EMBEDDED_INSTALLER_STUB: &'static [u8] =
        include_bytes!("embedded/korp-installer-bootx64.efi");

    /// Materialize the embedded installer stub as a `bootx64.efi` file under `root` (so
    /// [`build_esp`](Self::build_esp) can place it on the ESP). Pure byte-write — no
    /// toolchain, no cargo, no shell. The embedded blob is a valid PE32+ EFI application
    /// (`MZ` header), verified here so a corrupt embed fails loudly, never a faked boot.
    fn build_installer_stub(root: &Path) -> Result<PathBuf, String> {
        let bytes = Self::EMBEDDED_INSTALLER_STUB;
        if bytes.len() < 2 || &bytes[0..2] != b"MZ" {
            return Err(
                "embedded korp-installer bootx64.efi is not a PE (MZ) image — the committed \
                 stub is corrupt; regenerate with `cargo xtask gen-installer-stub`"
                    .to_string(),
            );
        }
        let efi = root.join("bootx64.efi");
        std::fs::write(&efi, bytes)
            .map_err(|e| format!("write embedded installer stub to {}: {e}", efi.display()))?;
        Ok(efi)
    }

    /// Format a FAT EFI System Partition carrying `EFI/BOOT/BOOTX64.EFI` — pure-Rust
    /// `fatfs`, zero shell (mirrors `kvm_install.rs::build_esp`). This is the ESP the
    /// UEFI ISO's `efiboot.img` boot image points at.
    fn build_esp(path: &Path, efi_app: &Path) -> Result<(), String> {
        use std::io::Write as _;
        let f = std::fs::OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .truncate(true)
            .open(path)
            .map_err(|e| format!("create ESP {}: {e}", path.display()))?;
        f.set_len(1440 * 1024)
            .map_err(|e| format!("size ESP: {e}"))?;
        fatfs::format_volume(
            &f,
            fatfs::FormatVolumeOptions::new().volume_label(*b"EFIESP     "),
        )
        .map_err(|e| format!("format FAT ESP: {e}"))?;
        let fs = fatfs::FileSystem::new(&f, fatfs::FsOptions::new())
            .map_err(|e| format!("mount ESP: {e}"))?;
        let boot = fs
            .root_dir()
            .create_dir("EFI")
            .and_then(|d| d.create_dir("BOOT"))
            .map_err(|e| format!("ESP EFI/BOOT: {e}"))?;
        let mut out = boot
            .create_file("BOOTX64.EFI")
            .map_err(|e| format!("ESP BOOTX64.EFI: {e}"))?;
        let bytes = std::fs::read(efi_app).map_err(|e| format!("read efi payload: {e}"))?;
        out.write_all(&bytes)
            .map_err(|e| format!("write BOOTX64.EFI: {e}"))?;
        out.flush().map_err(|e| format!("flush BOOTX64.EFI: {e}"))?;
        Ok(())
    }

    /// Create a blank, sparse, writable raw disk image of `size` bytes — pure-Rust
    /// (`File::set_len`), no `qemu-img` (mirrors `kvm_install.rs::create_blank_disk`).
    fn create_blank_disk(path: &Path, size: u64) -> Result<(), String> {
        let f = std::fs::OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(true)
            .open(path)
            .map_err(|e| format!("create blank disk {}: {e}", path.display()))?;
        f.set_len(size)
            .map_err(|e| format!("size blank disk: {e}"))?;
        Ok(())
    }
}

#[cfg(feature = "vm-boot")]
impl BootLauncher for TunnrBootLauncher {
    fn launch(&self, cell_name: &str, repo_root: &Path) -> Result<Box<dyn SerialConsole>, String> {
        // The one router is `launch_hw` — this is the default-hardware, no-medium door.
        self.launch_hw(cell_name, repo_root, &HwConfig::default(), None)
    }

    fn launch_hw(
        &self,
        cell_name: &str,
        repo_root: &Path,
        hw: &HwConfig,
        medium: Option<&Path>,
    ) -> Result<Box<dyn SerialConsole>, String> {
        // A LIVE cell (korp-live) firmware-boots run-from-RAM (no disk); an INSTALLER
        // variant boots its composed medium (or the self-announcing stand-in) with a
        // BLANK target disk, gating on INSTALL-OK; an ISO-KIND variant boots the medium
        // BYGG composed through the SAME live leg ([`plan_iso_launch`], gap #7) and only
        // falls to the honest stand-in refusal when there is no medium; everything else
        // is a vm boot.
        if Self::is_live_cell(cell_name) {
            Self::boot_live_iso(cell_name, repo_root, hw, medium)
        } else if Self::is_installer_cell(cell_name) {
            Self::boot_installer(cell_name, repo_root, hw, medium)
        } else if cell_name.ends_with("-iso") {
            match plan_iso_launch(medium) {
                IsoLaunchPlan::LiveComposed { medium } => {
                    Self::boot_live_iso(cell_name, repo_root, hw, Some(&medium))
                }
                IsoLaunchPlan::Standin => Self::boot_iso(cell_name, repo_root),
            }
        } else {
            Self::boot_vm(cell_name, hw)
        }
    }
}

/// Adapts jera's live [`jera::boot::BootProc`] (the draupnir→tunnr KVM proc) to a
/// [`SerialConsole`] (feature `vm-boot`): `drain_log` is pumped into ONE cumulative
/// console string on every read, `poll` is the phase. For a GPU (graphical) boot it
/// also owns a live [`FramebufferCapture`] — a QMP `screendump` loop rendering the
/// guest's real screen — surfaced through [`frame`](SerialConsole::frame).
#[cfg(feature = "vm-boot")]
struct HandleConsole {
    /// The live boot, behind a mutex because [`SerialConsole`] reads through `&self`
    /// while jera's proc polls/drains through `&mut`.
    proc: Mutex<Box<dyn jera::boot::BootProc>>,
    /// The console so far — every line `drain_log` has handed out, newline-joined.
    log: Mutex<String>,
    /// The GPU framebuffer capture (graphical boots only); `None` for serial cells.
    fb: Option<FramebufferCapture>,
}

#[cfg(feature = "vm-boot")]
impl HandleConsole {
    fn new(proc: Box<dyn jera::boot::BootProc>, fb: Option<FramebufferCapture>) -> Self {
        HandleConsole {
            proc: Mutex::new(proc),
            log: Mutex::new(String::new()),
            fb,
        }
    }

    /// Move the fresh console lines off the proc into the cumulative log.
    fn pump(&self) {
        let lines = self.proc.lock().unwrap().drain_log();
        if lines.is_empty() {
            return;
        }
        let mut log = self.log.lock().unwrap();
        for line in lines {
            log.push_str(&line);
            log.push('\n');
        }
    }

    /// **Let the guest finish writing before anything reads its disk.**
    ///
    /// MEASURED 2026-08-16, gunnar, and it is the reason this exists: the install ran
    /// to INSTALL-OK and streamed the payload onto p2 with `blake3=e3b150c78915…`, the
    /// installer QEMU was SIGKILLed 4 s later, and the SECOND boot staged the same
    /// 8 627 789 bytes back off `/dev/vda2` and hashed them to `b92123d1…` — same
    /// length, different bytes, and the startup self-test refused the disk. QEMU's raw
    /// drive is `cache=writeback`: guest writes sit in QEMU's own memory and a SIGKILL
    /// throws them away. Three earlier runs were simply lucky.
    ///
    /// So the install guest is asked to STOP rather than shot. `shutdown` returns as
    /// soon as the process is gone — which for an installer is at once, because it
    /// reboots itself after INSTALL-OK and tunnr passes `-no-reboot`, so QEMU exits and
    /// flushes on its own — and falls back to ACPI power-down and then SIGKILL if the
    /// guest is wedged. The KVM is always gone when this returns.
    fn settle(&self, grace: Duration) {
        if let Some(fb) = &self.fb {
            fb.stop();
        }
        self.pump();
        self.proc.lock().unwrap().shutdown(grace);
        self.pump();
    }
}

#[cfg(feature = "vm-boot")]
impl SerialConsole for HandleConsole {
    fn log(&self) -> String {
        self.pump();
        self.log.lock().unwrap().clone()
    }
    fn phase(&self) -> BootPhase {
        use jera::boot::BootStatus;
        self.pump();
        match self.proc.lock().unwrap().poll() {
            BootStatus::BootedOk => BootPhase::Passed,
            BootStatus::Booting | BootStatus::Running => BootPhase::Booting,
            // tunnr's own words ride through draupnir → jera: "boot timed out" for the
            // wall-clock backstop, the failure reason for a failed boot.
            BootStatus::Failed(e) => BootPhase::Failed(e),
            BootStatus::Killed => BootPhase::Failed("boot killed".to_string()),
        }
    }
    fn kill(&mut self) {
        if let Some(fb) = &self.fb {
            fb.stop();
        }
        self.pump();
        self.proc.lock().unwrap().kill();
        self.pump();
    }
    fn frame(&self) -> Option<Frame> {
        self.fb.as_ref().and_then(|fb| fb.latest())
    }
}

// ── reboot-into-korp: the two-phase console (install → reboot → APP-ALIVE) ────────────

/// How long the reboot worker keeps the second (installed) boot ALIVE + capturing frames
/// after it reaches `APP-ALIVE`, so the pane lands korp's running splash (overlaps the
/// outer pump's [`FRAME_GRACE`]). Torn down early on cancel/drop.
#[cfg(feature = "vm-boot")]
const REBOOT_POST_PASS_LINGER: Duration = Duration::from_secs(22);

/// **The reboot-into-korp console** (the owner, ★P0): a [`SerialConsole`] that drives the
/// WHOLE two-phase story on its own worker thread — (1) the installer boot to
/// `INSTALLER: INSTALL-OK`, then (2) lay a real FAT ESP (`korp-start`) onto the installed
/// disk and boot a SECOND QEMU firmware-boot OFF that disk, gating on `APP-ALIVE` — while
/// presenting ONE cumulative console (install log → reboot narration → second-boot log)
/// and ONE phase that latches `Passed` only when korp actually comes up. The pane/pump
/// drain it exactly like any other console; a robot e2e latches the `APP-ALIVE` heartbeat.
#[cfg(feature = "vm-boot")]
struct RebootConsole {
    log: Arc<Mutex<String>>,
    phase: Arc<Mutex<BootPhase>>,
    frame: Arc<Mutex<Option<Frame>>>,
    kill: Arc<AtomicBool>,
    _worker: Option<std::thread::JoinHandle<()>>,
}

/// **What the SECOND boot is, and it is two different things.**
///
/// A composed medium installs a whole boot chain of its own: a real `korp-boot` as
/// `\EFI\BOOT\BOOTX64.EFI`, a real EFI-stub `vmlinuz`, a real `korp-start`, the packed
/// payload on p2, the cmdline carrying `korp.entry=<product>`. Booting off that disk is
/// the thing the owner asked to be verified, and the ONLY thing the second boot has to do
/// is get out of the way.
///
/// The scaffold installs korp's demo payload, and dwarves lays its own embedded ESP over
/// p1 to give the firmware something to chainload. That overlay is a raw seek+write over
/// LBA 34 for 1 MiB — the efistub's fixed geometry — so pointing it at a REAL installed
/// disk destroys the ESP the installer just wrote and boots dwarves' own korp fixture
/// instead, which is how `APP-ALIVE {"app":"korp",…}` came back for gunnar and for
/// holger alike. The overlay is therefore bound to the scaffold and to nothing else.
#[cfg(feature = "vm-boot")]
#[derive(Clone, Debug, PartialEq, Eq)]
struct RebootPlan {
    /// Lay dwarves' embedded FAT ESP over p1 before booting. Scaffold only.
    overlay_standin_esp: bool,
    /// The marker the second boot must reach for the run to pass.
    marker: String,
    /// The console line naming what is about to boot.
    narration: String,
}

#[cfg(feature = "vm-boot")]
impl RebootPlan {
    /// The plan for a real composed medium, or for the scaffold.
    ///
    /// The real-medium marker is `korp-start`'s own `exec argv: ["<entry>"` line
    /// (`korp-installer/src/bin/korp-start.rs:224`, argv[0] from
    /// `cmdline::app_argv(entry, …)`). It is the last thing printed before `fexecve`
    /// replaces the process, so reaching it proves the whole installed chain ran —
    /// firmware → korp-boot → vmlinuz → korp-start → payload verified — AND it NAMES the
    /// product. `APP-ALIVE` cannot: the only things in the tree that print it are korp's
    /// own `korp-appliance` and the KVM fixture stubs, both with `"korp"` as a hardcoded
    /// literal, so gating gunnar's reboot on it gates gunnar on korp.
    fn for_medium(composed: bool, cell: &nornir_build_thing::InstallerBuild) -> RebootPlan {
        if !composed {
            return RebootPlan {
                overlay_standin_esp: true,
                marker: "APP-ALIVE".to_string(),
                narration: "[dwarves] ── reboot (STAND-IN): overlaying dwarves' own korp ESP \
                            onto p1 and booting the korp demo fixture — NOT this product ──"
                    .to_string(),
            };
        }
        // The marker itself is [`composed_handoff_marker`] — one spelling, shared with
        // the iso-kind live boot, so the two composed gates cannot drift.
        match TunnrBootLauncher::entry_of(cell) {
            Some(entry) => RebootPlan {
                overlay_standin_esp: false,
                marker: composed_handoff_marker(Some(&entry)),
                narration: format!(
                    "[dwarves] ── reboot: booting OFF the installed disk (cdrom detached, \
                     firmware walks to the ESP the installer wrote); waiting for korp-start \
                     to hand off to `{entry}` ──"
                ),
            },
            // A composed medium always declares `korp.entry=` (compose_installer_row
            // refuses without it). If one ever does not, gate on the handoff itself
            // rather than on a product name we would have to invent.
            None => RebootPlan {
                overlay_standin_esp: false,
                marker: composed_handoff_marker(None),
                narration: "[dwarves] ── reboot: booting OFF the installed disk; the cell \
                            declares no korp.entry=, so the gate is the handoff itself ──"
                    .to_string(),
            },
        }
    }
}

#[cfg(feature = "vm-boot")]
impl RebootConsole {
    /// Take the live installer boot + start the reboot worker. Non-blocking: returns at
    /// once with a console the pump immediately begins draining (phase `Booting`).
    fn start(
        cell: &str,
        install: HandleConsole,
        disk_path: PathBuf,
        hw: HwConfig,
        plan: RebootPlan,
    ) -> RebootConsole {
        let log = Arc::new(Mutex::new(String::new()));
        let phase = Arc::new(Mutex::new(BootPhase::Booting));
        let frame = Arc::new(Mutex::new(None));
        let kill = Arc::new(AtomicBool::new(false));
        let (l, p, fr, k, cell_s) = (
            log.clone(),
            phase.clone(),
            frame.clone(),
            kill.clone(),
            cell.to_string(),
        );
        let worker = std::thread::Builder::new()
            .name(format!("dwarves-reboot-{cell}"))
            .spawn(move || reboot_worker(&cell_s, install, disk_path, hw, plan, &l, &p, &fr, &k))
            .ok();
        RebootConsole {
            log,
            phase,
            frame,
            kill,
            _worker: worker,
        }
    }
}

#[cfg(feature = "vm-boot")]
impl SerialConsole for RebootConsole {
    fn log(&self) -> String {
        self.log.lock().unwrap().clone()
    }
    fn phase(&self) -> BootPhase {
        self.phase.lock().unwrap().clone()
    }
    fn kill(&mut self) {
        self.kill.store(true, Ordering::SeqCst);
    }
    fn frame(&self) -> Option<Frame> {
        self.frame.lock().unwrap().clone()
    }
}

#[cfg(feature = "vm-boot")]
impl Drop for RebootConsole {
    fn drop(&mut self) {
        // Signal the worker to tear down whichever QEMU is live (never leak a guest).
        self.kill.store(true, Ordering::SeqCst);
    }
}

/// A path as the `String` jera's spec carries (lossy only for a non-UTF-8 path, which
/// no staging dir here ever is).
#[cfg(feature = "vm-boot")]
fn path_str(p: &Path) -> String {
    p.to_string_lossy().into_owned()
}

/// Poll a live [`HandleConsole`] until its guest is terminally gone (Killed/Failed/etc.),
/// up to `budget` — so the file lock on the installed disk is released before the reboot
/// overlays a FAT ESP into it / a second QEMU opens it.
#[cfg(feature = "vm-boot")]
fn wait_terminated(con: &HandleConsole, budget: Duration) {
    let deadline = Instant::now() + budget;
    while Instant::now() < deadline {
        if !con.phase().is_running() {
            break;
        }
        std::thread::sleep(Duration::from_millis(80));
    }
    // A small settle so the kernel finishes reaping + releasing the OFD file lock.
    std::thread::sleep(Duration::from_millis(300));
}

/// The reboot worker: phase-1 installer boot → INSTALL-OK, then phase-2 boot off the
/// installed disk → APP-ALIVE, streaming ONE cumulative log. Feature `vm-boot`.
#[cfg(feature = "vm-boot")]
#[allow(clippy::too_many_arguments)]
fn reboot_worker(
    cell: &str,
    mut install: HandleConsole,
    disk_path: PathBuf,
    hw: HwConfig,
    plan: RebootPlan,
    log: &Arc<Mutex<String>>,
    phase: &Arc<Mutex<BootPhase>>,
    frame: &Arc<Mutex<Option<Frame>>>,
    kill: &Arc<AtomicBool>,
) {
    // ── phase 1: drain the installer boot until INSTALL-OK (Passed) or failure. ──
    let install_deadline = Instant::now() + Duration::from_secs(420);
    loop {
        if kill.load(Ordering::SeqCst) {
            install.kill();
            *phase.lock().unwrap() = BootPhase::Failed(cancelled_error("run").to_string());
            return;
        }
        *log.lock().unwrap() = install.log();
        if let Some(f) = install.frame() {
            *frame.lock().unwrap() = Some(f);
        }
        match install.phase() {
            BootPhase::Passed => break, // INSTALLER: INSTALL-OK reached
            BootPhase::Failed(e) => {
                *phase.lock().unwrap() = BootPhase::Failed(e);
                return;
            }
            _ => {}
        }
        if Instant::now() > install_deadline {
            install.kill();
            *phase.lock().unwrap() = BootPhase::Failed(
                dwarves::dwarves_err!(launch, 5, "install timed out").to_string(),
            );
            return;
        }
        std::thread::sleep(Duration::from_millis(50));
    }

    // Freeze the install log, tear down the installer QEMU (release the disk lock), and
    // narrate the reboot. The cumulative log from here = install_log + reboot lines + boot.
    let mut prelude = install.log();
    if !prelude.ends_with('\n') {
        prelude.push('\n');
    }
    install.settle(Duration::from_secs(30));
    wait_terminated(&install, Duration::from_secs(4));
    prelude.push('\n');
    prelude.push_str(&plan.narration);
    prelude.push('\n');
    *log.lock().unwrap() = prelude.clone();

    // Overlay dwarves' own FAT ESP — SCAFFOLD ONLY. Over a real installed disk this
    // would destroy the ESP the installer just wrote (see `RebootPlan`).
    if plan.overlay_standin_esp {
        prelude.push_str(
            "[dwarves] laying the stand-in FAT ESP onto the installed disk's p1 \
             (EFI/BOOT/BOOTX64.EFI = korp-boot stage 1 → chainloads \\vmlinuz) …\n",
        );
        if let Err(e) = TunnrBootLauncher::overlay_reboot_esp(&disk_path) {
            prelude.push_str(&format!("[dwarves] reboot FAILED: {e}\n"));
            *log.lock().unwrap() = prelude;
            *phase.lock().unwrap() = BootPhase::Failed(format!("reboot: {e}"));
            return;
        }
    }
    // The gate is NOT echoed here. The console log is what an operator and a robot both
    // read, and a narration line that quotes the marker makes every gate look already
    // reached — MEASURED 2026-08-16: the drive latched "the machine rebooted" off THIS
    // line, on a run whose second boot never happened.
    prelude.push_str("[dwarves] booting the installed disk under QEMU/OVMF (firmware boot) …\n");
    *log.lock().unwrap() = prelude.clone();

    // ── phase 2: boot off the installed disk, gate on the plan's marker. ──
    let mut boot = match TunnrBootLauncher::boot_installed_disk(cell, &disk_path, &hw, &plan.marker)
    {
        Ok(b) => b,
        Err(e) => {
            prelude.push_str(&format!("[dwarves] reboot boot spawn FAILED: {e}\n"));
            *log.lock().unwrap() = prelude;
            *phase.lock().unwrap() = BootPhase::Failed(format!("reboot: {e}"));
            return;
        }
    };

    let boot_deadline = Instant::now() + Duration::from_secs(260);
    let mut resolved_at: Option<Instant> = None;
    loop {
        if kill.load(Ordering::SeqCst) {
            boot.kill();
            if phase.lock().unwrap().is_running() {
                *phase.lock().unwrap() = BootPhase::Failed(cancelled_error("run").to_string());
            }
            return;
        }
        *log.lock().unwrap() = format!("{prelude}{}", boot.log());
        if let Some(f) = boot.frame() {
            *frame.lock().unwrap() = Some(f);
        }
        match resolved_at {
            None => match boot.phase() {
                BootPhase::Passed => {
                    *phase.lock().unwrap() = BootPhase::Passed;
                    resolved_at = Some(Instant::now());
                }
                BootPhase::Failed(e) => {
                    boot.kill();
                    *phase.lock().unwrap() = BootPhase::Failed(format!("reboot: {e}"));
                    return;
                }
                _ => {
                    if Instant::now() > boot_deadline {
                        boot.kill();
                        *phase.lock().unwrap() = BootPhase::Failed(format!(
                            "reboot: the second boot never reached `{}`",
                            plan.marker
                        ));
                        return;
                    }
                }
            },
            Some(t) => {
                // korp is up (APP-ALIVE). Keep the guest alive + capturing frames a while so
                // the pane lands the running splash, then tear it down (or on cancel/drop).
                if t.elapsed() > REBOOT_POST_PASS_LINGER {
                    boot.kill();
                    return;
                }
            }
        }
        std::thread::sleep(Duration::from_millis(50));
    }
}

// ── The LIVE GPU framebuffer: QMP `screendump` → egui texture (feature `vm-boot`). ────

/// A live GPU framebuffer capture: connects to a booting guest's **QMP** control socket
/// and runs `screendump` on a loop, decoding each PPM into a [`Frame`] the pane uploads
/// as an egui texture — the OS-less appliance's REAL screen. Pure-Rust (std `UnixStream`
/// + `serde_json` + a P6 PPM parser); no VNC/RFB crate, no new deps. A genuine capture
/// off the guest's virtio-gpu (the OVMF firmware screen at minimum, then whatever the
/// guest draws) — never a fabricated frame. The serial console keeps streaming in
/// parallel, so a graphical capture that never starts degrades to the honest placeholder
/// + live serial, it never blocks the boot.
#[cfg(feature = "vm-boot")]
struct FramebufferCapture {
    frame: Arc<Mutex<Option<Frame>>>,
    stop: Arc<AtomicBool>,
    _handle: Option<std::thread::JoinHandle<()>>,
}

#[cfg(feature = "vm-boot")]
impl FramebufferCapture {
    /// Spawn the capture thread against `qmp_sock` (created by QEMU with
    /// `-qmp unix:…,server=on,wait=off`). Non-blocking: returns immediately with an
    /// empty frame slot the loop fills once the first `screendump` decodes.
    fn start(qmp_sock: PathBuf, cell: String) -> FramebufferCapture {
        let frame = Arc::new(Mutex::new(None));
        let stop = Arc::new(AtomicBool::new(false));
        let (f, s) = (frame.clone(), stop.clone());
        let _handle = std::thread::Builder::new()
            .name(format!("dwarves-fb-{cell}"))
            .spawn(move || fb_capture_loop(&qmp_sock, &cell, &f, &s))
            .ok();
        FramebufferCapture {
            frame,
            stop,
            _handle,
        }
    }

    /// The latest decoded frame (a clone), or `None` before the first capture.
    fn latest(&self) -> Option<Frame> {
        self.frame.lock().unwrap().clone()
    }

    /// Ask the capture loop to stop (idempotent) — called on boot teardown.
    fn stop(&self) {
        self.stop.store(true, Ordering::SeqCst);
    }
}

#[cfg(feature = "vm-boot")]
impl Drop for FramebufferCapture {
    fn drop(&mut self) {
        self.stop.store(true, Ordering::SeqCst);
    }
}

/// The capture loop: connect (with retries — QEMU creates the socket a beat after
/// spawn), do the QMP handshake, then `screendump` → decode PPM → publish, ~2 fps, until
/// asked to stop or the socket closes (guest gone). Every failure path is silent + safe:
/// no frame is published, so the pane keeps its honest placeholder — NEVER a faked frame.
#[cfg(feature = "vm-boot")]
fn fb_capture_loop(
    sock: &Path,
    cell: &str,
    frame: &Arc<Mutex<Option<Frame>>>,
    stop: &Arc<AtomicBool>,
) {
    use std::io::{BufReader, Write};
    use std::os::unix::net::UnixStream;

    // 1. Connect — QEMU may take a moment to create the listening socket.
    let deadline = Instant::now() + Duration::from_secs(15);
    let stream = loop {
        if stop.load(Ordering::SeqCst) {
            return;
        }
        match UnixStream::connect(sock) {
            Ok(s) => break s,
            Err(_) if Instant::now() < deadline => {
                std::thread::sleep(Duration::from_millis(200));
            }
            Err(_) => return, // never came up — degrade to placeholder, no fake frame
        }
    };
    let _ = stream.set_read_timeout(Some(Duration::from_secs(5)));
    let mut writer = match stream.try_clone() {
        Ok(w) => w,
        Err(_) => return,
    };
    let mut reader = BufReader::new(stream);

    // 2. QMP handshake: greeting is skipped by `read_qmp_reply` (it waits for a
    //    return/error), so sending capabilities and reading its ack is enough.
    if writeln!(writer, r#"{{"execute":"qmp_capabilities"}}"#).is_err() {
        return;
    }
    if read_qmp_reply(&mut reader).is_none() {
        return;
    }

    // 3. screendump → decode → publish, paced ~30 fps.
    //
    // It was ~2 fps, on the reasoning that "the guest's screen changes on human
    // timescales". That is true of an appliance sitting in its hlt loop and FALSE of the
    // thing this console exists to show: MEASURED 2026-08-16 on t14s, a gunnar-installer
    // install is 1.4 s end to end, and the splash the installer draws to the GOP
    // framebuffer is on screen for about one of those seconds before the guest resets.
    // At 500 ms per capture — with the connect retry and the QMP handshake eaten out of
    // the same window — the drive landed a 640×480 "Display output is not active" (the
    // pre-GOP console) and then a 1280×800 frame that was black, because by then the
    // guest was already rebooting. Both are honest captures of nothing. 100 ms puts ~10
    // captures inside the window the logo is actually up. 100 ms was still not enough:
    // at that pace the drive caught the firmware's own TianoCore splash and then nothing
    // until the guest had already reset, so the product's OWN splash — the one the serial
    // reports as `drew nordisk/KORP boot splash to GOP framebuffer - OK` — fell BETWEEN
    // two captures every time. 30 ms is not a human-timescale assumption at all, which is
    // the point; the loop only lives as long as a guest is booting, so it is bounded by
    // the boot rather than left running against an idle appliance.
    let ppm = std::env::temp_dir().join(format!("dwarves-fb-{cell}.ppm"));
    while !stop.load(Ordering::SeqCst) {
        let cmd = format!(
            r#"{{"execute":"screendump","arguments":{{"filename":"{}"}}}}"#,
            ppm.display()
        );
        if writeln!(writer, "{cmd}").is_err() {
            break;
        }
        match read_qmp_reply(&mut reader) {
            Some(v) if v.get("return").is_some() => {
                if let Some(fr) = read_ppm(&ppm) {
                    *frame.lock().unwrap() = Some(fr);
                }
            }
            // An error reply (e.g. no active console yet) is not fatal — keep polling.
            Some(_) => {}
            // Socket closed ⇒ the guest is gone; stop cleanly (no fake frame).
            None => break,
        }
        // One short sleep rather than five: the stop flag is already checked at the top
        // of every pass, and at this pace the loop is responsive without the fan-out.
        if stop.load(Ordering::SeqCst) {
            break;
        }
        std::thread::sleep(Duration::from_millis(30));
    }
    let _ = std::fs::remove_file(&ppm);
}

/// Read QMP newline-delimited JSON until a command RESULT (`return`/`error`) arrives,
/// skipping the greeting and async `event` lines. `None` on EOF / read error / too many
/// non-result lines (bounded, so a chatty event stream can never spin forever).
#[cfg(feature = "vm-boot")]
fn read_qmp_reply(reader: &mut impl std::io::BufRead) -> Option<serde_json::Value> {
    for _ in 0..128 {
        let mut line = String::new();
        match reader.read_line(&mut line) {
            Ok(0) => return None, // EOF — socket closed
            Ok(_) => {}
            Err(_) => return None, // timeout / error
        }
        let line = line.trim();
        if line.is_empty() {
            continue;
        }
        if let Ok(v) = serde_json::from_str::<serde_json::Value>(line) {
            if v.get("return").is_some() || v.get("error").is_some() {
                return Some(v);
            }
            // greeting ("QMP") or async "event" → keep reading for the result
        }
    }
    None
}

/// Decode a binary **P6 PPM** (what QMP `screendump` writes) into an RGBA8 [`Frame`].
/// Tolerant of PPM `#` comments and arbitrary header whitespace; accepts 8-bit only
/// (maxval 255, which QEMU always emits). `None` on any malformed/short file, so a
/// half-written dump never becomes a garbage frame. Pure (no unix/QEMU deps) so it is
/// unit-tested in the default `cargo test --lib` gate, not only under `vm-boot`.
#[cfg(any(feature = "vm-boot", test))]
fn read_ppm(path: &Path) -> Option<Frame> {
    let data = std::fs::read(path).ok()?;
    let mut pos = 0usize;
    if ppm_next_token(&data, &mut pos)? != b"P6" {
        return None;
    }
    let width: usize = std::str::from_utf8(&ppm_next_token(&data, &mut pos)?)
        .ok()?
        .parse()
        .ok()?;
    let height: usize = std::str::from_utf8(&ppm_next_token(&data, &mut pos)?)
        .ok()?
        .parse()
        .ok()?;
    let maxval: usize = std::str::from_utf8(&ppm_next_token(&data, &mut pos)?)
        .ok()?
        .parse()
        .ok()?;
    if maxval != 255 || width == 0 || height == 0 {
        return None;
    }
    // Exactly ONE whitespace byte separates the header from the binary raster.
    pos += 1;
    let need = width.checked_mul(height)?.checked_mul(3)?;
    if data.len() < pos + need {
        return None;
    }
    let rgb = &data[pos..pos + need];
    let mut rgba = Vec::with_capacity(width * height * 4);
    for px in rgb.chunks_exact(3) {
        rgba.extend_from_slice(&[px[0], px[1], px[2], 255]);
    }
    Some(Frame {
        width,
        height,
        rgba,
    })
}

/// One whitespace/`#`-comment-delimited PPM header token, advancing `pos` to just past
/// it (at the following whitespace).
#[cfg(any(feature = "vm-boot", test))]
fn ppm_next_token(data: &[u8], pos: &mut usize) -> Option<Vec<u8>> {
    loop {
        while *pos < data.len() && data[*pos].is_ascii_whitespace() {
            *pos += 1;
        }
        if *pos < data.len() && data[*pos] == b'#' {
            while *pos < data.len() && data[*pos] != b'\n' {
                *pos += 1;
            }
        } else {
            break;
        }
    }
    let start = *pos;
    while *pos < data.len() && !data[*pos].is_ascii_whitespace() {
        *pos += 1;
    }
    (start != *pos).then(|| data[start..*pos].to_vec())
}

/// The honest stub launcher — WITHOUT the `vm-boot` feature the tunnr KVM backend is
/// not linked, so ▶ Build & Boot reports the feature is off (rebuild `--features
/// vm-boot`) rather than a fake boot. Same type name so the pane is feature-agnostic.
#[cfg(not(feature = "vm-boot"))]
pub struct TunnrBootLauncher;

#[cfg(not(feature = "vm-boot"))]
impl BootLauncher for TunnrBootLauncher {
    fn launch(&self, cell_name: &str, _repo_root: &Path) -> Result<Box<dyn SerialConsole>, String> {
        // CODED `dwarves-launch-4`: a feature-off build is a NAMED, actionable state —
        // never a fake boot, and a robot keys on the code, not this sentence.
        Err(dwarves::dwarves_err!(
            launch,
            4,
            "booting '{cell_name}' needs the `vm-boot` feature — rebuild dwarves-gui \
             `--features vm-boot` to boot THROUGH tunnr_vm under QEMU/OVMF/KVM"
        )
        .to_string())
    }
}

// ── The REAL edda RAID KVM engine: drive korp-installer through a genuine install+boot ──

/// **The edda RAID KVM engine** — drives the ⚒ Build Thing korp-installer cell through a
/// REAL install+boot by SHELLING OUT to edda's production acceptance harness
/// (`tunnr/crates/korp-installer/tests/raid_amd_nvme_boot.rs`, edda before 2026-09-26), the single source of truth
/// for the "multiple equal SSDs → RAID1 mirror, then boot the installed disk" story
/// (the owner's ★ mandate). That harness installs the mirror across two NVMe images via the
/// PRODUCTION [`korp_installer::raid::run_raid1_install`], power-cycles OFF the installed
/// NVMe under AMD-EPYC + OVMF, and reads back `APP-ALIVE … "verified":true` off the mirror
/// AND off a single surviving member.
///
/// This launcher streams that harness's combined stdout+stderr into the pane's console
/// VERBATIM, so the cell's PASS is set from the ACTUAL `APP-ALIVE` the guest emitted and
/// [`crate::build_thing`]'s `installer_json` parses `raid_built`/`booted_off_disk`/
/// `app_alive`/`single_member_survived` off the same real serial — never a fabricated
/// green. Reuse, never twin (L5): the KVM engine is edda's; dwarves drives it + drains it.
///
/// Scriptable exactly as the harness documents: `KORP_QEMU_CPU` (default `EPYC`) and
/// `KORP_RAID_SCRATCH` (the member-image dir the harness leaves behind) are threaded
/// through. korp-installer lives in TUNNR since 2026-09-26 (moved from edda), so the
/// checkout is located from `DWARVES_TUNNR_DIR` (the old `DWARVES_EDDA_DIR` is still read
/// if it holds the crate), else the tunnr sibling of the dwarves repo; its own cargo target
/// dir from `TUNNR_TARGET_DIR` (or the old `EDDA_TARGET_DIR`). The engine keeps its
/// `edda-raid` name so `DWARVES_INSTALLER_ENGINE=edda-raid` still selects it. Feature-INDEPENDENT: it
/// links nothing from `tunnr-vm` — it only spawns `cargo test` — so the real KVM path is
/// reachable whether or not `vm-boot` is compiled in.
pub struct EddaRaidInstallerLauncher {
    /// The checkout root that contains `crates/korp-installer` (tunnr since 2026-09-26).
    edda_dir: PathBuf,
    /// The cargo target dir for the harness build (keeps it off `/home`), or `None` to
    /// inherit the ambient `CARGO_TARGET_DIR`.
    target_dir: Option<PathBuf>,
    /// The guest CPU model (`-cpu …`) — `EPYC` mirrors the AMD metal.
    qemu_cpu: String,
    /// Where the harness writes the two NVMe member images (so a driver can read them
    /// back), or `None` to use the harness's own default.
    scratch: Option<PathBuf>,
}

impl Default for EddaRaidInstallerLauncher {
    fn default() -> Self {
        let edda_dir = ["DWARVES_TUNNR_DIR", "DWARVES_EDDA_DIR"]
            .into_iter()
            .filter_map(std::env::var_os)
            .map(PathBuf::from)
            .find(|d| d.join("crates/korp-installer/Cargo.toml").is_file())
            .or_else(|| {
                // Siblings of the dwarves repo (this crate is `<dwarves>/dwarves-gui`).
                [
                    PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../tunnr"),
                    PathBuf::from("tunnr"),
                    PathBuf::from("../tunnr"),
                ]
                .into_iter()
                .find(|c| c.join("crates/korp-installer/Cargo.toml").is_file())
            })
            .unwrap_or_else(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../tunnr"));
        EddaRaidInstallerLauncher {
            edda_dir,
            target_dir: std::env::var_os("TUNNR_TARGET_DIR")
                .or_else(|| std::env::var_os("EDDA_TARGET_DIR"))
                .map(PathBuf::from)
                .or_else(|| {
                    Some(PathBuf::from(format!(
                        "/scratch/{}/cargo-targets/tunnr",
                        std::env::var("USER").unwrap_or_default()
                    )))
                }),
            qemu_cpu: std::env::var("KORP_QEMU_CPU").unwrap_or_else(|_| "EPYC".to_string()),
            scratch: std::env::var_os("KORP_RAID_SCRATCH").map(PathBuf::from),
        }
    }
}

impl EddaRaidInstallerLauncher {
    /// A launcher pointed at an explicit edda checkout (tests + non-default layouts).
    pub fn at(edda_dir: impl Into<PathBuf>) -> Self {
        EddaRaidInstallerLauncher {
            edda_dir: edda_dir.into(),
            ..Default::default()
        }
    }

    /// The `cargo test` invocation that runs the real edda RAID harness (`--ignored`
    /// because it is a heavy `#[ignore]` acceptance test; `--nocapture` so its narrated
    /// serial reaches our console).
    fn harness_command(&self) -> std::process::Command {
        let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
        let mut cmd = std::process::Command::new(cargo);
        cmd.current_dir(&self.edda_dir)
            .args([
                "test",
                "-p",
                "korp-installer",
                "--test",
                "raid_amd_nvme_boot",
                "--",
                "--ignored",
                "--nocapture",
            ])
            .env("KORP_QEMU_CPU", &self.qemu_cpu)
            .stdout(std::process::Stdio::piped())
            .stderr(std::process::Stdio::piped());
        if let Some(t) = &self.target_dir {
            cmd.env("CARGO_TARGET_DIR", t);
        }
        if let Some(s) = &self.scratch {
            cmd.env("KORP_RAID_SCRATCH", s);
        }
        cmd
    }
}

impl BootLauncher for EddaRaidInstallerLauncher {
    fn launch(&self, _cell: &str, _repo_root: &Path) -> Result<Box<dyn SerialConsole>, String> {
        // Fail LOUD (never a fake green) if the edda checkout isn't where we think — the
        // console then NAMES what to set, rather than a cryptic spawn error.
        let manifest = self.edda_dir.join("crates/korp-installer/Cargo.toml");
        if !manifest.is_file() {
            return Err(format!(
                "korp-installer not found at {} — set DWARVES_TUNNR_DIR to the tunnr checkout \
                 (korp-installer moved there from edda 2026-09-26) so the RAID KVM harness can run",
                manifest.display()
            ));
        }
        ProcessConsole::spawn(self.harness_command()).map(|c| Box::new(c) as Box<dyn SerialConsole>)
    }

    fn launch_hw(
        &self,
        cell: &str,
        repo_root: &Path,
        _hw: &HwConfig,
        _medium: Option<&Path>,
    ) -> Result<Box<dyn SerialConsole>, String> {
        self.launch(cell, repo_root)
    }
}

/// A [`SerialConsole`] over a spawned child process: merges its stdout+stderr into one
/// cumulative line buffer (a reader thread per stream) and resolves the terminal phase
/// from the child's EXIT STATUS — exit 0 → [`BootPhase::Passed`], any non-zero → `Failed`.
/// A monitor thread joins BOTH readers before reaping, so every narrated line (the
/// `APP-ALIVE` markers, the `RAID-AMD-NVME PROOF OK` line) is captured before the phase
/// goes terminal. The pane's [`pump`] drains it exactly like a QEMU serial console — so
/// the edda RAID harness streams into the ⚒ pane with no second console vocabulary.
struct ProcessConsole {
    lines: Arc<Mutex<Vec<String>>>,
    /// `None` while running; `Some(passed)` once the child exited.
    done: Arc<Mutex<Option<bool>>>,
    /// The live child (taken by the monitor thread when it reaps); `kill()` reaches it here.
    child: Arc<Mutex<Option<std::process::Child>>>,
}

impl ProcessConsole {
    fn spawn(mut cmd: std::process::Command) -> Result<ProcessConsole, String> {
        let mut child = cmd
            .spawn()
            .map_err(|e| format!("spawn edda RAID harness failed: {e}"))?;
        let lines = Arc::new(Mutex::new(Vec::new()));
        let done = Arc::new(Mutex::new(None));
        let stdout = child.stdout.take();
        let stderr = child.stderr.take();
        let mut readers = Vec::new();
        for stream in [stdout.map(StdStream::Out), stderr.map(StdStream::Err)]
            .into_iter()
            .flatten()
        {
            let buf = lines.clone();
            readers.push(std::thread::spawn(move || stream.read_into(buf)));
        }
        let child = Arc::new(Mutex::new(Some(child)));
        let (child_w, done_w) = (child.clone(), done.clone());
        std::thread::Builder::new()
            .name("dwarves-edda-raid-monitor".to_string())
            .spawn(move || {
                for r in readers {
                    let _ = r.join(); // all output captured before we reap
                }
                let status = child_w.lock().unwrap().take().map(|mut c| c.wait());
                *done_w.lock().unwrap() = Some(matches!(status, Some(Ok(s)) if s.success()));
            })
            .ok();
        Ok(ProcessConsole { lines, done, child })
    }
}

/// One of a child's two output streams, type-erased so a single reader thread body drains
/// either into the shared line buffer.
enum StdStream {
    Out(std::process::ChildStdout),
    Err(std::process::ChildStderr),
}

impl StdStream {
    fn read_into(self, buf: Arc<Mutex<Vec<String>>>) {
        use std::io::BufRead;
        let reader: Box<dyn std::io::Read + Send> = match self {
            StdStream::Out(o) => Box::new(o),
            StdStream::Err(e) => Box::new(e),
        };
        for line in std::io::BufReader::new(reader).lines() {
            match line {
                Ok(l) => buf.lock().unwrap().push(l),
                Err(_) => break,
            }
        }
    }
}

impl SerialConsole for ProcessConsole {
    fn log(&self) -> String {
        self.lines.lock().unwrap().join("\n")
    }
    fn phase(&self) -> BootPhase {
        match *self.done.lock().unwrap() {
            None => BootPhase::Booting,
            Some(true) => BootPhase::Passed,
            Some(false) => BootPhase::Failed("edda RAID harness exited non-zero".to_string()),
        }
    }
    fn kill(&mut self) {
        if let Some(c) = self.child.lock().unwrap().as_mut() {
            let _ = c.kill();
        }
    }
}

// ── The REAL container launcher: run + follow logs through jera→draupnir. ─────────────

/// The real container launcher — RUNS a `[[build.container]]` cell's OCI image through
/// **jera → draupnir**'s ONE bollard/podman engine and follows its stdout into a
/// [`LogConsole`]. Behind the `container-run` feature (it links jera + draupnir's
/// `backend-oci`); the no-feature build ships the honest stub below (Run then reports
/// the feature is off — never fake logs). Same type name so the pane is feature-agnostic.
#[cfg(feature = "container-run")]
pub struct DraupnirContainerLauncher;

#[cfg(feature = "container-run")]
impl DraupnirContainerLauncher {
    /// Map a container launch-cell name to the OCI [`ContainerSpec`] to run. The
    /// hello-world busybox echoes a deterministic line then exits 0 (→ passed).
    fn spec_for(cell: &str) -> Result<jera::container::ContainerSpec, String> {
        use jera::container::ContainerSpec;
        let mut spec = match cell {
            "tunnr-hello-world" => {
                let mut s = ContainerSpec::image("docker.io/library/busybox:latest");
                s.cmd = vec![
                    "/bin/echo".to_string(),
                    "hello world from the tunnr container appliance".to_string(),
                ];
                s
            }
            // korp-falkordb — the fat-appliance's graph backend, run for REAL through
            // draupnir→podman. The image's OWN entrypoint (no cmd override); it is a
            // long-lived SERVICE (never exits), so its readiness is latched by a live
            // `redis PING → PONG` probe (see [`Self::readiness_probe`]), not exit 0.
            "korp-falkordb" => ContainerSpec::image("localhost/nordisk-falkordb-valkey:9"),
            other => {
                return Err(format!(
                    "no container image wired for launch cell '{other}'"
                ))
            }
        };
        spec = spec.with_name(format!("dwarves-{cell}"));
        Ok(spec)
    }

    /// The **readiness probe** for a long-lived SERVICE container cell — the argv to
    /// `exec` into the running container plus the substring that proves it is serving.
    /// `None` for a run-to-completion cell (the busybox hello-world, which passes on a
    /// clean exit 0). `korp-falkordb` → `redis-cli ping` must answer `PONG`.
    fn readiness_probe(cell: &str) -> Option<(Vec<String>, String)> {
        match cell {
            "korp-falkordb" => Some((vec!["redis-cli".into(), "ping".into()], "PONG".into())),
            _ => None,
        }
    }
}

#[cfg(feature = "container-run")]
impl ContainerLauncher for DraupnirContainerLauncher {
    fn launch(&self, cell_name: &str, _repo_root: &Path) -> Result<Box<dyn SerialConsole>, String> {
        use jera::boot::BootTarget;
        let spec = Self::spec_for(cell_name)?;
        // Route THROUGH jera's `connect_default_runner` — the ONE OCI engine (draupnir's
        // bollard) under `container-oci`; never a second engine, never a shell. A
        // detached run hands back a long-lived proc whose logs the follow-task streams
        // and whose exit code drives pass/fail.
        let runner = jera::container::connect_default_runner();
        let proc = runner
            .spawn(&BootTarget::Container(spec))
            .map_err(|e| format!("container run failed (is the OCI engine reachable?): {e}"))?;
        // A long-lived SERVICE container (korp-falkordb) never exits, so latch readiness
        // on a live exec probe (`redis PING → PONG`) rather than exit 0; a
        // run-to-completion container (the busybox hello-world) has no probe and passes
        // on a clean exit 0 through the plain [`LogConsole`].
        match Self::readiness_probe(cell_name) {
            Some((argv, marker)) => Ok(Box::new(ServiceLogConsole::new(proc, argv, marker))),
            None => Ok(Box::new(LogConsole::new(proc))),
        }
    }
}

/// Adapts a running container's jera [`BootProc`] to a [`SerialConsole`] LOG window
/// (feature `container-run`): `log()` drains the follow-task's new stdout/stderr lines
/// into a cumulative buffer the [`pump`] streams; `phase()` maps the container's
/// exit-code-aware state (running → Booting, clean exit → Passed, crash → Failed).
#[cfg(feature = "container-run")]
struct LogConsole {
    proc: Mutex<Box<dyn jera::boot::BootProc>>,
    acc: Mutex<Vec<String>>,
}

#[cfg(feature = "container-run")]
impl LogConsole {
    fn new(proc: Box<dyn jera::boot::BootProc>) -> Self {
        LogConsole {
            proc: Mutex::new(proc),
            acc: Mutex::new(Vec::new()),
        }
    }
}

#[cfg(feature = "container-run")]
impl SerialConsole for LogConsole {
    fn log(&self) -> String {
        let new = self.proc.lock().unwrap().drain_log();
        let mut acc = self.acc.lock().unwrap();
        acc.extend(new);
        acc.join("\n")
    }
    fn phase(&self) -> BootPhase {
        use jera::boot::BootStatus;
        match self.proc.lock().unwrap().poll() {
            BootStatus::Booting | BootStatus::Running => BootPhase::Booting,
            BootStatus::BootedOk => BootPhase::Passed,
            BootStatus::Failed(e) => BootPhase::Failed(e),
            BootStatus::Killed => BootPhase::Failed("container killed".to_string()),
        }
    }
    fn kill(&mut self) {
        self.proc.lock().unwrap().kill();
    }
}

/// Adapts a long-lived **SERVICE** container (FalkorDB) to a [`SerialConsole`] whose
/// readiness is a live **exec probe**, not process exit. A server never exits on its
/// own, so [`LogConsole`]'s "exit 0 = passed" would only ever time out. This console
/// streams the container's real stdout AND, once the container is running, execs a
/// readiness command into it on a throttle (`redis-cli ping`); the first reply carrying
/// the marker (`PONG`) latches [`BootPhase::Passed`] and appends a crisp proof line. The
/// container is a real detached run THROUGH draupnir→podman; dropping the console reaps
/// it (stop + remove) so a proof run leaves nothing behind. Feature `container-run`.
#[cfg(feature = "container-run")]
struct ServiceLogConsole {
    proc: Mutex<Box<dyn jera::boot::BootProc>>,
    acc: Mutex<Vec<String>>,
    /// The readiness exec argv (`redis-cli ping`) and the reply substring that proves
    /// the service is actually serving (`PONG`).
    probe: Vec<String>,
    marker: String,
    /// The terminal phase once latched — a passed service stays passed (and the exec
    /// probe stops firing), a died-before-ready service stays failed.
    latched: Mutex<Option<BootPhase>>,
    /// Throttle: fire the exec probe at most ~every 300ms (a `podman exec` on every
    /// 20ms pump tick would hammer the engine for nothing).
    last_probe: Mutex<Instant>,
}

#[cfg(feature = "container-run")]
impl ServiceLogConsole {
    fn new(proc: Box<dyn jera::boot::BootProc>, probe: Vec<String>, marker: String) -> Self {
        ServiceLogConsole {
            proc: Mutex::new(proc),
            acc: Mutex::new(Vec::new()),
            probe,
            marker,
            latched: Mutex::new(None),
            last_probe: Mutex::new(Instant::now() - Duration::from_secs(1)),
        }
    }
}

#[cfg(feature = "container-run")]
impl SerialConsole for ServiceLogConsole {
    fn log(&self) -> String {
        let new = self.proc.lock().unwrap().drain_log();
        let mut acc = self.acc.lock().unwrap();
        acc.extend(new);
        acc.join("\n")
    }
    fn phase(&self) -> BootPhase {
        use jera::boot::BootStatus;
        if let Some(p) = self.latched.lock().unwrap().clone() {
            return p;
        }
        // A service that EXITS before it is ready is a failure, never a pass.
        match self.proc.lock().unwrap().poll() {
            BootStatus::Booting => return BootPhase::Booting,
            BootStatus::Running => {}
            BootStatus::BootedOk => {
                let p = BootPhase::Failed("service container exited before it was ready".into());
                *self.latched.lock().unwrap() = Some(p.clone());
                return p;
            }
            BootStatus::Failed(e) => {
                let p = BootPhase::Failed(e);
                *self.latched.lock().unwrap() = Some(p.clone());
                return p;
            }
            BootStatus::Killed => {
                let p = BootPhase::Failed("service container killed".into());
                *self.latched.lock().unwrap() = Some(p.clone());
                return p;
            }
        }
        // Running: fire the readiness exec on a throttle; the marker reply latches Passed.
        {
            let mut last = self.last_probe.lock().unwrap();
            if last.elapsed() < Duration::from_millis(300) {
                return BootPhase::Booting;
            }
            *last = Instant::now();
        }
        let argv: Vec<&str> = self.probe.iter().map(String::as_str).collect();
        if let Ok(out) = self.proc.lock().unwrap().exec(&argv) {
            let replied = out
                .stdout
                .iter()
                .chain(out.stderr.iter())
                .any(|l| l.contains(&self.marker));
            if replied {
                self.acc.lock().unwrap().push(format!(
                    "[dwarves] KORP-FALKORDB READY — `{}` → {} (real container, real reply)",
                    self.probe.join(" "),
                    self.marker,
                ));
                *self.latched.lock().unwrap() = Some(BootPhase::Passed);
                return BootPhase::Passed;
            }
        }
        BootPhase::Booting
    }
    fn kill(&mut self) {
        self.proc.lock().unwrap().kill();
    }
}

#[cfg(feature = "container-run")]
impl Drop for ServiceLogConsole {
    fn drop(&mut self) {
        // Reap the detached service container (stop + remove) — a proof run leaves nothing.
        if let Ok(mut p) = self.proc.lock() {
            p.kill();
        }
    }
}

/// The honest stub container launcher — WITHOUT the `container-run` feature the
/// jera→draupnir OCI engine is not linked, so ▶ Run reports the feature is off (rebuild
/// `--features container-run`) rather than fake logs. Same type name (feature-agnostic pane).
#[cfg(not(feature = "container-run"))]
pub struct DraupnirContainerLauncher;

#[cfg(not(feature = "container-run"))]
impl ContainerLauncher for DraupnirContainerLauncher {
    fn launch(&self, cell_name: &str, _repo_root: &Path) -> Result<Box<dyn SerialConsole>, String> {
        // CODED `dwarves-launch-3`: the container engine is not linked in this build.
        Err(dwarves::dwarves_err!(
            launch,
            3,
            "running container '{cell_name}' needs the `container-run` feature — rebuild \
             dwarves-gui `--features container-run` to run THROUGH jera→draupnir's OCI \
             engine (bollard) and follow its logs"
        )
        .to_string())
    }
}

// ── the Exec pane's sandbox ───────────────────────────────────────────────────

/// **How a built executable is run: inside a container, never against the host.**
///
/// Decided 2026-08-06. An `exe` cell's ▶ KÖR used to spawn the artifact as a bare child
/// process with the operator's own environment, cwd and write access — so "what does this
/// build do?" was answered by letting it do it, to this machine. Containerised, the answer
/// is the same every time and the artifact cannot write over anything outside.
///
/// # Why this is a plain struct and not a `ContainerSpec`
///
/// The OCI types live behind the `container-run` feature, so a spec-shaped plan would be
/// untestable in the default build — and the property worth guarding (the argv the
/// container runs is the CONTAINER path, and the artifact is mounted read-only) is
/// exactly the part that needs no engine to check. [`Self::to_spec`] does the conversion
/// where the feature is on; [`Self::cmd`] and [`Self::bind`] are checkable everywhere.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExecSandbox {
    /// The image the artifact runs in.
    pub image: String,
    /// The artifact on the host. Mounted read-only; never executed from here.
    pub artifact: PathBuf,
    /// The operator's argv, already split.
    pub argv: Vec<String>,
}

impl ExecSandbox {
    /// Where the artifact is mounted inside the container.
    pub const MOUNT_DIR: &'static str = "/artefakt";

    /// The default base image, overridable with `$DWARVES_EXEC_IMAGE`.
    ///
    /// A `scratch` image cannot be the default: these artifacts are dynamically linked
    /// against the host's glibc, and a run that dies on a missing loader is a worse
    /// answer than a slightly larger image.
    pub const DEFAULT_IMAGE: &'static str = "docker.io/library/debian:bookworm-slim";

    /// A sandbox for `artifact`, run with `argv`.
    pub fn new(artifact: impl Into<PathBuf>, argv: Vec<String>) -> Self {
        let image = std::env::var("DWARVES_EXEC_IMAGE")
            .ok()
            .filter(|v| !v.trim().is_empty())
            .unwrap_or_else(|| Self::DEFAULT_IMAGE.to_string());
        ExecSandbox {
            image,
            artifact: artifact.into(),
            argv,
        }
    }

    /// The artifact's path INSIDE the container.
    pub fn in_container(&self) -> String {
        let name = self
            .artifact
            .file_name()
            .map(|n| n.to_string_lossy().to_string())
            .unwrap_or_else(|| "artefakt".to_string());
        format!("{}/{name}", Self::MOUNT_DIR)
    }

    /// The command the container runs: the in-container path, then the operator's argv.
    ///
    /// Never the host path. That distinction is the whole of the change — a spec that
    /// carried `/home/…/target/release/x` would be a host path handed to a container that
    /// cannot see it, and a launcher that "helpfully" fell back to the host would be the
    /// bare process this replaces.
    pub fn cmd(&self) -> Vec<String> {
        let mut v = vec![self.in_container()];
        v.extend(self.argv.iter().cloned());
        v
    }

    /// The bind mount, in podman's `host:container:ro` spelling.
    pub fn bind(&self) -> String {
        format!("{}:{}:ro", self.artifact.display(), self.in_container())
    }

    /// The one line the pane shows so the sandbox is visible rather than implied.
    pub fn banner(&self) -> String {
        format!(
            "kör i container {} · {} · utan nät",
            self.image,
            self.bind()
        )
    }

    /// The jera spec this plan becomes.
    #[cfg(feature = "container-run")]
    pub fn to_spec(&self, name: &str) -> jera::container::ContainerSpec {
        use jera::container::{BindMount, ContainerSpec, NetMode};
        let mut spec = ContainerSpec::image(self.image.clone()).with_name(name.to_string());
        spec.cmd = self.cmd();
        spec.mounts = vec![BindMount {
            host: self.artifact.display().to_string(),
            container: self.in_container(),
            ro: true,
        }];
        // No network. "It cannot write over anything outside" is the point, and an
        // artifact nobody asked to reach the network should not be able to.
        spec.net = NetMode::None;
        spec
    }
}

/// One per Exec run, so no two containers can ever contend for the same name. Not a
/// count of live containers — there is at most one of those, and that is
/// `DeclaredForge::take_live`'s job, not this counter's.
#[cfg(feature = "container-run")]
static EXEC_RUN_SEQ: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);

/// Runs a BUILT ARTIFACT inside a container, as the [`crate::build_scene`] Exec pane's
/// ▶ KÖR — the artifact mirror of [`DraupnirContainerLauncher`], which runs a DECLARED
/// image cell.
///
/// It is a [`ContainerLauncher`] so the whole console contract above serves it unchanged:
/// one [`BootRun`], one [`pump`], one log window, one kill path. The `cell_name` it is
/// handed is only a label for the container.
pub struct ArtifactExecLauncher {
    /// What to run, and how.
    pub sandbox: ExecSandbox,
}

#[cfg(feature = "container-run")]
impl ContainerLauncher for ArtifactExecLauncher {
    /// # Why this does not go through `jera`'s runner
    ///
    /// It did, and the mount silently vanished. jera's detached
    /// `BootRunner::spawn` calls `draupnir::Boot::boot`, whose `BootSpec` has **no
    /// bind field at all** — so a `ContainerSpec` may carry `mounts`, and that leg
    /// drops every one of them without a word. Measured 2026-08-06: the container came
    /// up and died on `executable file /artefakt/<x> not found`. `net` DOES flow
    /// through both legs, which is what made the gap easy to miss.
    ///
    /// `ContainerBoot::boot_with_binds` is the mount-aware twin, and it lives on
    /// draupnir. Same engine, one layer down — not a second one.
    fn launch(&self, cell_name: &str, _repo_root: &Path) -> Result<Box<dyn SerialConsole>, String> {
        if !self.sandbox.artifact.is_file() {
            // CODED `dwarves-launch-2`: nothing to mount, so nothing can run.
            return Err(dwarves::dwarves_err!(
                launch,
                2,
                "{} is not a file, so there is nothing to mount into the container",
                self.sandbox.artifact.display()
            )
            .to_string());
        }
        // A UNIQUE name per run. A fixed one collides with the previous run's container
        // while it is still being removed — the teardown is asked for synchronously but
        // completes on the engine's own clock, so pressing KÖR twice raced the removal
        // and the second run died on a name conflict. Observed as a flake before it was
        // understood, which is the only warning that shape ever gives.
        let n = EXEC_RUN_SEQ.fetch_add(1, Ordering::SeqCst);
        let spec = self
            .sandbox
            .to_spec(&format!("dwarves-exec-{cell_name}-{n}"));
        let binds = spec.bind_strings();
        let backend = draupnir::container::ContainerBoot::new();
        let machine = backend
            .boot_with_binds(&spec.to_draupnir(), &binds)
            .map_err(|e| format!("the container run failed (is the OCI engine reachable?): {e}"))?;
        Ok(Box::new(BoundRunConsole {
            backend,
            machine: Some(machine),
            acc: Mutex::new(Vec::new()),
        }))
    }
}

/// A [`SerialConsole`] over a container booted **with its bind mounts** — the console
/// half of the note above.
///
/// [`LogConsole`] wraps a jera `BootProc`, and there is no `BootProc` on this leg: the
/// mount-aware boot hands back a bare `Machine`. Everything else is the same contract,
/// drained through draupnir's own [`draupnir::container::ContainerControl`], so the pump
/// above cannot tell the two apart.
#[cfg(feature = "container-run")]
struct BoundRunConsole {
    backend: draupnir::container::ContainerBoot,
    /// `None` once killed, so a second kill is a no-op rather than a second stop.
    machine: Option<draupnir::Machine>,
    acc: Mutex<Vec<String>>,
}

#[cfg(feature = "container-run")]
impl SerialConsole for BoundRunConsole {
    fn log(&self) -> String {
        use draupnir::container::ContainerControl;
        if let Some(m) = &self.machine {
            let new = self.backend.drain_logs(m);
            self.acc.lock().unwrap().extend(new);
        }
        self.acc.lock().unwrap().join("\n")
    }

    fn phase(&self) -> BootPhase {
        use draupnir::container::{ContainerControl, ContainerState};
        let Some(m) = &self.machine else {
            return BootPhase::Failed("the run was torn down".to_string());
        };
        match self.backend.container_state(m) {
            ContainerState::Running => BootPhase::Booting,
            ContainerState::Exited(0) => BootPhase::Passed,
            // A non-zero exit is the artifact's own answer, and it is not a pass.
            ContainerState::Exited(code) => BootPhase::Failed(format!("exit {code}")),
            ContainerState::Gone => BootPhase::Failed("the container went away".to_string()),
        }
    }

    fn kill(&mut self) {
        use draupnir::container::ContainerControl;
        if let Some(m) = self.machine.take() {
            // Drain what is left BEFORE the container is removed, or a fast run's whole
            // output is lost to the teardown that follows it.
            let last = self.backend.drain_logs(&m);
            self.acc.lock().unwrap().extend(last);
            self.backend.stop(&m);
        }
    }
}

/// The honest stub: WITHOUT `container-run` there is no OCI engine linked, and the
/// answer is that — never a fall back to running the artifact against the host, which is
/// the exact thing this type exists to stop.
#[cfg(not(feature = "container-run"))]
impl ContainerLauncher for ArtifactExecLauncher {
    fn launch(
        &self,
        _cell_name: &str,
        _repo_root: &Path,
    ) -> Result<Box<dyn SerialConsole>, String> {
        // CODED `dwarves-launch-3`: the container engine is not linked in this build.
        Err(dwarves::dwarves_err!(
            launch,
            3,
            "an exe is run INSIDE a container, and this build has no OCI engine linked — \
             rebuild dwarves-gui `--features container-run`. It will not be run against \
             the host instead"
        )
        .to_string())
    }
}

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

    /// A fake console that replays a captured serial in growing batches, then reports
    /// a terminal phase — the test double for a real KVM boot.
    struct ReplayConsole {
        /// Growing cumulative log (lines revealed one batch per `log()` call).
        revealed: Vec<String>,
        pending: VecDeque<String>,
        /// The phase to report once the replay is exhausted.
        terminal: BootPhase,
    }
    impl ReplayConsole {
        fn new(serial: &str, terminal: BootPhase) -> Self {
            ReplayConsole {
                revealed: Vec::new(),
                pending: serial.lines().map(str::to_string).collect(),
                terminal,
            }
        }
    }
    impl ReplayConsole {
        /// Cumulative log — every line revealed so far.
        fn cumulative(&self) -> String {
            let mut s = self.revealed.join("\n");
            if !s.is_empty() {
                s.push('\n');
            }
            s
        }
    }

    /// The pump calls `log()` then `phase()` each tick; each `phase()` reveals one more
    /// serial line (interior mutability behind a Mutex), reaching `terminal` once the
    /// replay is exhausted — a growing cumulative log, exactly like a live boot.
    struct Replay(Mutex<ReplayConsole>);
    impl SerialConsole for Replay {
        fn log(&self) -> String {
            self.0.lock().unwrap().cumulative()
        }
        fn phase(&self) -> BootPhase {
            let mut g = self.0.lock().unwrap();
            if let Some(line) = g.pending.pop_front() {
                g.revealed.push(line);
                BootPhase::Booting
            } else {
                g.terminal.clone()
            }
        }
        fn kill(&mut self) {}
    }

    const KORP_SERIAL: &str = "\
==================================================
  KORP surveillance demo  (KVM)
[korp] PID1 = appliance (pid=1)
[korp] firmware: UEFI/OVMF boot reached PID 1
[korp] installed korp-demo (headless pipeline + in-image FalkorDB)
[korp] placed data component korp-map -> /var/lib/korp-demo/map/europe.parquet
[korp] started korp-demo
[korp] korp-demo installed and started
KORP-APPLIANCE BOOT-OK: appliance reached runtime";

    #[test]
    fn pump_streams_the_korp_serial_and_ends_passed() {
        let mut src = Replay(Mutex::new(ReplayConsole::new(
            KORP_SERIAL,
            BootPhase::Passed,
        )));
        let console = Arc::new(Mutex::new(Vec::new()));
        let phase = Arc::new(Mutex::new(BootPhase::Booting));
        let kill = AtomicBool::new(false);
        let frame = Arc::new(Mutex::new(None));
        let ok = pump(
            &mut src,
            &kill,
            &console,
            &phase,
            &frame,
            Duration::from_secs(5),
            false,
        );
        assert!(ok, "the korp boot reached BOOT-OK (passed)");
        assert_eq!(*phase.lock().unwrap(), BootPhase::Passed);
        let log = console.lock().unwrap().join("\n");
        assert!(
            log.contains("installed korp-demo"),
            "korp-demo install streamed:\n{log}"
        );
        assert!(
            log.contains("started korp-demo"),
            "korp-demo start streamed:\n{log}"
        );
        assert!(
            log.contains("KORP-APPLIANCE BOOT-OK: appliance reached runtime"),
            "the BOOT-OK marker streamed to the console:\n{log}"
        );
    }

    /// The GPU framebuffer decode is REAL: a byte-exact P6 PPM (the exact shape QMP
    /// `screendump` writes) decodes to the right dimensions and RGBA pixels — with a
    /// `#` comment + irregular header whitespace to prove the parser is tolerant. This
    /// is the pure half of the graphical console; a malformed/short dump yields `None`
    /// (never a garbage/faked frame).
    #[test]
    fn read_ppm_decodes_a_real_p6_framebuffer_to_rgba() {
        // 2x1 image: red pixel then green pixel. Header carries a comment + extra ws.
        let mut bytes = Vec::new();
        bytes.extend_from_slice(b"P6\n# dwarves screendump\n2  1\n255\n");
        bytes.extend_from_slice(&[255, 0, 0, 0, 255, 0]); // RGB: red, green
        let dir = tempfile::tempdir().unwrap();
        let path = dir.path().join("fb.ppm");
        std::fs::write(&path, &bytes).unwrap();

        let frame = read_ppm(&path).expect("a valid P6 PPM decodes");
        assert_eq!((frame.width, frame.height), (2, 1));
        assert_eq!(
            frame.rgba,
            vec![255, 0, 0, 255, /* red+α */ 0, 255, 0, 255 /* green+α */],
            "RGB→RGBA8 with opaque alpha, row-major"
        );

        // A truncated raster (header promises 2 px, only 1 present) is rejected — no
        // half-frame ever reaches the texture.
        let mut short = Vec::new();
        short.extend_from_slice(b"P6\n2 1\n255\n");
        short.extend_from_slice(&[1, 2, 3]); // only ONE pixel of the two
        let sp = dir.path().join("short.ppm");
        std::fs::write(&sp, &short).unwrap();
        assert!(
            read_ppm(&sp).is_none(),
            "a short/truncated dump is rejected, never faked"
        );
    }

    #[test]
    fn a_cancelled_boot_is_failed_not_passed() {
        // A console that never terminates on its own — only cancellation ends it.
        let mut src = Replay(Mutex::new(ReplayConsole::new(
            "[korp] booting…\n[korp] still booting…",
            BootPhase::Booting, // never terminal
        )));
        let console = Arc::new(Mutex::new(Vec::new()));
        let phase = Arc::new(Mutex::new(BootPhase::Booting));
        let kill = AtomicBool::new(true); // cancelled from the start
        let frame = Arc::new(Mutex::new(None));
        let ok = pump(
            &mut src,
            &kill,
            &console,
            &phase,
            &frame,
            Duration::from_secs(5),
            false,
        );
        assert!(!ok, "a cancelled boot is NOT a passed smoke gate");
        assert!(matches!(*phase.lock().unwrap(), BootPhase::Failed(_)));
    }

    #[test]
    fn a_launcher_error_surfaces_and_never_fakes_boot_ok() {
        struct FailLauncher;
        impl BootLauncher for FailLauncher {
            fn launch(&self, _c: &str, _r: &Path) -> Result<Box<dyn SerialConsole>, String> {
                Err("QEMU not on PATH — install qemu-system-x86".to_string())
            }
        }
        let run = BootRun::spawn("korp-kvm", PathBuf::from("."), Arc::new(FailLauncher));
        // Wait for the worker to resolve.
        for _ in 0..200 {
            if !run.is_running() {
                break;
            }
            std::thread::sleep(Duration::from_millis(10));
        }
        assert!(
            !run.passed(),
            "a missing precondition never fakes a BOOT PASS"
        );
        assert!(matches!(run.phase(), BootPhase::Failed(_)));
        let log = run.log().join("\n");
        assert!(
            log.contains("QEMU not on PATH"),
            "the missing precondition is surfaced: {log}"
        );
        assert!(!log.contains("BOOT-OK"), "no fabricated BOOT-OK");
    }

    /// ── gap #7: the iso-kind launch plan, pure (no QEMU, no feature) ─────────────────
    ///
    /// The routing rule itself: an iso-kind variant WITH a composed medium selects the
    /// live-ISO leg and carries THE COMPOSED FILE, verbatim; no medium (or a path no
    /// longer on disk) is the stand-in leg. RED before 2026-08-20: the rule did not
    /// exist — `spawn_iso` carried no medium at all and the launcher refused every
    /// composed variant (see `composed_iso_routing` below for that recorded red).
    #[test]
    fn an_iso_variant_with_a_composed_medium_plans_the_live_leg_with_that_file() {
        let dir = tempfile::tempdir().unwrap();
        let iso = dir.path().join("gunnar-appliance-iso.iso");
        std::fs::write(&iso, b"the composed medium").unwrap();
        assert_eq!(
            plan_iso_launch(Some(&iso)),
            IsoLaunchPlan::LiveComposed {
                medium: iso.clone()
            },
            "a composed medium takes the live leg, carrying the composed file itself"
        );
        assert_eq!(
            plan_iso_launch(None),
            IsoLaunchPlan::Standin,
            "no build → the stand-in"
        );
        assert_eq!(
            plan_iso_launch(Some(&dir.path().join("vanished.iso"))),
            IsoLaunchPlan::Standin,
            "an artifact no longer on disk is NOT booted on the build's word"
        );
    }

    /// ── gap #7, extended (the medium-pick fix): the planned medium ends with `.iso` ──
    ///
    /// The routing above carries whatever the pane hands it, verbatim — which is the
    /// point, and also why the pick UPSTREAM must be right: on t14s (2026-08-18) an
    /// iso-kind BYGG's outcome recorded the KERNEL (`/boot/vmlinuz-…`, the first
    /// provenance input in the artifact list) as the artifact, the gap-7 leg
    /// faithfully planned `LiveComposed` around it, and QEMU failed opening it. This
    /// test drives the plan FROM the real pick — `BuildReport::medium()`, the exact
    /// thing `settle()` reads into the outcome `composed_medium()` hands over — over
    /// the live repro's list shape, so the planned medium is the mastered `.iso`.
    /// RED before `BuildReport::medium()` existed: the pick was `artifacts.first()`
    /// and this planned the kernel.
    #[test]
    fn the_planned_medium_for_a_composed_iso_variant_is_the_mastered_iso() {
        use dwarves::build_thing::BuildReport;
        use nornir_build_thing::variant::ArtifactKind;

        let dir = tempfile::tempdir().unwrap();
        let kernel = dir.path().join("vmlinuz-7.0.0-29-generic");
        let payload = dir.path().join("gunnar");
        let iso = dir.path().join("gunnar-appliance-iso.iso");
        for p in [&kernel, &payload, &iso] {
            std::fs::write(p, b"bytes on disk, so the plan's is_file() gate passes").unwrap();
        }
        // The appliance-ISO order, verbatim: inputs first, the medium last.
        let report = BuildReport {
            variant: "gunnar-appliance-iso".into(),
            exit_ok: true,
            kind: ArtifactKind::Iso,
            artifacts: vec![(kernel, 51), (payload, 51), (iso.clone(), 51)],
            ..Default::default()
        };
        let medium = report
            .medium()
            .map(|(p, _)| p.clone())
            .expect("a produced medium");
        assert!(
            medium.extension().is_some_and(|e| e == "iso"),
            "the planned medium is the mastered image, never a provenance input: {}",
            medium.display()
        );
        assert_eq!(
            plan_iso_launch(Some(&medium)),
            IsoLaunchPlan::LiveComposed { medium: iso },
            "and the gap-7 leg boots THAT file"
        );
    }

    /// The composed gate is ONE spelling for both composed legs (installer reboot +
    /// iso-kind live boot): korp-start's `INSTALLER: `-prefixed exec-argv line, naming
    /// the declared entry when there is one.
    #[test]
    fn the_composed_handoff_marker_names_the_declared_entry() {
        assert_eq!(
            composed_handoff_marker(Some("gunnar")),
            "INSTALLER: exec argv: [\"gunnar\""
        );
        assert_eq!(composed_handoff_marker(None), "INSTALLER: exec argv: [");
    }

    /// A spy launcher proves the pane→launcher carry for an iso-kind variant: ▶ hands
    /// the launcher THE composed file and a GPU hardware config, and the intro names
    /// that file (never the stand-in notice). RED before 2026-08-20 in the strongest
    /// sense: `spawn_iso` had no medium parameter, so the pane could not hand the
    /// composed file over at all.
    #[test]
    fn spawn_iso_carries_the_composed_medium_and_gpu_hw_to_the_launcher() {
        // spawn_iso now picks the ssh-forward port first; serialize against the test
        // that deliberately exhausts the window, or the spy is never reached.
        let _guard = SSH_PORT_TEST_LOCK.lock().unwrap();
        type Seen = Arc<Mutex<Option<(String, HwConfig, Option<PathBuf>)>>>;
        struct Spy(Seen);
        impl BootLauncher for Spy {
            fn launch(&self, _c: &str, _r: &Path) -> Result<Box<dyn SerialConsole>, String> {
                Err("spy: launch() must not be reached — launch_hw carries the medium".into())
            }
            fn launch_hw(
                &self,
                cell: &str,
                _root: &Path,
                hw: &HwConfig,
                medium: Option<&Path>,
            ) -> Result<Box<dyn SerialConsole>, String> {
                *self.0.lock().unwrap() =
                    Some((cell.to_string(), *hw, medium.map(Path::to_path_buf)));
                Err("spy stops here — the routing is recorded".into())
            }
        }

        let dir = tempfile::tempdir().unwrap();
        let iso = dir.path().join("gunnar-appliance-iso.iso");
        std::fs::write(&iso, b"the composed medium").unwrap();
        let seen: Seen = Arc::new(Mutex::new(None));
        let run = BootRun::spawn_iso(
            "gunnar-appliance-iso",
            dir.path().to_path_buf(),
            Arc::new(Spy(seen.clone())),
            HwConfig::default(),
            Some(iso.clone()),
        );
        for _ in 0..200 {
            if !run.is_running() {
                break;
            }
            std::thread::sleep(Duration::from_millis(10));
        }
        let (cell, hw, medium) = seen
            .lock()
            .unwrap()
            .clone()
            .expect("the launcher was reached");
        assert_eq!(cell, "gunnar-appliance-iso");
        assert_eq!(
            medium.as_deref(),
            Some(iso.as_path()),
            "the launcher gets THE COMPOSED FILE, never a re-mastered one"
        );
        assert!(
            hw.gpu,
            "the iso-kind ▶ carries a real hardware config with the GPU on"
        );
        let log = run.log().join("\n");
        assert!(
            log.contains(&format!("medium: {}", iso.display())),
            "the intro names the medium about to boot:\n{log}"
        );
        assert!(
            !log.contains("STAND-IN"),
            "a composed boot never says STAND-IN:\n{log}"
        );
    }

    /// …and with NO medium the stand-in still announces itself, before anything boots.
    #[test]
    fn spawn_iso_without_a_medium_announces_the_stand_in() {
        struct Refuse;
        impl BootLauncher for Refuse {
            fn launch(&self, _c: &str, _r: &Path) -> Result<Box<dyn SerialConsole>, String> {
                Err("no medium — refused honestly".into())
            }
        }
        let run = BootRun::spawn_iso(
            "gunnar-appliance-iso",
            PathBuf::from("."),
            Arc::new(Refuse),
            HwConfig::default(),
            None,
        );
        for _ in 0..200 {
            if !run.is_running() {
                break;
            }
            std::thread::sleep(Duration::from_millis(10));
        }
        let log = run.log().join("\n");
        assert!(
            log.contains(STANDIN_NOTICE),
            "the no-medium boot announces the stand-in before anything boots:\n{log}"
        );
        assert!(!run.passed(), "an honest refusal is never a passed gate");
    }

    /// Serializes the tests that probe/occupy the ssh-forward host-port window, so a
    /// test that deliberately fills the window never flakes a concurrent pick.
    static SSH_PORT_TEST_LOCK: Mutex<()> = Mutex::new(());

    /// ── clone-into-the-appliance: the live-ISO ssh forward is ANNOUNCED ─────────────
    ///
    /// MEASURED on the live t14s guest 2026-08-20: the ⚒ pane's live-ISO leg started
    /// QEMU with `-netdev user,id=n0 -device virtio-net-pci,netdev=n0` — NO hostfwd —
    /// so gunnar's sshd, listening in the guest on 0.0.0.0:2222, was unreachable from
    /// the host and there was no address to `git clone` INTO the live-booted
    /// appliance. gunnar's own forge test (`gunnar_appliance_iso`) proves
    /// ssh-over-hostfwd works in principle; the gap is purely the pane leg's netdev
    /// line.
    ///
    /// This pins the operator half: once the guest is really booting, the console
    /// says WHERE the forward listens — `[dwarves] ssh forward: host
    /// 127.0.0.1:<port> -> guest 2222` — so the address is read off the pane, never
    /// out of the source. RED 2026-08-20 against today's code: no ssh-forward line
    /// exists anywhere in the boot path.
    #[test]
    fn an_iso_boot_announces_the_ssh_forward_address() {
        let _guard = SSH_PORT_TEST_LOCK.lock().unwrap();
        struct Live;
        impl BootLauncher for Live {
            fn launch(&self, _c: &str, _r: &Path) -> Result<Box<dyn SerialConsole>, String> {
                Ok(Box::new(Replay(Mutex::new(ReplayConsole::new(
                    KORP_SERIAL,
                    BootPhase::Passed,
                )))))
            }
        }
        let dir = tempfile::tempdir().unwrap();
        let iso = dir.path().join("gunnar-appliance-iso.iso");
        std::fs::write(&iso, b"the composed medium").unwrap();
        let run = BootRun::spawn_iso(
            "gunnar-appliance-iso",
            dir.path().to_path_buf(),
            Arc::new(Live),
            HwConfig::default(),
            Some(iso),
        );
        for _ in 0..500 {
            if !run.is_running() {
                break;
            }
            std::thread::sleep(Duration::from_millis(10));
        }
        assert!(run.passed(), "the fake boot replays to BOOT-OK");
        let log = run.log().join("\n");
        assert!(
            log.contains("[dwarves] ssh forward: host 127.0.0.1:"),
            "the console names the ssh forward's host address:\n{log}"
        );
        assert!(
            log.contains("-> guest 2222"),
            "…and the guest port it reaches (gunnar's sshd convention):\n{log}"
        );
    }

    /// The planned netdev argv, exact: user-mode SLIRP on virtio-net (the tokens the
    /// leg always had) plus the LOOPBACK-bound forward — `tcp:127.0.0.1:…`, never the
    /// all-interfaces `tcp::…` tunnr's `NetConfig::User` seam would spell. RED
    /// 2026-08-20 before `live_ssh_netdev_args` existed: the leg planned tunnr's
    /// default plain netdev, no hostfwd at all (the t14s measurement).
    #[test]
    fn the_live_iso_netdev_plans_a_loopback_ssh_hostfwd() {
        assert_eq!(
            live_ssh_netdev_args(10022),
            vec![
                "-netdev".to_string(),
                "user,id=n0,hostfwd=tcp:127.0.0.1:10022-:2222".to_string(),
                "-device".to_string(),
                "virtio-net-pci,netdev=n0".to_string(),
            ],
            "loopback-bound forward to the guest's 2222, on the same user netdev"
        );
    }

    /// The operator line, exact — and a pick-next-free rebind is LOUD, never silent.
    #[test]
    fn the_ssh_forward_notice_names_the_address_and_any_rebind() {
        assert_eq!(
            ssh_forward_notice(10022),
            "[dwarves] ssh forward: host 127.0.0.1:10022 -> guest 2222"
        );
        assert_eq!(
            ssh_forward_notice(10024),
            "[dwarves] ssh forward: host 127.0.0.1:10024 -> guest 2222 (10022 was taken)",
            "a rebound port states the port it stepped past"
        );
    }

    /// The pick walk is BOUNDED and an exhausted window fails BY NAME — the two rules
    /// that keep the forward honest: the operator is never bound somewhere unstated,
    /// and never left guessing which port was meant.
    #[test]
    fn the_port_pick_is_bounded_and_fails_by_name() {
        let _guard = SSH_PORT_TEST_LOCK.lock().unwrap();
        // Hold a real port so the walk must step past it.
        let held = std::net::TcpListener::bind(("127.0.0.1", 0)).unwrap();
        let base = held.local_addr().unwrap().port();
        let picked = pick_free_loopback_port(base, 4).expect("a later port in the window is free");
        assert!(
            picked > base && picked < base + 4,
            "next-free stays inside the bounded window: picked {picked}, base {base}"
        );
        // A window of exactly the held port has nothing free: a NAMED failure.
        let err = pick_free_loopback_port(base, 1).expect_err("the only candidate is taken");
        assert!(
            err.contains("no free ssh-forward host port") && err.contains(&base.to_string()),
            "the failure names the exhausted range, never a silent rebind: {err}"
        );
    }

    /// …and an exhausted window fails the SPAWNED RUN by name too (coded
    /// dwarves-launch-5), before the launcher ever runs — the ▶ never boots a guest
    /// while pretending a forward it does not have.
    #[test]
    fn an_exhausted_port_window_fails_the_iso_boot_by_name() {
        let _guard = SSH_PORT_TEST_LOCK.lock().unwrap();
        // Occupy the ENTIRE window. A bind that fails means someone else already
        // holds that port right now — taken either way, which is all the test needs.
        let _held: Vec<std::net::TcpListener> = (LIVE_SSH_HOST_PORT
            ..LIVE_SSH_HOST_PORT + LIVE_SSH_PORT_TRIES)
            .filter_map(|p| std::net::TcpListener::bind(("127.0.0.1", p)).ok())
            .collect();
        struct MustNotLaunch;
        impl BootLauncher for MustNotLaunch {
            fn launch(&self, _c: &str, _r: &Path) -> Result<Box<dyn SerialConsole>, String> {
                panic!("the launcher must never run without the announced forward");
            }
        }
        let run = BootRun::spawn_iso(
            "gunnar-appliance-iso",
            PathBuf::from("."),
            Arc::new(MustNotLaunch),
            HwConfig::default(),
            None,
        );
        for _ in 0..500 {
            if !run.is_running() {
                break;
            }
            std::thread::sleep(Duration::from_millis(10));
        }
        assert!(
            !run.passed(),
            "no forward, no boot — never a silent forward-less run"
        );
        assert_eq!(run.error_code(), Some("dwarves-launch-5"));
        let log = run.log().join("\n");
        assert!(
            log.contains("no free ssh-forward host port"),
            "the console names the exhausted window:\n{log}"
        );
    }

    /// ── gap #7 (dwarves-ui audit): a COMPOSED iso-kind variant must boot, not be refused ──
    ///
    /// REPRODUCED live 2026-08-20 (t14s): `gunnar-appliance-iso` BYGGed a real 32 MB
    /// medium — a self-contained El Torito ESP ISO out of isocompose — and ▶ KÖR still
    /// answered `dwarves-launch-5 … no real appliance wired for launch cell
    /// 'gunnar-appliance-iso' (bootable: …)`, offering the korp-installer efistub
    /// STAND-IN instead. These tests pin the routing: an iso-kind variant WITH a
    /// composed medium takes the live-ISO boot leg (the leg that already firmware-boots
    /// a self-contained-ESP ISO from RAM, GPU + QMP framebuffer included), so the only

The file goes on; the console shows all of it.