← Commit history

v2.0.724 (insiders): A pup window can no longer sit on the taskbar titled PUPBIRTH-... A new window starts on pup's birth page and is then sent to its address; when that step failed, the failure was swallowed and the window stayed on the birth page for good (John's laptop, 2026-10-17). The open now retries the navigate once, and the health check sends any window still on its birth page 20 s after birth to the address its session recorded (up to 3 tries; this net could not be forced to fire in testing). Gates on arav-rog and ConfRoomROG: smoke all pass, foreground pass, parity 55/56, jump list 11/14 (baseline); the isolated-close replay kept every window on both. Includes 2.0.723.

John Lauer ·c931f9cb90 ·24d ago ·parent 2a19634
5 files changed +50−14
rust/Cargo.lock+12−12
@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"  [[package]] name = "adom-pup-bridge"-version = "2.0.723"+version = "2.0.724" dependencies = [  "pup-browser",  "pup-cdp",@@ -781,7 +781,7 @@ dependencies = [  [[package]] name = "pup-browser"-version = "2.0.723"+version = "2.0.724" dependencies = [  "pup-core",  "pup-host",@@ -793,7 +793,7 @@ dependencies = [  [[package]] name = "pup-cdp"-version = "2.0.723"+version = "2.0.724" dependencies = [  "serde_json",  "thiserror",@@ -803,7 +803,7 @@ dependencies = [  [[package]] name = "pup-core"-version = "2.0.723"+version = "2.0.724" dependencies = [  "serde",  "serde_json",@@ -813,7 +813,7 @@ dependencies = [  [[package]] name = "pup-dashboard"-version = "2.0.723"+version = "2.0.724" dependencies = [  "pup-cdp",  "pup-core",@@ -826,7 +826,7 @@ dependencies = [  [[package]] name = "pup-host"-version = "2.0.723"+version = "2.0.724" dependencies = [  "serde",  "serde_json",@@ -835,7 +835,7 @@ dependencies = [  [[package]] name = "pup-host-mac"-version = "2.0.723"+version = "2.0.724" dependencies = [  "core-foundation",  "core-graphics",@@ -851,7 +851,7 @@ dependencies = [  [[package]] name = "pup-host-win"-version = "2.0.723"+version = "2.0.724" dependencies = [  "pup-core",  "pup-host",@@ -861,7 +861,7 @@ dependencies = [  [[package]] name = "pup-identity"-version = "2.0.723"+version = "2.0.724" dependencies = [  "fontdue",  "image",@@ -871,7 +871,7 @@ dependencies = [  [[package]] name = "pup-session"-version = "2.0.723"+version = "2.0.724" dependencies = [  "pup-core",  "serde",@@ -881,7 +881,7 @@ dependencies = [  [[package]] name = "pup-vault"-version = "2.0.723"+version = "2.0.724" dependencies = [  "pup-core",  "pup-host",@@ -892,7 +892,7 @@ dependencies = [  [[package]] name = "pup-verbs"-version = "2.0.723"+version = "2.0.724" 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.723"+version = "2.0.724" edition = "2021" license = "MIT" 
rust/verbs/src/extdrive.rs+31
@@ -654,6 +654,37 @@ pub fn rebind_unbound(ctx: &Ctx) -> usize { /// reload_stale_worker never cycles the browser while this is above zero: a window with no session has no file, /// so the cycle would close it for good (John's laptop, 2.0.694 install: an "AI Flow" page and a YouTube video). pub static STRAYS_WAITING: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(usize::MAX);   // MAX = not scanned yet+/// 2.0.724: a pup window whose tab is still on the birth page 20 s after it was born never reached its address (the+/// navigate after birth failed or timed out). It sat on John's taskbar titled PUPBIRTH-... (2026-10-17). Send it to the+/// address its session recorded; at most 3 tries per tab, a minute apart.+pub fn rescue_birth_tabs(ctx: &Ctx) -> usize {+    static TRIED: Mutex<Option<std::collections::HashMap<i64, (u32, u64)>>> = Mutex::new(None);+    if !alive(ctx) { return 0; }+    let Ok(r) = run(json!({ "action": "tabs" }), Duration::from_secs(10)) else { return 0 };+    let tabs = r["tabs"].as_array().cloned().unwrap_or_default();+    let now = pup_core::now_ms();+    let sessions = ctx.sessions.all();+    let mut tried = TRIED.lock().unwrap();+    let tried = tried.get_or_insert_with(Default::default);+    let mut n = 0;+    for t in &tabs {+        let u = t["url"].as_str().unwrap_or("");+        if !(u.contains("/birth.html") && u.starts_with("chrome-extension://")) { continue; }+        let (Some(tab), Some(wid)) = (t["tabId"].as_i64(), t["windowId"].as_i64()) else { continue };+        let Some(s) = sessions.iter().find(|s| s.window_id == Some(wid)) else { continue };+        if now.saturating_sub(s.born_at_ms.max(s.created_ms)) < 20_000 { continue; }+        let target = s.tabs.iter().find(|x| x.target_id == tab.to_string()).or_else(|| s.tabs.get(0)).map(|x| x.url.clone()).unwrap_or_default();+        if target.is_empty() || is_blank_or_birth(&target) { continue; }+        let e = tried.entry(tab).or_insert((0, 0));+        if e.0 >= 3 || now.saturating_sub(e.1) < 60_000 { continue; }+        *e = (e.0 + 1, now);+        let ok = run(json!({ "action": "navigate", "url": target, "tabId": tab, "waitMs": 20000 }), Duration::from_secs(25)).is_ok();+        pup_core::log(format!("[birth] \"{}\" tab {tab} was still on the birth page {} s after birth; sent it to {target} (try {}): {}", s.id, now.saturating_sub(s.born_at_ms.max(s.created_ms)) / 1000, e.0, if ok { "ok" } else { "failed" }));+        if ok { n += 1; }+    }+    n+}+ pub fn adopt_stray_windows(ctx: &Ctx) -> usize {     static SEEN: Mutex<Option<std::collections::HashMap<i64, u64>>> = Mutex::new(None);     if !alive(ctx) { return 0; }
rust/verbs/src/lib.rs+5−1
@@ -2024,7 +2024,11 @@ pub fn open_window_ext(ctx: &Ctx, caller: &Caller, args: &Value, sid: &str, owne                 "progress": { "phase": "navigating", "elapsedSec": t_verb.elapsed().as_secs(), "percent": 70 },                 "_hint": "This box is slow: the window exists, is yours and is parked in the background, and the page is still loading. Poll pup_window_info {} (url/title fill in) or pup_readiness (active[]) rather than opening again; a second open with the same window label is refused as owned." }));         }-        let nav = match extdrive::run(json!({ "action": "navigate", "url": url, "tabId": chrome_tab, "waitMs": 25000 }), Duration::from_secs(30)) {+        // 2.0.724 (John, 2026-10-17: a window on his taskbar titled PUPBIRTH-...): a failed navigate here was+        // swallowed, the open answered success, and the window stayed on the birth page for good. Retry once; the+        // health tick's rescue_birth_tabs is the net for anything that still sticks.+        let nav_try = || extdrive::run(json!({ "action": "navigate", "url": url, "tabId": chrome_tab, "waitMs": 25000 }), Duration::from_secs(30));+        let nav = match nav_try().or_else(|e| { pup_core::log(format!("[window] \"{sid}\" first navigate off the birth page failed ({}); retrying once", e["error"].as_str().unwrap_or("?"))); std::thread::sleep(Duration::from_millis(1500)); nav_try() }) {             Ok(v) => v,             Err(e) => {                 if s.hwnd.map(|h| !ctx.host.is_alive(h)).unwrap_or(false) && !extdrive::alive(ctx) {
rust/verbs/src/recovery.rs+1
@@ -646,6 +646,7 @@ pub fn health_tick(ctx: &Ctx) {     crate::extdrive::adopt_tokenless(ctx);   // 2.0.580: a token-less extension browser after a restart     forget_user_closed(ctx);                 // 2.0.595: the user's close is final     lane_gone_pass(ctx);                     // 2.0.595: a gone lane is forgotten unless it provably crashed+    crate::extdrive::rescue_birth_tabs(ctx);   // 2.0.724: a window stuck on its birth page (PUPBIRTH-... on the taskbar)     crate::extdrive::adopt_stray_windows(ctx);   // 2.0.694: windows Chrome made outside pup (a dragged-out tab, a page's popup)     crate::extdrive::rebind_unbound(ctx);   // ext sessions whose birth outran the hwnd wait: bind + stamp now     crate::extdrive::reload_stale_worker(ctx);   // a bridge_install must not wait for every thread's window to close