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

nordisk/Skidbladnir

crates/installer/nordisk-pairing/src/frontside/update.rs

//! **The front does the update its shop opened** (0.5.26, P4a; A22 W8, A28 §4 "update step"):
//! the ONE update step every product's front runs, moved here from Holger's front
//! (`holger-estate-front/src/update.rs`, the newer of the two: lane B5's [`Declared`] and its keep
//! fallback) and Gunnar's (`gunnar-front/src/update.rs`), which carried the same `decide`,
//! `on_trial`, `staged_of` and `wait_confirmed`. Feature `frontside-update`.
//!
//! The operator opens an update at the ONE box (`EstateOrder.UpdateEstate`); the front PULLS it
//! (`SoldEstate.UpdateTake`, behind [`Shop`]) and does it one box at a time, in the shop's order
//! twin → main → front, reporting each (`SoldEstate.UpdateReport`) before the next is handed out:
//!
//! ```text
//!   twin / main:  GET /live/running on the box's log door (the kept installer material)
//!     runs the release's app already         → done:nothing
//!     same BASE, live on, the release's app   → fetched (sha256, length, pin checked),
//!                                               POST /live/stage, waited for until confirmed
//!                                                                         → done:live
//!                                               the old app back, no trial  → failed (rolled back)
//!     a stage refused (korp's legs)           → failed (nothing moved)
//!     a stage refused for a keep boot only    → SoldEstate.Upgrade (keep medium) → done:keep
//!       (lane B5: the release env moved, a declaration the box keeps does not ride, or
//!        the box takes none live — `crate::live::falls_to_keep`)
//!     BASE moved, live off, no live app       → SoldEstate.Upgrade (keep medium) → done:keep
//!   front (itself, its own slot):
//!     runs it already (staged here before)    → done:live (else done:nothing)
//!     same BASE, live on                      → staged into its own slot: it re-execs, and
//!                                               reports on its next look, running the new app
//!     its own stage rolled back (the marker,  → failed (rolled back), never staged again
//!       the old app, no trial)
//!     else                                    → needs-keep (the shop keep-boots the front)
//! ```
//!
//! [`step`] is the decision and the sequence over [`Hands`] (the boxes' live doors, the fetch,
//! the keep upgrade, a clock) — pure enough for tests; [`look`] is one look over a [`Shop`].
//! A take the shop refuses (out of turn, no update open) is its words. Nothing here names a
//! product: the front's keep medium is the product's words ([`Hands::front_keep`]).

use std::time::Duration;

use crate::live::{Declared, Running, Stage};

/// How often a staged box is read again.
pub const POLL_EVERY: Duration = Duration::from_secs(10);
/// How long a staged box has to confirm (its trial is 60 s of serving; korp-start rolls a box
/// that never serves back at 600 s).
pub const CONFIRM_BUDGET: Duration = Duration::from_secs(15 * 60);
/// A box running its OLD app with no trial this long after the stage was rolled back.
pub const ROLLED_BACK_AFTER: Duration = Duration::from_secs(60);
/// The marker the front leaves before it stages ITSELF: after its re-exec it reports `live`.
pub const STAGED_SELF_REL: &str = "update/staged-self";

/// One live app as the shop hands it (`EstateLiveApp`).
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct LiveApp {
    pub base: String,
    pub app: String,
    pub app_len: u64,
    /// hex
    pub sig: String,
    pub url: String,
    pub sha256: String,
    /// Lane B5: the release declaration's bundle and its signature, hex (empty: none / unsigned).
    pub release: String,
    pub release_sig: String,
    /// Lane B5: the release's `korp.env.*` tokens (empty: none declared).
    pub env: Vec<String>,
    /// feed-big (2026-11-04): the release declaration BY REFERENCE (`release` empty) — its
    /// object's presigned url, sha256 and length (Holger 3.2's carries the signed CVE bundle).
    pub release_url: String,
    pub release_sha256: String,
    pub release_len: u64,
}

/// The update the shop handed out (`EstateUpdateTakeResponse`).
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Take {
    pub open: bool,
    pub release: String,
    pub live: bool,
    /// `twin` | `main` | `front`.
    pub role: String,
    /// The live app main and twin run (the release's product app), while live is on.
    pub app: Option<LiveApp>,
    /// The front's own live app.
    pub front: Option<LiveApp>,
    pub words: String,
}

/// The ONE box's update door, as this front calls it.
pub trait Shop {
    /// `SoldEstate.UpdateTake`.
    fn take(&self) -> Result<Take, String>;
    /// `SoldEstate.UpdateReport`: `role`'s step, `outcome` and `words` signed together.
    fn report(&self, role: &str, outcome: &str, words: &str) -> Result<String, String>;
}

/// What this front can do to a box.
pub trait Hands {
    /// `GET /live/running` of `role` (the front: its own slot).
    fn running(&self, role: &str) -> Result<Running, String>;
    /// `POST /live/stage` to `role` with what the release declares (the front: its own slot —
    /// it then re-execs).
    fn stage(&self, role: &str, stage: &Stage, declared: &Declared) -> Result<String, String>;
    /// The archive at the presigned url.
    fn fetch(&self, url: &str) -> Result<Vec<u8>, String>;
    /// `role` (main or twin) upgraded from the release's keep medium and answering again.
    fn keep(&self, role: &str) -> Result<String, String>;
    /// Wait `d` (a test's clock moves instead).
    fn wait(&self, d: Duration);
    /// Was the front's own stage of `release` left here before its re-exec? (and forget it)
    fn staged_self(&self, release: &str) -> bool;
    /// Leave the marker before the front stages itself.
    fn mark_staged_self(&self, release: &str) -> Result<(), String>;
    /// **The product's words for the front's keep boot** (`needs-keep`): what the shop
    /// keep-boots, and from which medium (`<product>'s front from <product>-front-keep`).
    fn front_keep(&self) -> String {
        "the front from its keep medium".into()
    }
    /// `roles` (main, twin or both) RE-ADMITTED from the kept bundle where they stand, in ONE
    /// run (`crate::kept::readmit_together`): the second half of a keep upgrade, for boxes that
    /// booted their upgraded system and were never re-admitted (Gunnar's front, gunnar.rs
    /// 2026-10-25; every product's since T16). A front that keeps no bundle refuses by name.
    fn readmit(&self, roles: &[&str]) -> Result<String, String> {
        Err(format!("REFUSED [readmit-unsupported] this front re-admits no box ({})", roles.join(" and ")))
    }
}

/// The refusal of a box's OPEN install listener (`crate::bootstrap::respond`: no `/live/*`
/// there): the box booted a fresh system and waits for its installer's feed.
pub const OPEN_INSTALL_REFUSAL: &str = "[install-path]";

/// Pure: **does `role`'s live door answer as a box waiting for its re-admission?** A main or
/// twin whose keep medium ran and whose upgraded system booted (its pairing state cleared, its
/// data kept) answers `GET /live/running` with the open listener's refusal until the front feeds
/// it (gunnar.rs main, 2026-10-25). Moved from Gunnar's front (T16).
pub fn awaits_readmission(role: &str, door_said: &str) -> bool {
    role != "front" && door_said.contains(OPEN_INSTALL_REFUSAL)
}

/// What the re-admission done first left: the box read again, the re-admission's words, and
/// whether this step's own box was among the re-admitted.
type Readmitted = (Result<Running, String>, Option<String>, bool);

/// **Boxes left waiting for their re-admission are re-admitted FIRST, and TOGETHER** (Gunnar's
/// front, gunnar.rs 2026-10-25; T16 for every product): this box and the other box of the
/// estate alike, in one [`Hands::readmit`]. A re-admission re-feeds every other box of the kept
/// plan, and a box at its open install listener never takes a re-feed (the twin's keep upgrade
/// waited 3606 s on such a main). Then this box is read again; the step goes on from what it
/// runs. A door that answers anything else is left to the step as it answered.
fn readmit_first(h: &dyn Hands, role: &str, first: Result<Running, String>) -> Result<Readmitted, Report> {
    let peer = if role == "main" { "twin" } else { "main" };
    let mut waiting: Vec<&str> = Vec::new();
    let mut said = String::new();
    match &first {
        Err(e) if awaits_readmission(role, e) => {
            waiting.push(role);
            said = e.clone();
        }
        Err(_) => return Ok((first, None, false)),
        Ok(_) => {}
    }
    let peer_busy = crate::frontside::busy::on(peer);
    if let Err(e) = h.running(peer) {
        if awaits_readmission(peer, &e) {
            waiting.push(peer);
            if said.is_empty() {
                said = e;
            }
        }
    }
    if waiting.is_empty() {
        return Ok((first, None, false));
    }
    let back = h.readmit(&waiting).map_err(|why| (FAILED.to_string(), format!("{} answer(s) as a box never re-admitted after its keep boot ({said}); the re-admission: {why}", waiting.join(" and "))))?;
    drop(peer_busy);
    let me = waiting.contains(&role);
    let again = if me { h.running(role) } else { first };
    match again {
        Ok(r) => Ok((Ok(r), Some(back), me)),
        Err(again) => Err((FAILED.to_string(), format!("its keep boot was never re-admitted: {back}; its live door did not answer after: {again}"))),
    }
}

pub const DONE_KEEP: &str = "done:keep";
pub const DONE_LIVE: &str = "done:live";
pub const DONE_NOTHING: &str = "done:nothing";
pub const NEEDS_KEEP: &str = "needs-keep";
pub const FAILED: &str = "failed";

/// What is done to one box ([`decide`]).
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Plan {
    /// It runs the release's app already.
    Nothing,
    /// The release's app staged live into its slot.
    Live,
    /// Its BASE moved, live is off, or the release has no live app: the keep medium.
    Keep(String),
    /// A trial is still open on it: asked again on the next look.
    Wait(String),
}

/// Whether a slot word still has a trial open (`b trial`, `b booted`).
fn on_trial(slot: &str) -> bool {
    slot.split_whitespace().nth(1).is_some()
}

/// Pure: **what one box gets** — its running app against the release's live app.
pub fn decide(r: &Running, app: Option<&LiveApp>, live: bool) -> Plan {
    let Some(app) = app else {
        return Plan::Keep(if live { "the release carries no live app for it".into() } else { "live updates are OFF for this estate".into() });
    };
    if on_trial(&r.slot) {
        return Plan::Wait(format!("[live-trial-open] slot {}: its last update is not confirmed yet", r.slot));
    }
    if r.app.trim() == app.app.trim() {
        return Plan::Nothing;
    }
    if !live {
        return Plan::Keep("live updates are OFF for this estate".into());
    }
    if !r.live_update {
        return Plan::Keep("live update is OFF on the box (korp.live_update=off, its BASE)".into());
    }
    if r.base.trim() != app.base.trim() {
        return Plan::Keep(format!("its BASE moved ({} → {}): a base update", short(&r.base), short(&app.base)));
    }
    Plan::Live
}

fn short(h: &str) -> &str {
    &h[..h.len().min(12)]
}

fn unhex(s: &str) -> Result<Vec<u8>, String> {
    let s = s.trim();
    if !s.len().is_multiple_of(2) {
        return Err("odd hex".into());
    }
    (0..s.len()).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|e| e.to_string())).collect()
}

/// **The release's app, fetched and proven** — its sha256 as the shop named it, its length and
/// its pin as the release recorded — and the stage the box takes.
pub fn staged_of(h: &dyn Hands, app: &LiveApp) -> Result<Stage, String> {
    if app.sig.trim().is_empty() {
        return Err("[update-unsigned] the release's live app carries no signature: never staged".into());
    }
    let archive = h.fetch(&app.url)?;
    let sha: String = ring::digest::digest(&ring::digest::SHA256, &archive).as_ref().iter().map(|b| format!("{b:02x}")).collect();
    if !app.sha256.trim().is_empty() && sha != app.sha256.trim() {
        return Err(format!("[update-fetch] the archive's sha256 is {}, the release says {}", short(&sha), short(&app.sha256)));
    }
    if archive.len() as u64 != app.app_len {
        return Err(format!("[update-fetch] the archive is {} bytes, the release says {}", archive.len(), app.app_len));
    }
    let pin = blake3::hash(&archive).to_hex().to_string();
    if pin != app.app.trim() {
        return Err(format!("[update-fetch] the archive's pin is {}, the release says {}", short(&pin), short(&app.app)));
    }
    Ok(Stage { pin_hex: app.app.trim().to_string(), signature: unhex(&app.sig).map_err(|e| format!("[update-unsigned] the signature: {e}"))?, base: app.base.trim().to_string(), archive })
}

/// **What the release declares beside `app`** (lane B5): its release declaration and its env,
/// as the box is handed them. A declaration that is not hex is refused by name.
pub fn declared_of(app: &LiveApp) -> Result<Declared, String> {
    let release = match app.release.trim() {
        "" => None,
        b => Some((unhex(b).map_err(|e| format!("[update-release] the release declaration: {e}"))?, unhex(&app.release_sig).map_err(|e| format!("[update-release] its signature: {e}"))?)),
    };
    Ok(Declared { release, env: (!app.env.is_empty()).then(|| app.env.clone()) })
}

/// **What the release declares, its declaration fetched when it rides by reference**
/// (feed-big): the object at `release_url` proven by `release_sha256` and `release_len`, never
/// staged otherwise; inline or none: [`declared_of`].
pub fn declared_fetched(h: &dyn Hands, app: &LiveApp) -> Result<Declared, String> {
    let want = app.release_sha256.trim();
    if want.is_empty() {
        return declared_of(app);
    }
    if app.release_url.trim().is_empty() {
        return Err(format!("[update-release-fetch] the release declaration rides by reference ({}) and the shop named no object", short(want)));
    }
    let bytes = h.fetch(&app.release_url).map_err(|e| format!("[update-release-fetch] {e}"))?;
    let sha: String = ring::digest::digest(&ring::digest::SHA256, &bytes).as_ref().iter().map(|b| format!("{b:02x}")).collect();
    if sha != want || bytes.len() as u64 != app.release_len {
        return Err(format!("[update-release-fetch] the release declaration fetched is {} ({} bytes), the release says {} ({} bytes)", short(&sha), bytes.len(), short(want), app.release_len));
    }
    let sig = unhex(&app.release_sig).map_err(|e| format!("[update-release] its signature: {e}"))?;
    Ok(Declared { release: Some((bytes, sig)), env: (!app.env.is_empty()).then(|| app.env.clone()) })
}

/// **Wait until `role` runs `app` confirmed**; the old app back with no trial is a rollback.
pub fn wait_confirmed(h: &dyn Hands, role: &str, app: &str, old: &str) -> Result<String, String> {
    let mut waited = Duration::ZERO;
    let mut last = String::new();
    while waited < CONFIRM_BUDGET {
        h.wait(POLL_EVERY);
        waited += POLL_EVERY;
        match h.running(role) {
            Ok(r) if r.app.trim() == app.trim() && !on_trial(&r.slot) => return Ok(format!("the {role} runs app {} confirmed (slot {})", short(app), r.slot)),
            Ok(r) if r.app.trim() == old.trim() && !on_trial(&r.slot) && waited >= ROLLED_BACK_AFTER => {
                return Err(format!("[update-rolled-back] the {role} runs its old app {} again (slot {}): the new one never confirmed", short(old), r.slot))
            }
            Ok(r) => last = format!("slot {}, app {}", r.slot, short(&r.app)),
            // The box is down while it re-execs: asked again.
            Err(e) => last = e,
        }
    }
    Err(format!("[update-not-confirmed] the {role} did not confirm within {} min ({last})", CONFIRM_BUDGET.as_secs() / 60))
}

/// What a step reports: the outcome word and the words.
pub type Report = (String, String);

/// **One box's step**: the box whose turn it is, updated, and what to report — `None` when
/// nothing is to be reported now (no update open; a trial still open; the front staged itself
/// and reports after its re-exec).
pub fn step(h: &dyn Hands, t: &Take) -> Option<Report> {
    if !t.open || t.role.is_empty() {
        return None;
    }
    let role = t.role.as_str();
    if !matches!(role, "twin" | "main" | "front") {
        return Some((FAILED.into(), format!("this front updates twin, main and front, not {role:?}")));
    }
    // The box this step works on is BUSY until it ends: the recharge watcher leaves it alone
    // (lane G5, A7 row 10; Gunnar aa14357b).
    let _busy = crate::frontside::busy::on(role);
    let done = |how: &str, words: String| Some((format!("done:{how}"), words));
    let failed = |words: String| Some((FAILED.to_string(), words));
    let front = role == "front";
    let app = if front { t.front.as_ref() } else { t.app.as_ref() };
    let mut first = h.running(role);
    let (mut readmitted, mut self_readmitted) = (None, false);
    if !front {
        match readmit_first(h, role, first) {
            Ok((again, back, me)) => (first, readmitted, self_readmitted) = (again, back, me),
            Err(report) => return Some(report),
        }
    }
    // The words of a re-admission done first, before what this step did.
    let first_words = readmitted.as_ref().map(|b| format!("re-admitted first: {b}; ")).unwrap_or_default();
    let running = match first {
        Ok(r) => r,
        // A box with no live door answers nothing live: its keep medium (the fallback).
        Err(e) if !front && e.contains("[live-unsupported]") => {
            return match h.keep(role) {
                Ok(w) => done("keep", format!("its live door: {e}; {w}")),
                Err(k) => failed(format!("its live door: {e}; the keep upgrade: {k}")),
            }
        }
        Err(e) if front => return Some((NEEDS_KEEP.into(), format!("the front's own slot: {e}"))),
        Err(e) => return failed(format!("its live door did not answer: {e}")),
    };
    match decide(&running, app, t.live) {
        Plan::Wait(_) => None,
        Plan::Nothing => {
            if let Some(back) = readmitted.filter(|_| self_readmitted) {
                return done("keep", format!("its keep boot was never re-admitted: {back}; the {role} runs app {}", short(&running.app)));
            }
            let how = if front && h.staged_self(&t.release) { "live" } else { "nothing" };
            done(how, format!("{first_words}the {role} runs app {}", short(&running.app)))
        }
        Plan::Keep(why) if front => Some((NEEDS_KEEP.into(), format!("{why}: the shop keep-boots {}", h.front_keep()))),
        Plan::Keep(why) => match h.keep(role) {
            Ok(w) => done("keep", format!("{first_words}{why}: {w}")),
            Err(e) => failed(format!("{first_words}{why}; the keep upgrade of the {role}: {e}")),
        },
        Plan::Live => {
            let app = app.expect("Live is decided on an app");
            // A32-8: the front staged THIS release into its own slot before (the marker), and runs
            // its old app again with no trial: korp rolled the new one back. Said once, as a box's
            // rollback is; never staged again on every look.
            if front && h.staged_self(&t.release) {
                return failed(format!(
                    "[update-rolled-back] the front runs its old app {} again (slot {}): its own stage of release {} never confirmed",
                    short(&running.app),
                    running.slot,
                    t.release
                ));
            }
            let (stage, declared) = match staged_of(h, app).and_then(|s| declared_fetched(h, app).map(|d| (s, d))) {
                Ok(s) => s,
                Err(e) => return failed(e),
            };
            if front {
                if let Err(e) = h.mark_staged_self(&t.release) {
                    return failed(format!("the marker before its own stage: {e}"));
                }
                return match h.stage(role, &stage, &declared) {
                    // It re-execs now; its next look reports.
                    Ok(_) => None,
                    // Only its keep boot lays what the release moved: no live update of its own.
                    Err(e) if crate::live::falls_to_keep(&e) => {
                        h.staged_self(&t.release);
                        Some((NEEDS_KEEP.into(), format!("its own slot refused the live stage ({}): the shop keep-boots {}", e.trim(), h.front_keep())))
                    }
                    Err(e) => failed(format!("its own stage was refused: {e}")),
                };
            }
            match h.stage(role, &stage, &declared) {
                Ok(_) => {}
                // Only a keep boot lays what the release moved (lane B5).
                Err(e) if crate::live::falls_to_keep(&e) => {
                    let why = format!("the {role} refused the live stage ({})", e.trim());
                    return match h.keep(role) {
                        Ok(w) => done("keep", format!("{why}: {w}")),
                        Err(k) => failed(format!("{why}; the keep upgrade of the {role}: {k}")),
                    };
                }
                Err(e) => return failed(format!("the stage was refused: {e}")),
            }
            match wait_confirmed(h, role, &app.app, &running.app) {
                Ok(w) => done("live", w),
                Err(e) => failed(e),
            }
        }
    }
}

/// **One look**: take, do the box whose turn it is, report it. `Ok(None)`: nothing to report
/// now. Answers `(role, outcome, the shop's words)`.
pub fn look(shop: &dyn Shop, h: &dyn Hands) -> Result<Option<(String, String, String)>, String> {
    let t = shop.take()?;
    let Some((outcome, words)) = step(h, &t) else { return Ok(None) };
    let said = shop.report(&t.role, &outcome, &words)?;
    Ok(Some((t.role, outcome, said)))
}

/// **Where a box reaches a release object the bench named on its own loopback** (the mock's
/// guestfwd answers the host's ports at 10.0.2.100). Any other url is as given.
pub fn reachable(url: &str) -> String {
    match url.strip_prefix("http://127.0.0.1:") {
        Some(rest) => format!("http://10.0.2.100:{rest}"),
        None => url.to_string(),
    }
}

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

    const OLD: &str = "0101010101010101010101010101010101010101010101010101010101010101";

    fn archive() -> Vec<u8> {
        (0..4096u32).map(|i| (i % 251) as u8).collect()
    }

    fn app_of(base: &str) -> LiveApp {
        let a = archive();
        LiveApp {
            base: base.into(),
            app: blake3::hash(&a).to_hex().to_string(),
            app_len: a.len() as u64,
            sig: "ab".repeat(80),
            url: "https://objsto.example/release/holger/03.live-app.znippy?X-Amz=…".into(),
            sha256: ring::digest::digest(&ring::digest::SHA256, &a).as_ref().iter().map(|b| format!("{b:02x}")).collect(),
            ..LiveApp::default()
        }
    }

    fn running(base: &str, app: &str, slot: &str) -> Running {
        Running { base: base.into(), app: app.into(), slot: slot.into(), live_update: true }
    }

    /// A fake estate: each box's running app, what a stage does to it, the keep upgrade.
    #[derive(Default)]
    struct Fake {
        boxes: RefCell<BTreeMap<String, Running>>,
        /// The app a staged box comes back with (`None`: it rolls back).
        comes_back: RefCell<Option<String>>,
        refuse_stage: Option<&'static str>,
        no_door: bool,
        staged: RefCell<Vec<String>>,
        declared: RefCell<Vec<Declared>>,
        kept: RefCell<Vec<String>>,
        marker: RefCell<Option<String>>,
        waited: RefCell<Duration>,
        down_polls: RefCell<u32>,
        /// Boxes whose live door is the OPEN install listener (a keep boot never re-admitted).
        open_door: RefCell<Vec<String>>,
        readmitted: RefCell<Vec<String>>,
        /// The re-admission refused with these words.
        readmit_refused: RefCell<Option<String>>,
    }

    impl Hands for Fake {
        fn running(&self, role: &str) -> Result<Running, String> {
            if self.no_door {
                return Err("REFUSED [live-unsupported] this VM takes no live update".into());
            }
            if *self.down_polls.borrow() > 0 {
                *self.down_polls.borrow_mut() -= 1;
                return Err("connection refused (re-exec)".into());
            }
            if self.open_door.borrow().iter().any(|r| r == role) {
                return Err(OPEN_LISTENER_404.into());
            }
            self.boxes.borrow().get(role).cloned().ok_or_else(|| "no such box".into())
        }
        fn readmit(&self, roles: &[&str]) -> Result<String, String> {
            if let Some(e) = self.readmit_refused.borrow().clone() {
                return Err(e);
            }
            // The re-admission re-feeds every OTHER box of the kept plan, and one at its open
            // install listener never takes a re-feed: the run ends at its budget.
            if let Some(other) = self.open_door.borrow().iter().find(|r| !roles.contains(&r.as_str())) {
                return Err(waited_on(other));
            }
            self.readmitted.borrow_mut().push(roles.join("+"));
            self.open_door.borrow_mut().retain(|r| !roles.contains(&r.as_str()));
            Ok(format!("{} re-admitted by the front after its upgrade (its data kept); 3 box(es) closed", roles.join(" and ")))
        }
        fn stage(&self, role: &str, stage: &Stage, declared: &Declared) -> Result<String, String> {
            self.declared.borrow_mut().push(declared.clone());
            if let Some(w) = self.refuse_stage {
                return Err(w.into());
            }
            self.staged.borrow_mut().push(role.into());
            // A box is down while it re-execs; the front reads its own slot, never down.
            if role != "front" {
                *self.down_polls.borrow_mut() = 2;
            }
            let mut b = self.boxes.borrow_mut();
            let r = b.get_mut(role).unwrap();
            if let Some(app) = self.comes_back.borrow().clone() {
                assert_eq!(app, stage.pin_hex);
                r.app = app;
            }
            r.slot = "a".into();
            Ok("staged".into())
        }
        fn fetch(&self, url: &str) -> Result<Vec<u8>, String> {
            if url.contains("-live-release/") {
                return Ok(b"korp".to_vec());
            }
            Ok(archive())
        }
        fn keep(&self, role: &str) -> Result<String, String> {
            // The keep upgrade's second half is the same re-admission (Gunnar's `upgrade_one`).
            if let Some(other) = self.open_door.borrow().iter().find(|r| *r != role) {
                return Err(waited_on(other));
            }
            self.kept.borrow_mut().push(role.into());
            Ok(format!("the {role} was booted from the release's keep medium"))
        }
        fn wait(&self, d: Duration) {
            *self.waited.borrow_mut() += d;
        }
        fn staged_self(&self, release: &str) -> bool {
            self.marker.borrow_mut().take().is_some_and(|r| r == release)
        }
        fn mark_staged_self(&self, release: &str) -> Result<(), String> {
            *self.marker.borrow_mut() = Some(release.into());
            Ok(())
        }
    }

    /// What gunnar.rs's main answered on `GET /live/running` (2026-10-25 03:54Z): its keep medium
    /// had run, the cloud then put the server in error state, the box booted its upgraded system
    /// and waited for its re-admission — its door the OPEN install listener (`bootstrap::respond`).
    const OPEN_LISTENER_404: &str = "REFUSED [install-path] this listener has /install/hello, /install/log, /install/feed, /install/status, /install/offer, /install/secret-offer and /install/close";

    /// What the twin's keep upgrade answered on gunnar.rs (2026-10-25 11:08Z): its re-admission
    /// re-fed the main, which sat at its open install listener.
    fn waited_on(other: &str) -> String {
        format!("REFUSED [installer-incomplete] after 3606 s: front: no signal; {other}: never answered (last: {OPEN_LISTENER_404}); twin: pair   twin: tunnel wireguard up · joined · role twin · PAIRED")
    }


    fn take(role: &str, live: bool) -> Take {
        let base = "b".repeat(64);
        Take { open: true, release: "03".into(), live, role: role.into(), app: live.then(|| app_of(&base)), front: live.then(|| app_of(&base)), words: String::new() }
    }

    /// ★ Same BASE, live on: the twin gets the release's app live — fetched and proven, staged,
    /// waited for through its re-exec until it runs it confirmed.
    #[test]
    fn same_base_is_staged_live_and_waited_for_until_confirmed() {
        let f = Fake::default();
        let app = app_of(&"b".repeat(64));
        f.boxes.borrow_mut().insert("twin".into(), running(&"b".repeat(64), OLD, "a"));
        *f.comes_back.borrow_mut() = Some(app.app.clone());
        let (o, w) = step(&f, &take("twin", true)).expect("reported");
        assert_eq!(o, DONE_LIVE, "{w}");
        assert!(w.contains("confirmed"), "{w}");
        assert_eq!(*f.staged.borrow(), vec!["twin".to_string()]);
        assert!(f.kept.borrow().is_empty());
    }

    /// ★ The box comes back on its OLD app with no trial: rolled back — reported failed; a stage
    /// korp's legs refuse is failed too, and nothing is kept in its place.
    #[test]
    fn a_box_that_rolls_back_or_refuses_the_stage_is_reported_failed() {
        let f = Fake::default();
        f.boxes.borrow_mut().insert("main".into(), running(&"b".repeat(64), OLD, "a"));
        let (o, w) = step(&f, &take("main", true)).unwrap();
        assert_eq!(o, FAILED);
        assert!(w.contains("[update-rolled-back]"), "{w}");
        assert!(*f.waited.borrow() >= ROLLED_BACK_AFTER);
        let r = Fake { refuse_stage: Some("[live-verify] the signature leg"), ..Fake::default() };
        r.boxes.borrow_mut().insert("main".into(), running(&"b".repeat(64), OLD, "a"));
        let (o, w) = step(&r, &take("main", true)).unwrap();
        assert_eq!(o, FAILED);
        assert!(w.contains("[live-verify]") && r.kept.borrow().is_empty(), "{w}");
    }

    /// ★ BASE moved, live off, the box's own switch, or a box with no live door: the keep
    /// medium through the shop's Upgrade — never a stage.
    #[test]
    fn a_moved_base_live_off_the_boxs_switch_or_no_live_door_go_through_the_keep_medium() {
        for (t, r, no_door) in [
            (take("twin", true), running(&"c".repeat(64), OLD, "a"), false),
            (take("twin", false), running(&"b".repeat(64), OLD, "a"), false),
            (take("twin", true), Running { live_update: false, ..running(&"b".repeat(64), OLD, "a") }, false),
            (take("twin", true), running(&"b".repeat(64), OLD, "a"), true),
        ] {
            let f = Fake { no_door, ..Fake::default() };
            f.boxes.borrow_mut().insert("twin".into(), r);
            let (o, w) = step(&f, &t).unwrap();
            assert_eq!(o, DONE_KEEP, "{w}");
            assert!(f.staged.borrow().is_empty());
            assert_eq!(*f.kept.borrow(), vec!["twin".to_string()]);
        }
    }

    /// ★ The front stages ITSELF and reports after its re-exec (`live`); its BASE moved, it asks
    /// the shop to keep-boot it (`needs-keep`); a box on trial is left for the next look.
    #[test]
    fn the_front_stages_itself_and_reports_after_its_reexec_or_asks_for_its_keep_boot() {
        let f = Fake::default();
        let app = app_of(&"b".repeat(64));
        f.boxes.borrow_mut().insert("front".into(), running(&"b".repeat(64), OLD, "a"));
        *f.comes_back.borrow_mut() = Some(app.app.clone());
        assert_eq!(step(&f, &take("front", true)), None, "staged: it re-execs, nothing reported now");
        assert_eq!(f.marker.borrow().as_deref(), Some("03"));
        *f.down_polls.borrow_mut() = 0;
        let (o, _) = step(&f, &take("front", true)).unwrap();
        assert_eq!(o, DONE_LIVE, "the marker says it was this front's own live update");
        let moved = Fake::default();
        moved.boxes.borrow_mut().insert("front".into(), running(&"c".repeat(64), OLD, "a"));
        assert_eq!(step(&moved, &take("front", true)).unwrap().0, NEEDS_KEEP);
        assert!(moved.kept.borrow().is_empty(), "the front never upgrades itself through Upgrade");
        let trial = Fake::default();
        trial.boxes.borrow_mut().insert("twin".into(), running(&"b".repeat(64), OLD, "b booted"));
        assert_eq!(step(&trial, &take("twin", true)), None);
    }

    /// ★ **The front's own stage that rolls back is reported failed, not staged again** (A32-8):
    /// the front staged itself (the marker left), re-exec'd, its trial failed and korp rolled it
    /// back: it runs its OLD app with no trial. The next look must say so once, and must not
    /// stage the same release into its own slot on every look for ever.
    #[test]
    fn the_fronts_own_stage_that_rolls_back_is_reported_failed_not_restaged() {
        let f = Fake::default();
        f.boxes.borrow_mut().insert("front".into(), running(&"b".repeat(64), OLD, "a"));
        // `comes_back` None: the new app never confirms; korp rolls the slot back to OLD.
        assert_eq!(step(&f, &take("front", true)), None, "staged: it re-execs, nothing reported now");
        assert_eq!(f.staged.borrow().len(), 1);
        let (o, w) = step(&f, &take("front", true)).expect("the rollback is reported on the next look");
        assert_eq!(o, FAILED, "{w}");
        assert!(w.contains("[update-rolled-back]") && w.contains("03"), "{w}");
        assert_eq!(f.staged.borrow().len(), 1, "the release was not staged into its own slot again");
        assert!(f.marker.borrow().is_none(), "the marker is spent");
    }

    /// ★ **What the release declares rides the stage** (lane B5): the release declaration
    /// (bundle and signature, hex as the shop hands them) and the release's env reach the box;
    /// an app that declares neither stages neither.
    #[test]
    fn the_release_declaration_and_env_ride_the_stage() {
        let f = Fake::default();
        let mut app = app_of(&"b".repeat(64));
        app.release = "6b6f7270".into();
        app.release_sig = "77".repeat(64);
        app.env = vec!["korp.env.HOLGER_DATA=/data".into()];
        f.boxes.borrow_mut().insert("twin".into(), running(&"b".repeat(64), OLD, "a"));
        *f.comes_back.borrow_mut() = Some(app.app.clone());
        let t = Take { app: Some(app), ..take("twin", true) };
        assert_eq!(step(&f, &t).unwrap().0, DONE_LIVE);
        assert_eq!(f.declared.borrow()[0], Declared { release: Some((b"korp".to_vec(), vec![0x77; 64])), env: Some(vec!["korp.env.HOLGER_DATA=/data".into()]) });
        let plain = Fake::default();
        plain.boxes.borrow_mut().insert("twin".into(), running(&"b".repeat(64), OLD, "a"));
        step(&plain, &take("twin", true));
        assert_eq!(plain.declared.borrow()[0], Declared::default(), "nothing declared");
    }

    /// ★ feed-big (2026-11-04): **a release declaration by reference is fetched, proven by its
    /// sha256 and length, and rides the stage** — Holger 3.2's declaration carries the signed CVE
    /// bundle (~70 MB), so the shop hands its object's url, never the bytes; bytes that are not
    /// the release's are never staged.
    #[test]
    fn a_release_declaration_by_reference_is_fetched_proven_and_staged() {
        let f = Fake::default();
        let mut app = app_of(&"b".repeat(64));
        app.release_url = "https://objsto.example/release/holger-live-release/k.korp-release?X-Amz=…".into();
        app.release_sha256 = ring::digest::digest(&ring::digest::SHA256, b"korp").as_ref().iter().map(|b| format!("{b:02x}")).collect();
        app.release_len = 4;
        app.release_sig = "77".repeat(64);
        f.boxes.borrow_mut().insert("twin".into(), running(&"b".repeat(64), OLD, "a"));
        *f.comes_back.borrow_mut() = Some(app.app.clone());
        let t = Take { app: Some(app.clone()), ..take("twin", true) };
        assert_eq!(step(&f, &t).unwrap().0, DONE_LIVE);
        assert_eq!(f.declared.borrow()[0].release, Some((b"korp".to_vec(), vec![0x77; 64])));
        let wrong = Fake::default();
        wrong.boxes.borrow_mut().insert("twin".into(), running(&"b".repeat(64), OLD, "a"));
        let mut bad = app;
        bad.release_sha256 = "0".repeat(64);
        let (o, said) = step(&wrong, &Take { app: Some(bad), ..take("twin", true) }).unwrap();
        assert_eq!(o, FAILED, "{said}");
        assert!(said.contains("[update-release-fetch]") && wrong.staged.borrow().is_empty(), "{said}");
    }

    /// ★ **A box that refuses a stage only a keep boot answers** (its release env moved, its
    /// declaration cannot ride, it takes none live) is keep-booted: `done:keep` for main and
    /// twin, `needs-keep` for the front (its marker forgotten); a declaration that does not hold
    /// stays a failure.
    #[test]
    fn a_keep_refusal_of_the_stage_is_answered_with_a_keep_boot() {
        for w in ["REFUSED [live-env-moved] the release changes korp.env.*", "REFUSED [live-release-absent] none rides", "REFUSED [live-declared-unsupported] x"] {
            let f = Fake { refuse_stage: Some(w), ..Fake::default() };
            f.boxes.borrow_mut().insert("twin".into(), running(&"b".repeat(64), OLD, "a"));
            let (o, said) = step(&f, &take("twin", true)).unwrap();
            assert_eq!(o, DONE_KEEP, "{said}");
            assert_eq!(*f.kept.borrow(), vec!["twin".to_string()]);
            let front = Fake { refuse_stage: Some(w), ..Fake::default() };
            front.boxes.borrow_mut().insert("front".into(), running(&"b".repeat(64), OLD, "a"));
            let (o, said) = step(&front, &take("front", true)).unwrap();
            assert_eq!(o, NEEDS_KEEP, "{said}");
            assert!(front.marker.borrow().is_none(), "the front's marker is forgotten: its keep boot is no live update");
        }
        let f = Fake { refuse_stage: Some("REFUSED [release-sig] does not hold"), ..Fake::default() };
        f.boxes.borrow_mut().insert("twin".into(), running(&"b".repeat(64), OLD, "a"));
        assert_eq!(step(&f, &take("twin", true)).unwrap().0, FAILED);
        assert!(f.kept.borrow().is_empty());
    }

    /// ★ The fetched archive must be the release's — sha256, length, pin — and signed.
    #[test]
    fn an_archive_that_is_not_the_releases_is_never_staged() {
        let f = Fake::default();
        let mut bad = app_of(&"b".repeat(64));
        bad.app = "9".repeat(64);
        assert!(staged_of(&f, &bad).unwrap_err().contains("pin"));
        let mut unsigned = app_of(&"b".repeat(64));
        unsigned.sig.clear();
        assert!(staged_of(&f, &unsigned).unwrap_err().contains("[update-unsigned]"));
        let mut sha = app_of(&"b".repeat(64));
        sha.sha256 = "0".repeat(64);
        assert!(staged_of(&f, &sha).unwrap_err().contains("sha256"));
        assert!(step(&Fake::default(), &Take { open: false, ..take("twin", true) }).is_none());
    }

    /// The ONE box's update, in memory: its turn order twin → main → front, refusing a report
    /// out of turn by name, `open = false` once the front reported.
    struct FakeShop {
        turn: RefCell<Vec<&'static str>>,
        reported: RefCell<Vec<(String, String)>>,
        refuse_take: Option<&'static str>,
        live: bool,
    }

    impl FakeShop {
        fn open(live: bool) -> FakeShop {
            FakeShop { turn: RefCell::new(vec!["twin", "main", "front"]), reported: RefCell::default(), refuse_take: None, live }
        }
    }

    impl Shop for FakeShop {
        fn take(&self) -> Result<Take, String> {
            if let Some(w) = self.refuse_take {
                return Err(format!("refused: {w}"));
            }
            Ok(match self.turn.borrow().first() {
                Some(r) => take(r, self.live),
                None => Take::default(),
            })
        }
        fn report(&self, role: &str, outcome: &str, _: &str) -> Result<String, String> {
            let mut turn = self.turn.borrow_mut();
            if turn.first() != Some(&role) {
                return Err(format!("refused: [update-out-of-turn] {role} reported, the turn is {:?}", turn.first()));
            }
            turn.remove(0);
            self.reported.borrow_mut().push((role.into(), outcome.into()));
            Ok(format!("{role}: {outcome} recorded"))
        }
    }

    /// ★ Live off: twin then main are kept and the front needs its keep boot, in the shop's order.
    #[test]
    fn live_off_twin_then_main_are_kept_and_the_front_needs_keep_in_the_shops_order() {
        let shop = FakeShop::open(false);
        let h = Fake::default();
        for r in ["twin", "main", "front"] {
            h.boxes.borrow_mut().insert(r.into(), running(&"b".repeat(64), OLD, "a"));
        }
        for _ in 0..4 {
            look(&shop, &h).expect("every look is in turn");
        }
        assert_eq!(shop.reported.borrow().iter().map(|(r, o)| format!("{r} {o}")).collect::<Vec<_>>(), ["twin done:keep", "main done:keep", "front needs-keep"]);
        assert_eq!(h.kept.borrow().as_slice(), ["twin", "main"], "the front never upgrades itself");
        assert_eq!(look(&shop, &h).unwrap(), None, "nothing open: nothing reported");
    }

    /// ★ Live on: twin, main and front all take the release live, in the shop's order.
    #[test]
    fn live_on_twin_main_and_front_take_the_release_live() {
        let shop = FakeShop::open(true);
        let h = Fake::default();
        for r in ["twin", "main", "front"] {
            h.boxes.borrow_mut().insert(r.into(), running(&"b".repeat(64), OLD, "a"));
        }
        *h.comes_back.borrow_mut() = Some(app_of(&"b".repeat(64)).app);
        for _ in 0..4 {
            look(&shop, &h).expect("every look is in turn");
        }
        assert_eq!(shop.reported.borrow().iter().map(|(r, o)| format!("{r} {o}")).collect::<Vec<_>>(), ["twin done:live", "main done:live", "front done:live"]);
        assert!(h.kept.borrow().is_empty());
    }

    #[test]
    fn a_take_refused_or_an_unknown_role_is_said_by_name() {
        let shop = FakeShop { refuse_take: Some("[update-none-open] no update is open for acme"), ..FakeShop::open(false) };
        assert!(look(&shop, &Fake::default()).unwrap_err().contains("update-none-open"));
        assert_eq!(step(&Fake::default(), &Take { open: true, role: "nas".into(), ..Default::default() }).map(|r| r.0).as_deref(), Some(FAILED));
    }

    /// ★ The bench's loopback bucket is reached at the mock's guestfwd; a real url is as given.
    #[test]
    fn a_bench_loopback_release_object_is_reached_at_the_mocks_guestfwd() {
        assert_eq!(reachable("http://127.0.0.1:17463/objsto/x?X=1"), "http://10.0.2.100:17463/objsto/x?X=1");
        assert_eq!(reachable("https://objsto.example/holger-release/x?X=1"), "https://objsto.example/holger-release/x?X=1");
    }

    /// ★ **While a step runs its box is BUSY** — the recharge watcher leaves it alone — and only
    /// then (lane G5, A7 row 10; Gunnar aa14357b `a_box_is_busy_while_its_step_runs_and_only_then`).
    #[test]
    fn a_box_is_busy_while_its_step_runs_and_only_then() {
        struct Peek(Fake);
        impl Hands for Peek {
            fn running(&self, role: &str) -> Result<Running, String> {
                assert!(crate::frontside::busy::busy(Some(role)) && crate::frontside::busy::busy(None), "the step's own box is busy");
                self.0.running(role)
            }
            fn stage(&self, role: &str, s: &Stage, d: &Declared) -> Result<String, String> {
                self.0.stage(role, s, d)
            }
            fn fetch(&self, u: &str) -> Result<Vec<u8>, String> {
                self.0.fetch(u)
            }
            fn keep(&self, role: &str) -> Result<String, String> {
                assert!(crate::frontside::busy::busy(Some(role)), "busy through its keep boot");
                self.0.keep(role)
            }
            fn wait(&self, d: Duration) {
                self.0.wait(d)
            }
            fn staged_self(&self, r: &str) -> bool {
                self.0.staged_self(r)
            }
            fn mark_staged_self(&self, r: &str) -> Result<(), String> {
                self.0.mark_staged_self(r)
            }
        }
        let p = Peek(Fake { no_door: true, ..Fake::default() });
        assert_eq!(step(&p, &take("twin", true)).unwrap().0, DONE_KEEP);
    }

    /// ★ P4a: **the front's keep boot is said in the product's words** ([`Hands::front_keep`]);
    /// a product that names none gets the neutral words. Nothing else in the step names a product.
    #[test]
    fn the_fronts_keep_boot_is_said_in_the_products_words() {
        struct Named(Fake);
        impl Hands for Named {
            fn running(&self, role: &str) -> Result<Running, String> {
                self.0.running(role)
            }
            fn stage(&self, role: &str, s: &Stage, d: &Declared) -> Result<String, String> {
                self.0.stage(role, s, d)
            }
            fn fetch(&self, u: &str) -> Result<Vec<u8>, String> {
                self.0.fetch(u)
            }
            fn keep(&self, role: &str) -> Result<String, String> {
                self.0.keep(role)
            }
            fn wait(&self, d: Duration) {
                self.0.wait(d)
            }
            fn staged_self(&self, r: &str) -> bool {
                self.0.staged_self(r)
            }
            fn mark_staged_self(&self, r: &str) -> Result<(), String> {
                self.0.mark_staged_self(r)
            }
            fn front_keep(&self) -> String {
                "Acme's front from acme-front-keep".into()
            }
        }
        let named = Named(Fake::default());
        named.0.boxes.borrow_mut().insert("front".into(), running(&"c".repeat(64), OLD, "a"));
        let (o, w) = step(&named, &take("front", true)).unwrap();
        assert_eq!(o, NEEDS_KEEP);
        assert!(w.ends_with("the shop keep-boots Acme's front from acme-front-keep"), "{w}");
        let refused = Named(Fake { refuse_stage: Some("REFUSED [live-env-moved] x"), ..Fake::default() });
        refused.0.boxes.borrow_mut().insert("front".into(), running(&"b".repeat(64), OLD, "a"));
        let (o, w) = step(&refused, &take("front", true)).unwrap();
        assert_eq!(o, NEEDS_KEEP);
        assert!(w.contains("[live-env-moved]") && w.ends_with("the shop keep-boots Acme's front from acme-front-keep"), "{w}");
        let plain = Fake::default();
        plain.boxes.borrow_mut().insert("front".into(), running(&"c".repeat(64), OLD, "a"));
        assert!(step(&plain, &take("front", true)).unwrap().1.ends_with("the shop keep-boots the front from its keep medium"));
    }
    // ── T16: Gunnar's re-admission first (moved from gunnar-front/src/update.rs) ──

    /// Pure: only a main or twin at its OPEN install listener awaits its re-admission.
    #[test]
    fn only_a_box_at_its_open_install_listener_awaits_its_readmission() {
        assert!(awaits_readmission("main", OPEN_LISTENER_404));
        assert!(awaits_readmission("twin", OPEN_LISTENER_404));
        assert!(!awaits_readmission("front", OPEN_LISTENER_404), "the front is never re-admitted by itself");
        assert!(!awaits_readmission("main", "connection refused"));
    }

    /// ★ **A box whose keep boot was never re-admitted** (gunnar.rs main, 2026-10-25): the front
    /// re-admits it from the kept bundle, reads it again and goes on: it runs the release's app
    /// already → done:keep, nothing staged, no second keep.
    #[test]
    fn a_box_left_waiting_for_its_readmission_is_readmitted_then_read_again() {
        let f = Fake::default();
        let app = app_of(&"b".repeat(64));
        f.boxes.borrow_mut().insert("main".into(), running(&"b".repeat(64), &app.app, "a"));
        f.open_door.borrow_mut().push("main".into());
        let (o, w) = step(&f, &take("main", true)).expect("reported");
        assert_eq!(o, DONE_KEEP, "{w}");
        assert!(w.contains("re-admitted") && w.contains("never re-admitted"), "{w}");
        assert_eq!(*f.readmitted.borrow(), vec!["main".to_string()]);
        assert!(f.kept.borrow().is_empty(), "no second keep boot: {w}");
        assert!(f.staged.borrow().is_empty());
    }

    /// ★ Re-admitted, but not on the release (its BASE moved): on to the keep medium as any box.
    #[test]
    fn a_readmitted_box_that_is_not_on_the_release_goes_on_to_its_keep_upgrade() {
        let f = Fake::default();
        f.boxes.borrow_mut().insert("main".into(), running(&"c".repeat(64), OLD, "a"));
        f.open_door.borrow_mut().push("main".into());
        let (o, w) = step(&f, &take("main", true)).expect("reported");
        assert_eq!(o, DONE_KEEP, "{w}");
        assert_eq!(*f.readmitted.borrow(), vec!["main".to_string()]);
        assert_eq!(*f.kept.borrow(), vec!["main".to_string()]);
    }

    /// ★ A re-admission that fails is reported failed by name (the open listener and why); a door
    /// that does not answer at all is NOT taken for one waiting to be re-admitted.
    #[test]
    fn a_failed_readmission_and_a_silent_door_are_reported_failed() {
        let f = Fake::default();
        f.boxes.borrow_mut().insert("main".into(), running(&"b".repeat(64), OLD, "a"));
        f.open_door.borrow_mut().push("main".into());
        *f.readmit_refused.borrow_mut() = Some("the box did not close".into());
        let (o, w) = step(&f, &take("main", true)).unwrap();
        assert_eq!(o, FAILED);
        assert!(w.contains("[install-path]") && w.contains("the box did not close"), "{w}");
        let silent = Fake::default();
        *silent.down_polls.borrow_mut() = 1;
        silent.boxes.borrow_mut().insert("twin".into(), running(&"b".repeat(64), OLD, "a"));
        let (o, w) = step(&silent, &take("twin", true)).unwrap();
        assert_eq!(o, FAILED);
        assert!(w.contains("its live door did not answer"), "{w}");
        assert!(silent.readmitted.borrow().is_empty());
    }

    /// ★ **The twin's keep upgrade with a main left at its open install listener** (gunnar.rs
    /// 2026-10-25, the twin's re-admission waited 3606 s on the main): the main is re-admitted
    /// FIRST, then the twin goes through its keep medium.
    #[test]
    fn a_twin_whose_main_waits_for_its_readmission_readmits_the_main_first_then_keeps() {
        let f = Fake::default();
        f.boxes.borrow_mut().insert("twin".into(), running(&"c".repeat(64), OLD, "a"));
        f.boxes.borrow_mut().insert("main".into(), running(&"c".repeat(64), OLD, "a"));
        f.open_door.borrow_mut().push("main".into());
        let (o, w) = step(&f, &take("twin", true)).expect("reported");
        assert_eq!(o, DONE_KEEP, "{w}");
        assert_eq!(*f.readmitted.borrow(), vec!["main".to_string()], "the main first, on its own");
        assert_eq!(*f.kept.borrow(), vec!["twin".to_string()], "then the twin's keep upgrade");
        assert!(w.contains("main re-admitted"), "the words name the main's re-admission: {w}");
    }

    /// ★ **Both boxes at their open install listener**: ONE re-admission of both, then the twin
    /// is read again — it runs the release's app → done:keep, no second keep boot.
    #[test]
    fn both_boxes_left_waiting_are_readmitted_together_then_read_again() {
        let f = Fake::default();
        let app = app_of(&"b".repeat(64));
        f.boxes.borrow_mut().insert("twin".into(), running(&"b".repeat(64), &app.app, "a"));
        f.boxes.borrow_mut().insert("main".into(), running(&"b".repeat(64), &app.app, "a"));
        f.open_door.borrow_mut().extend(["main".to_string(), "twin".to_string()]);
        let (o, w) = step(&f, &take("twin", true)).expect("reported");
        assert_eq!(o, DONE_KEEP, "{w}");
        assert_eq!(*f.readmitted.borrow(), vec!["twin+main".to_string()], "one re-admission of both");
        assert!(f.open_door.borrow().is_empty());
        assert!(f.kept.borrow().is_empty() && f.staged.borrow().is_empty(), "{w}");
    }

    /// ★ **A front that keeps no bundle refuses the re-admission by name** (the default
    /// [`Hands::readmit`]): the box at its open install listener is reported failed with the
    /// listener's words and the refusal, never taken for a silent door.
    #[test]
    fn a_front_that_keeps_no_bundle_reports_the_open_listener_and_its_refusal() {
        struct NoBundle(Fake);
        impl Hands for NoBundle {
            fn running(&self, role: &str) -> Result<Running, String> {
                self.0.running(role)
            }
            fn stage(&self, role: &str, s: &Stage, d: &Declared) -> Result<String, String> {
                self.0.stage(role, s, d)
            }
            fn fetch(&self, u: &str) -> Result<Vec<u8>, String> {
                self.0.fetch(u)
            }
            fn keep(&self, role: &str) -> Result<String, String> {
                self.0.keep(role)
            }
            fn wait(&self, d: Duration) {
                self.0.wait(d)
            }
            fn staged_self(&self, r: &str) -> bool {
                self.0.staged_self(r)
            }
            fn mark_staged_self(&self, r: &str) -> Result<(), String> {
                self.0.mark_staged_self(r)
            }
        }
        let f = NoBundle(Fake::default());
        f.0.boxes.borrow_mut().insert("main".into(), running(&"b".repeat(64), OLD, "a"));
        f.0.open_door.borrow_mut().push("main".into());
        let (o, w) = step(&f, &take("main", true)).unwrap();
        assert_eq!(o, FAILED);
        assert!(w.contains("[install-path]") && w.contains("[readmit-unsupported]"), "{w}");
    }
}