← Commit history

v2.0.723 (insiders): Closing an isolated pup window can no longer end the main browser. A health sweep dropped the isolated lane while its close was still waiting, the cleanup then fell back to the shared adom-you lane, wiped its record, and the next open ended the live browser as an orphan, taking every pup window with it (John's laptop, 2026-10-15). The lane is now fixed for the whole cleanup, a missing lane is never replaced by adom-you, and a browser that is polling is adopted instead of ended. Replayed on arav-rog and ConfRoomROG (main window open, isolated window opened and closed): every window survived. Gates: smoke all pass, foreground pass, parity 55/56, jump list 11/14 (baseline). Includes 2.0.722.

John Lauer ·87c80c4f68 ·26d ago ·parent a584fb6
4 files changed +53−21
rust/Cargo.lock+12−12
@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"  [[package]] name = "adom-pup-bridge"-version = "2.0.722"+version = "2.0.723" dependencies = [  "pup-browser",  "pup-cdp",@@ -781,7 +781,7 @@ dependencies = [  [[package]] name = "pup-browser"-version = "2.0.722"+version = "2.0.723" dependencies = [  "pup-core",  "pup-host",@@ -793,7 +793,7 @@ dependencies = [  [[package]] name = "pup-cdp"-version = "2.0.722"+version = "2.0.723" dependencies = [  "serde_json",  "thiserror",@@ -803,7 +803,7 @@ dependencies = [  [[package]] name = "pup-core"-version = "2.0.722"+version = "2.0.723" dependencies = [  "serde",  "serde_json",@@ -813,7 +813,7 @@ dependencies = [  [[package]] name = "pup-dashboard"-version = "2.0.722"+version = "2.0.723" dependencies = [  "pup-cdp",  "pup-core",@@ -826,7 +826,7 @@ dependencies = [  [[package]] name = "pup-host"-version = "2.0.722"+version = "2.0.723" dependencies = [  "serde",  "serde_json",@@ -835,7 +835,7 @@ dependencies = [  [[package]] name = "pup-host-mac"-version = "2.0.722"+version = "2.0.723" dependencies = [  "core-foundation",  "core-graphics",@@ -851,7 +851,7 @@ dependencies = [  [[package]] name = "pup-host-win"-version = "2.0.722"+version = "2.0.723" dependencies = [  "pup-core",  "pup-host",@@ -861,7 +861,7 @@ dependencies = [  [[package]] name = "pup-identity"-version = "2.0.722"+version = "2.0.723" dependencies = [  "fontdue",  "image",@@ -871,7 +871,7 @@ dependencies = [  [[package]] name = "pup-session"-version = "2.0.722"+version = "2.0.723" dependencies = [  "pup-core",  "serde",@@ -881,7 +881,7 @@ dependencies = [  [[package]] name = "pup-vault"-version = "2.0.722"+version = "2.0.723" dependencies = [  "pup-core",  "pup-host",@@ -892,7 +892,7 @@ dependencies = [  [[package]] name = "pup-verbs"-version = "2.0.722"+version = "2.0.723" dependencies = [  "base64",  "png 0.17.16",
rust/Cargo.toml+1−1
@@ -4,7 +4,7 @@ members = ["core", "host", "host-win", "host-mac", "cdp", "browser", "session",  # One version for the whole bridge, in lockstep with src/BRIDGE_VERSION (tools/ship.sh bumps both). [workspace.package]-version = "2.0.722"+version = "2.0.723" edition = "2021" license = "MIT" 
rust/verbs/src/extdrive.rs+17−1
@@ -54,7 +54,9 @@ pub fn default_lane() -> std::sync::Arc<LaneState> { DEFAULT_LANE.get_or_init(|| pub fn st() -> std::sync::Arc<LaneState> {     let p = CUR_PROFILE.with(|c| c.borrow().clone());     if p.is_empty() || p == "adom-you" { return default_lane(); }-    lane_for_profile(&p).unwrap_or_else(default_lane)+    // 2.0.723: never fall back to the adom-you lane for another profile. A lane dropped mid-call (the idle sweep of+    // an isolated profile) made a close clear the adom-you lane and end the user's main browser.+    ensure_lane(&p) } pub fn cur_profile() -> String { let p = CUR_PROFILE.with(|c| c.borrow().clone()); if p.is_empty() { "adom-you".into() } else { p } } pub fn enter_profile(profile: &str) {@@ -907,6 +909,20 @@ pub fn ext_handle() -> std::sync::Arc<crate::BrowserHandle> {  #[cfg(test)] mod tests {+    // 2.0.723: a lane dropped while its thread still works in it (the idle sweep during a 20 s close) must never+    // resolve to the adom-you lane; that wiped the user's main browser record and ended it.+    #[test] fn dropped_lane_never_aliases_adom_you() {+        super::default_lane().token.lock().unwrap().clear();+        *super::default_lane().token.lock().unwrap() = "keep-me".into();+        super::enter_profile("iso-test-723");+        super::drop_lane_for_profile("iso-test-723");+        let l = super::st();+        assert_eq!(l.profile, "iso-test-723");+        l.token.lock().unwrap().clear();+        assert_eq!(*super::default_lane().token.lock().unwrap(), "keep-me");+        super::leave_profile();+        super::drop_lane_for_profile("iso-test-723");+    }     #[test] fn blank_or_birth() {         assert!(super::is_blank_or_birth("")); assert!(super::is_blank_or_birth("about:blank"));         assert!(super::is_blank_or_birth("chrome-extension://liomcaladmooagjiebkfgegciadapnpi/birth.html?token=b1y2"));
rust/verbs/src/lib.rs+23−7
@@ -120,15 +120,21 @@ pub fn wait_profile_free(ctx: &Ctx, profile: &str, max: Duration) -> bool { }  pub fn kill_extdrive(ctx: &Ctx) -> u32 {-    let pids: Vec<u32> = extdrive::st().pids.lock().unwrap().clone();+    // 2.0.723 (John's laptop, 2026-10-15: every pup window vanished). The lane is chosen ONCE. Closing an isolated+    // window waited 20 s for its profile to free up, the health sweep dropped that lane meanwhile, and the clears+    // below then resolved st() to the adom-you lane: its token and pids were wiped, and the next open ended the+    // user's live adom-you browser as an "orphan" (18 processes).+    let lane = extdrive::st();+    let lane_path = lane_file(ctx);+    let pids: Vec<u32> = lane.pids.lock().unwrap().clone();     let mut n = 0;     for p in pids { if ctx.host.alive(p) { ctx.host.kill(p); n += 1; } }-    if n > 0 { let _ = wait_profile_free(ctx, &extdrive::cur_profile(), Duration::from_secs(20)); }-    extdrive::st().pids.lock().unwrap().clear();-    extdrive::st().token.lock().unwrap().clear();-    extdrive::mark_dead();-    extdrive::reset_worker_version();   // 2.0.647: the next hello is the truth, not the last lane's-    let _ = std::fs::remove_file(lane_file(ctx));+    if n > 0 { let _ = wait_profile_free(ctx, &lane.profile, Duration::from_secs(20)); }+    lane.pids.lock().unwrap().clear();+    lane.token.lock().unwrap().clear();+    *lane.last_poll.lock().unwrap() = 0;+    lane.worker.lock().unwrap().clear();   // 2.0.647: the next hello is the truth, not the last lane's+    let _ = std::fs::remove_file(lane_path);     if n > 0 { pup_core::log(format!("[extdrive] closed {n} browser process(es)")); }     n }@@ -299,6 +305,16 @@ pub fn launch_extdrive(ctx: &Ctx, url: &str) -> Result<u32, Value> {             while t0.elapsed() < Duration::from_secs(10) { if !lock.exists() { break; } std::thread::sleep(Duration::from_millis(200)); }         }     }+    // 2.0.723: a browser on this profile that polled the lane in the last 10 s is ALIVE and connected, whatever the+    // pid list says (it can be emptied by a lost lane record). Adopt it; never end it as stale or orphaned.+    if extdrive::connected_ms_ago() < 10_000 {+        let live = orphan_browser_pids(ctx, &dir);+        if !live.is_empty() {+            set_extdrive_pids(live.clone()); persist_lane(ctx);+            pup_core::log(format!("[lane] profile \"{profile}\": its browser is connected ({} process(es)) but pup had no pids for it; adopted, not relaunched", live.len()));+            return Ok(live[0]);+        }+    }     let stale = kill_extdrive(ctx);   // 2.0.686: kill_extdrive now waits until the profile is truly free     if stale > 0 {         pup_core::log(format!("[extdrive] ended {stale} stale browser process(es) holding profile \"{profile}\" before relaunching"));