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

nordisk/holger

holger-estate-front/src/update.rs

//! **The front does the update its shop opened** — the update step is SHARED since P4a
//! (2026-10-25): Skidbladnir nordisk-pairing `frontside::update` (`decide`, `staged_of`,
//! `declared_of`, `wait_confirmed`, `step`, `look`, the [`Hands`] and [`Shop`] seams), moved there
//! from this file; Gunnar's front takes it in T16. The shop's side of it ([`Shop`]) is
//! monetize-estate-side's `ShopDoor` (feature `metered`). What stays Holger's: its hands
//! (`crate::sold::EstateHands`: the boxes' live doors over the kept material, the release
//! fetch, the keep upgrade through the ONE box) and the words of its front's keep boot.

pub use nordisk_pairing::frontside::update::*;

/// **What the shop keep-boots when Holger's front needs it** (`needs-keep`): its front, from
/// the `holger-front-keep` medium ([`Hands::front_keep`]).
pub const FRONT_KEEP: &str = "Holger's front from holger-front-keep";

#[cfg(test)]
mod tests {
    use super::*;
    use nordisk_pairing::live::{Declared, Running, Stage};
    use std::time::Duration;

    /// A front whose own slot is on an old BASE: only its keep boot can lay the release.
    struct OldBase;

    impl Hands for OldBase {
        fn running(&self, _: &str) -> Result<Running, String> {
            Ok(Running {
                base: "c".repeat(64),
                app: "0".repeat(64),
                slot: "a".into(),
                live_update: true,
            })
        }
        fn stage(&self, _: &str, _: &Stage, _: &Declared) -> Result<String, String> {
            panic!("a moved BASE is never staged")
        }
        fn fetch(&self, _: &str) -> Result<Vec<u8>, String> {
            panic!("a moved BASE is never fetched")
        }
        fn keep(&self, _: &str) -> Result<String, String> {
            panic!("the front never upgrades itself through Upgrade")
        }
        fn wait(&self, _: Duration) {}
        fn staged_self(&self, _: &str) -> bool {
            false
        }
        fn mark_staged_self(&self, _: &str) -> Result<(), String> {
            Ok(())
        }
        fn front_keep(&self) -> String {
            FRONT_KEEP.into()
        }
    }

    /// ★ P4a: on the shared step, **Holger's front asks for its keep boot in the words it always
    /// did** — the shop keep-boots Holger's front from holger-front-keep.
    /// What a Holger main answers on `GET /live/running` after its keep medium ran in place and
    /// its upgraded system booted: the OPEN install listener (nordisk-pairing `bootstrap::respond`).
    const OPEN_LISTENER: &str = "REFUSED [install-path] this listener has /install/hello, /install/log, /install/feed, /install/status, /install/offer, /install/secret-offer and /install/close";

    /// A Holger estate whose main waits at its open install listener; the front re-admits.
    struct Waiting {
        readmitted: std::cell::RefCell<Vec<String>>,
        app: String,
    }

    impl Hands for Waiting {
        fn running(&self, role: &str) -> Result<Running, String> {
            if role == "main" && self.readmitted.borrow().is_empty() {
                return Err(OPEN_LISTENER.into());
            }
            Ok(Running {
                base: "b".repeat(64),
                app: self.app.clone(),
                slot: "a".into(),
                live_update: true,
            })
        }
        fn stage(&self, _: &str, _: &Stage, _: &Declared) -> Result<String, String> {
            panic!("a box on the release is never staged")
        }
        fn fetch(&self, _: &str) -> Result<Vec<u8>, String> {
            panic!("nothing is fetched")
        }
        fn keep(&self, role: &str) -> Result<String, String> {
            panic!("no second keep boot of the {role}")
        }
        fn wait(&self, _: Duration) {}
        fn staged_self(&self, _: &str) -> bool {
            false
        }
        fn mark_staged_self(&self, _: &str) -> Result<(), String> {
            Ok(())
        }
        fn readmit(&self, roles: &[&str]) -> Result<String, String> {
            self.readmitted.borrow_mut().push(roles.join("+"));
            Ok(format!(
                "{} re-admitted by the front after its upgrade (its data kept)",
                roles.join(" and ")
            ))
        }
    }

    /// ★ T16 (RED before the shared step took Gunnar's re-admission): **a Holger main left at
    /// its open install listener is re-admitted first**, read again, and — on the release
    /// already — reported `done:keep`, never "its live door did not answer".
    #[test]
    fn a_holger_main_left_at_its_open_listener_is_readmitted_and_reported_kept() {
        let h = Waiting {
            readmitted: Default::default(),
            app: "1".repeat(64),
        };
        let app = LiveApp {
            base: "b".repeat(64),
            app: "1".repeat(64),
            sig: "ab".into(),
            ..LiveApp::default()
        };
        let t = Take {
            open: true,
            release: "04".into(),
            live: true,
            role: "main".into(),
            app: Some(app),
            ..Take::default()
        };
        let (outcome, words) = step(&h, &t).expect("reported");
        assert_eq!(outcome, DONE_KEEP, "{words}");
        assert_eq!(*h.readmitted.borrow(), vec!["main".to_string()]);
        assert!(words.contains("never re-admitted"), "{words}");
    }

    #[test]
    fn holgers_front_asks_for_its_keep_boot_in_holgers_words() {
        let app = LiveApp {
            base: "b".repeat(64),
            app: "1".repeat(64),
            sig: "ab".into(),
            ..LiveApp::default()
        };
        let t = Take {
            open: true,
            release: "03".into(),
            live: true,
            role: "front".into(),
            front: Some(app),
            ..Take::default()
        };
        let (outcome, words) = step(&OldBase, &t).expect("reported");
        assert_eq!(outcome, NEEDS_KEEP);
        assert!(
            words.ends_with(": the shop keep-boots Holger's front from holger-front-keep"),
            "{words}"
        );
    }
}