← Commit history

Bind Fields recording to the exact Pup session and refuse failed-tour proof

John Lauer ·f876b3ee6f · ·parent 29fec55
1 file changed +11−8
crates/adom-aiflow/src/main.rs+11−8
@@ -3964,16 +3964,17 @@ fn main() {                     let mut hwnd: Option<i64> = None;                     for _ in 0..40 {                         std::thread::sleep(std::time::Duration::from_secs(2));-                        let lw = br.call("desktop_list_windows", &json!({}));-                        // the relay wraps some answers as a JSON string under `output`+                        let lw = br.call("pup_list_windows", &json!({}));                         let inner: Value = lw["output"].as_str().and_then(|t| serde_json::from_str(t).ok()).unwrap_or(Value::Null);-                        let d = lw.get("data").cloned().or_else(|| inner.get("data").cloned()).unwrap_or(lw.clone());-                        let wins = d["windows"].as_array().cloned().or_else(|| lw["windows"].as_array().cloned()).or_else(|| inner["windows"].as_array().cloned()).unwrap_or_default();-                        // the app's window, not a wiki page about it: the app titles itself exactly "Adom Fields"-                        hwnd = wins.iter().find(|w| w["title"].as_str().map(|t| (t == "Adom Fields" || t.starts_with("Adom Fields - ")) && !t.contains("Wiki") && !t.contains("wiki")).unwrap_or(false)).and_then(|w| w["hwnd"].as_i64().or_else(|| w["hwnd"].as_str().and_then(|h| h.parse().ok())));+                        let sessions = lw["sessions"].as_array().or_else(|| inner["sessions"].as_array());+                        // Only the session created by this invocation owns the recording target.+                        // A title match can select another user's or another run's Fields window.+                        hwnd = sessions.and_then(|rows| rows.iter().find(|w| w["sessionId"].as_str() == Some(sid.as_str())))+                            .and_then(|w| w["hwnd"].as_i64().or_else(|| w["hwnd"].as_str().and_then(|h| h.parse().ok())))+                            .filter(|h| *h > 0);                         if hwnd.is_some() { break; }                     }-                    let Some(win) = hwnd else { let _ = br.call("pup_close_window", &json!({"sessionId": sid})); err(&format!("the app's window did not appear on {} (no window titled Adom Fields{})", br.target, open_err.map(|e| format!("; pup said {e}")).unwrap_or_default()), &["`adom-bridge --target <box> pup_readiness` says whether pup is ready there; a browser_gone means pup's browser died: `pup_close_window` the old sessions or restart pup on the box.".into(), if preboard::proxy_is_local(&proxy) { format!("The URL {proxy} is a localhost proxy: only the machine hosting this container can open it. `tour fields --on <that machine>`.") } else { String::new() }, on_note.clone()]); };+                    let Some(win) = hwnd else { let _ = br.call("pup_close_window", &json!({"sessionId": sid})); err(&format!("the app's window did not appear on {} (no window for this walkthrough session{})", br.target, open_err.map(|e| format!("; pup said {e}")).unwrap_or_default()), &["`adom-bridge --target <box> pup_readiness` says whether pup is ready there; a browser_gone means pup's browser died: `pup_close_window` the old sessions or restart pup on the box.".into(), if preboard::proxy_is_local(&proxy) { format!("The URL {proxy} is a localhost proxy: only the machine hosting this container can open it. `tour fields --on <that machine>`.") } else { String::new() }, on_note.clone()]); };                     let save_to = std::fs::canonicalize(&dir).unwrap_or(dir.clone()).display().to_string();                     let (step, visit) = (r.data["currentStep"].as_str().unwrap_or("fields").to_string(), r.data["steps"][r.data["currentStep"].as_str().unwrap_or("fields")]["visits"].as_array().map(|a| a.len()).unwrap_or(1));                     // the editor clip of this visit is not the walkthrough: the app window is the camera@@ -4010,7 +4011,9 @@ fn main() {                     let fields: Value = std::process::Command::new("curl").args(["-s", &format!("http://127.0.0.1:{port}/fields.json")]).output().ok().and_then(|o| serde_json::from_slice(&o.stdout).ok()).unwrap_or(Value::Null);                     let issues = fields["issues"].as_array().cloned().unwrap_or_default();                     r.stage_end("tour-fields", Some(json!({"issues": issues.len(), "tour": tour_ok})), None);-                    r.outcome("tour-fields", json!({"issues": issues, "nets": fields["nets"], "chips": fields["chips"], "heat": fields["heat"]}));+                    if tour_ok {+                        r.outcome("tour-fields", json!({"issues": issues, "nets": fields["nets"], "chips": fields["chips"], "heat": fields["heat"]}));+                    }                     r.log("artifact", json!({"step": step, "visit": visit, "kind": "fields-summary", "issues": issues.len(), "peakJ": fields["scales"]["jMax"], "peakRiseC": fields["heat"]["peakRiseC"]}));                     r.save().unwrap();                     if !tour_ok { err("the app's walkthrough did not run (adom-fields tour failed)", &["`adom-fields tour` by hand shows why; the clip of this visit still exists.".into()]); }