← Commit history
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.11.0+0.11.1
Cargo.lock+1−1
@@ -122,7 +122,7 @@ dependencies = [  [[package]] name = "swx-bridge"-version = "0.11.0"+version = "0.11.1" dependencies = [  "miniz_oxide",  "serde",
Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "swx-bridge"-version = "0.11.0"+version = "0.11.1" edition = "2021" rust-version = "1.80" description = "Adom SOLIDWORKS bridge — swx_* verbs over the SOLIDWORKS COM API, as a single native binary"
README.md+38−1
@@ -506,7 +506,44 @@ not.  ## Roadmap -- v0.11.0 (this): **CSWA phase 1 — exam parts as release validation.** Drew:+- v0.11.1 (this): **which verb put that dialog up?** Measured 2026-10-15 on+  adom_drew2:+  - a File ▸ Open dialog appeared during a bridge battery, with Drew away;+  - it was `"Open "`, `#32770`, owned by the SOLIDWORKS main frame, with the+    file name empty;+  - every `swx_stop` after it was `stop_blocked_by_dialog`;+  - the census called it `kind:"unknown"`, `bridgeCaused:false`, and+    `swx_state` once reported `blockingDialogs: []` while it was up.++  Now:+  - **Every verb is bracketed by a dialog census.** One census runs before the+    verb (after it is marked in flight), and one after. A dialog proved absent+    in the first and present in the second appeared DURING that verb.+    - Replies carry `dialogsAppeared`, and the ledger records `dialog_appeared`.+    - Sightings persist (`dialog-sightings.json`) with first-seen and+      last-proved-absent times and the user's last input.+    - Anything first seen outside a bracket stays unattributed.+  - **The census is native and deterministic.** It covers every top-level+    window of SOLIDWORKS and its CEF children: every `#32770`, plus visible+    owned popups. It runs on every `swx_state` call, `screenshot:false`+    included (that path used to skip the census and say `[]`). ab's list is+    only the fallback. No visible modal is dropped: the quit preconditions+    report `moreBlockingDialogs`.+  - **`swx_stop`'s refusal names the dialog:** title, hwnd, class, owner, rect,+    `ageSeconds`, attribution, and whether the bridge may cancel it.+  - **The one dialog the bridge may cancel:** the standard Windows file dialog+    (file-name control 1148/1152 and IDCANCEL), with an EMPTY name read by+    WM_GETTEXT. A bridge verb must provably have raised it: proved absent after+    the verb began, and no user input since. `swx_dismiss_blocking_dialogs+    {close, confirm, reason}` sends IDCANCEL, a no-op, and ledgers+    `dialog_cancelled` with the attribution. Anything with text typed, or of+    unknown origin, is the user's.+  - **Quit rung (b) reads `swCommands_App_Exit` from the installed interop**+    and does not send `RunCommand` unless it reads 2825. A wrong command id+    could raise any dialog.++  681 tests.+- v0.11.0: **CSWA phase 1 — exam parts as release validation.** Drew:   "pull 3-5 random parts from CSWA exams as part of our e2e testing/validation   for each release … We need this bridge to be a real, useable tool, for more   than the basics." Every new op is built and simulated, and NOT YET MEASURED
SKILL.md+5
@@ -92,6 +92,11 @@ BEFORE you do it: 8. **`swx_stop` quits only a session with zero documents open**, and `force`    never waives that. Treat a quit as close to a one-way door: relaunching takes    minutes and can stop at the sign-in window again.+   A dialog blocking a quit is NAMED: the refusal gives its hwnd, owner, age+   and `attribution` (the bridge verb that raised it, if one provably did). The+   bridge cancels only a standard file dialog with an EMPTY name that it raised+   itself (`swx_dismiss_blocking_dialogs {close, confirm, reason}`); every other+   dialog is the user's to answer. 9. **Never end a session the user is working in, and never pile windows on    them.** Every quit and sweep refuses `user_is_active` when SOLIDWORKS is in    front with recent input, or documents appeared that the bridge did not open;
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "solidworks",   "displayName": "SOLIDWORKS",-  "version": "0.11.0",+  "version": "0.11.1",   "description": "Bridge for SOLIDWORKS desktop CAD — create and open documents, a modeling DSL, file management (find/open/save/export/Pack and Go), custom properties, BOM generation, design analysis (mass properties, bodies, parameters, interference, rebuild health) and background window/dialog etiquette, over the SOLIDWORKS COM API. Single native binary: no interpreter, no PowerShell, ~10ms cold start.",   "author": "Adom Inc.",   "license": "MIT",
install.sh

  
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "solidworks-bridge",-  "version": "0.11.0",+  "version": "0.11.1",   "type": "app",   "description": "Skills for your container so your AI knows how to drive the SOLIDWORKS bridge (swx_* verbs). The bridge runtime itself is the release zip — a single native swx-bridge.exe; Adom Bridge loads that.",   "tags": [
src/comhost/com.rs+21
@@ -1332,6 +1332,27 @@ impl ComBackend {             }             "b" => {                 let sw = self.app(false, true)?;+                // THE COMMAND ID IS READ, NOT ASSUMED (0.11.1). RunCommand runs+                // whatever command the number names; a wrong one could raise any+                // dialog (a File ▸ Open blocked every quit on 2026-10-15, origin+                // under investigation). swCommands_e.swCommands_App_Exit is read+                // from the installed interop; unreadable or different from 2825,+                // the rung does not send it.+                let exe = sw.string("GetExecutablePath", &[]).unwrap_or_default();+                let verified = swconst_ids(&exe, &[("swCommands_e", "swCommands_App_Exit")])+                    .ok()+                    .and_then(|m| m.get("swCommands_App_Exit").copied());+                if verified != Some(crate::quit::SW_COMMANDS_APP_EXIT) {+                    detail.push(format!(+                        "RunCommand NOT sent: swCommands_App_Exit reads {verified:?} from the installed interop, not {} — \+                         a command id that cannot be verified is never run",+                        crate::quit::SW_COMMANDS_APP_EXIT+                    ));+                    drop(sw);+                    self.sw = None;+                    detail.push("released every COM reference".into());+                    return Ok(json!({"rung": rung, "result": "declined", "detail": detail.join("; "), "comReferencesOutstanding": 0}));+                }                 let called = sw.call(                     "RunCommand",                     &[&Var::i4(crate::quit::SW_COMMANDS_APP_EXIT), &Var::bstr("")],
src/comhost/mock.rs+13
@@ -1124,6 +1124,19 @@ impl Backend for MockBackend {             t.push('\n');             let _ = std::fs::write(&f, t);         }+        // A DIALOG THAT APPEARS WHILE A COMMAND RUNS (0.11.1): `dialog-on-<cmd>.json`+        // in the simulated machine's dir holds window rows; the command puts them+        // up (once), as SOLIDWORKS raising a File ▸ Open would.+        if let Ok(dir) = std::env::var(crate::launchos::ENV_FAKE_DIR) {+            let trigger = std::path::Path::new(&dir).join(format!("dialog-on-{cmd}.json"));+            if let Some(rows) = std::fs::read_to_string(&trigger).ok().and_then(|t| serde_json::from_str::<Vec<Value>>(&t).ok()) {+                let wpath = std::path::Path::new(&dir).join("windows.json");+                let mut all: Vec<Value> = std::fs::read_to_string(&wpath).ok().and_then(|t| serde_json::from_str(&t).ok()).unwrap_or_default();+                all.extend(rows);+                let _ = std::fs::write(&wpath, Value::Array(all).to_string());+                let _ = std::fs::remove_file(&trigger);+            }+        }         let out = self.dispatch(cmd, args);         // The simulated APPLICATION's session outlives the com host, because a         // real one does. Written after every command so a respawn resumes from
src/dialogs.rsadded+247
@@ -0,0 +1,247 @@+//! WHICH VERB PUT THAT DIALOG UP? (0.11.1)+//!+//! MEASURED 2026-10-15 on adom_drew2: a SOLIDWORKS File ▸ Open dialog (title+//! "Open ", `#32770`, owned by the SOLIDWORKS main frame, file name empty)+//! appeared between 14:57 and 15:00Z, DURING a bridge battery, with Drew away+//! from the machine — and from then on every `swx_stop` was+//! `stop_blocked_by_dialog`. The census called it `kind:"unknown"`,+//! `bridgeCaused:false`: it could not say where it came from, and so the+//! session the bridge had started could never be ended by the bridge.+//!+//! So every verb is now BRACKETED by a census of SOLIDWORKS' dialogs: one+//! before it runs, one after. A dialog first seen in the after-census, that+//! the before-census (taken after the verb was marked in flight) proved+//! ABSENT, appeared DURING that verb — `appearedDuring`. Sightings persist+//! (data root, `dialog-sightings.json`) with the time the dialog was first+//! seen, the last time it was proved absent, and the user's last input. A+//! dialog first seen outside a bracket stays UNATTRIBUTED: the bridge never+//! claims what it cannot prove.+//!+//! Policy is unchanged — the bridge never answers a user's dialog — with ONE+//! carve-out: the standard Windows FILE dialog, with an EMPTY file name, that+//! the bridge PROVABLY provoked (appeared during a bridge verb, no user input+//! since that verb began). Cancelling that is a no-op the bridge owes the+//! user; `swx_dismiss_blocking_dialogs` may do it, ledgered with the+//! attribution. Anything with text typed into it is the user's.++use serde_json::{json, Value};+use std::sync::Mutex;++/// One verb in flight: what, and since when (ms since the epoch).+#[derive(Clone, Debug, PartialEq)]+pub struct InFlight {+    pub id: u64,+    pub verb: String,+    pub started_ms: u64,+}++static IN_FLIGHT: Mutex<Vec<InFlight>> = Mutex::new(Vec::new());+static NEXT_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);+static FILE_LOCK: Mutex<()> = Mutex::new(());++fn now_ms() -> u64 {+    crate::launchos::epoch_ms() as u64+}++fn sightings_path() -> std::path::PathBuf {+    crate::scratch::root().join("dialog-sightings.json")+}++/// The persisted sightings (hwnd → what is known about its appearance).+pub fn sightings() -> Vec<Value> {+    std::fs::read_to_string(sightings_path()).ok().and_then(|t| serde_json::from_str::<Vec<Value>>(&t).ok()).unwrap_or_default()+}++fn save(v: &[Value]) {+    let _ = std::fs::write(sightings_path(), serde_json::to_string(v).unwrap_or_else(|_| "[]".into()));+}++/// The dialogs SOLIDWORKS is showing now: every top-level window of its+/// process(es) and their CEF children that is a `#32770`, or is a visible,+/// non-tool window OWNED by another SOLIDWORKS window. `None` = could not look.+/// Deterministic: the native window list, every time — never ab's.+pub fn census_windows(os: &dyn crate::launchos::LaunchOs) -> Option<(Vec<crate::launchos::WindowInfo>, Vec<u32>)> {+    let procs = os.processes().ok()?;+    let sw: Vec<&crate::launchos::ProcInfo> = procs.iter().filter(|p| p.is("SLDWORKS.exe")).collect();+    let mut pids: Vec<u32> = Vec::new();+    for p in &sw {+        pids.push(p.pid);+        pids.extend(procs.iter().filter(|c| c.is("swCefSubProc.exe") && c.ppid == Some(p.pid)).map(|c| c.pid));+    }+    let mut all = Vec::new();+    for pid in &pids {+        all.extend(os.windows_of_pid(*pid).ok()?);+    }+    let ours: std::collections::HashSet<i64> = all.iter().map(|w| w.hwnd).collect();+    let rows = all+        .iter()+        .filter(|w| {+            w.is_dialog()+                || (w.visible+                    && w.owner.map(|o| ours.contains(&o)).unwrap_or(false)+                    && !w.exstyle.map(|e| e & crate::launchos::WS_EX_TOOLWINDOW != 0).unwrap_or(false))+        })+        .cloned()+        .collect();+    Some((rows, sw.iter().map(|p| p.pid).collect()))+}++/// OBSERVE: record every dialog not seen before, attributed to the verbs in+/// flight; strike off sightings whose window is gone. Returns the sightings of+/// the dialogs that are up now.+pub fn observe(os: &dyn crate::launchos::LaunchOs) -> Option<Vec<Value>> {+    let (rows, _) = census_windows(os)?;+    let _g = FILE_LOCK.lock().unwrap_or_else(|e| e.into_inner());+    let mut seen = sightings();+    let last_ok = seen.iter().filter_map(|s| s.get("censusMs").and_then(|v| v.as_u64())).max();+    let now = now_ms();+    let flight: Vec<InFlight> = IN_FLIGHT.lock().unwrap_or_else(|e| e.into_inner()).clone();+    let last_input_ms = os.seconds_since_last_input().ok().map(|s| now.saturating_sub(s * 1000));+    let up: std::collections::HashSet<i64> = rows.iter().map(|w| w.hwnd).collect();+    // Gone windows are forgotten; the census time moves on for the rest.+    seen.retain(|s| s.get("hwnd").and_then(|h| h.as_i64()).map(|h| up.contains(&h)).unwrap_or(false));+    let mut current = Vec::new();+    for w in &rows {+        if let Some(s) = seen.iter_mut().find(|s| s["hwnd"] == w.hwnd && s["title"] == w.title.as_str()) {+            s["lastSeenMs"] = json!(now);+            current.push(s.clone());+            continue;+        }+        // A window under a REUSED hwnd with a new title is a new sighting.+        seen.retain(|s| s["hwnd"] != w.hwnd);+        let s = json!({+            "hwnd": w.hwnd,+            "title": w.title,+            "className": w.class_name,+            "owner": w.owner,+            "firstSeenMs": now,+            "lastSeenMs": now,+            // The last census that could see SOLIDWORKS' windows and did not+            // contain this one: it appeared after that.+            "absentAtMs": last_ok,+            "appearedDuring": flight.iter().map(|f| json!({"verb": f.verb, "startedMs": f.started_ms})).collect::<Vec<_>>(),+            "lastInputMs": last_input_ms,+        });+        current.push(s.clone());+        seen.push(s);+    }+    // Every sighting carries the time of the latest census, so the next new+    // dialog knows when it was last proved absent.+    let mut out = seen;+    out.push(json!({"hwnd": Value::Null, "censusMs": now}));+    out.retain(|s| !s["hwnd"].is_null() || s.get("censusMs").and_then(|v| v.as_u64()) == Some(now));+    save(&out);+    Some(current)+}++/// IS THIS SIGHTING PROVABLY THE BRIDGE'S? It appeared during a bridge verb —+/// proved ABSENT at or after that verb began, first seen while it was in+/// flight — and the user gave no input from that verb's start until `now`+/// (`last_input_now_ms`: the latest input as read now). Returns the verb.+pub fn bridge_caused(s: &Value, last_input_now_ms: Option<u64>) -> Option<String> {+    let absent = s.get("absentAtMs").and_then(|v| v.as_u64())?;+    let input_then = s.get("lastInputMs").and_then(|v| v.as_u64())?;+    let input_now = last_input_now_ms?;+    s.get("appearedDuring")?.as_array()?.iter().find_map(|d| {+        let started = d.get("startedMs").and_then(|v| v.as_u64())?;+        let verb = d.get("verb").and_then(|v| v.as_str())?;+        (started <= absent && input_then < started && input_now < started).then(|| verb.to_string())+    })+}++/// What is known about one dialog's appearance, for a report.+pub fn attribution(hwnd: i64, os: &dyn crate::launchos::LaunchOs) -> Value {+    let Some(s) = sightings().into_iter().find(|s| s["hwnd"] == hwnd) else {+        return json!({"known": false, "why": "no census saw this window appear: it was up before the bridge first looked, so where it came from cannot be said"});+    };+    let now = now_ms();+    let input_now = os.seconds_since_last_input().ok().map(|x| now.saturating_sub(x * 1000));+    let during = s.get("appearedDuring").and_then(|d| d.as_array()).cloned().unwrap_or_default();+    let caused = bridge_caused(&s, input_now);+    json!({+        "known": true,+        "firstSeenMs": s["firstSeenMs"],+        "ageSeconds": s.get("firstSeenMs").and_then(|v| v.as_u64()).map(|f| now.saturating_sub(f) / 1000),+        "appearedDuring": if during.is_empty() { Value::Null } else { json!(during.iter().filter_map(|d| d.get("verb").cloned()).collect::<Vec<_>>()) },+        "appearedBetweenVerbs": during.is_empty(),+        "provedAbsentAtMs": s["absentAtMs"],+        "bridgeCaused": caused.is_some(),+        "bridgeCausedBy": caused,+        "userInputSinceItAppeared": match (input_now, s.get("firstSeenMs").and_then(|v| v.as_u64())) {+            (Some(i), Some(f)) => json!(i >= f),+            _ => Value::Null,+        },+    })+}++/// A verb begins: marked in flight FIRST, then the census — so the census+/// that proves a dialog absent is one taken after the verb began.+pub fn begin(verb: &str) -> u64 {+    let id = NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst);+    IN_FLIGHT.lock().unwrap_or_else(|e| e.into_inner()).push(InFlight { id, verb: verb.to_string(), started_ms: now_ms() });+    let os = crate::launchos::os();+    let _ = observe(os.as_ref());+    id+}++/// A verb ends: the census FIRST (a dialog it raised is attributed to it),+/// then it leaves the in-flight list. New dialogs are ledgered.+pub fn end(id: u64) -> Vec<Value> {+    let os = crate::launchos::os();+    let before: std::collections::HashSet<i64> = sightings().iter().filter_map(|s| s.get("hwnd").and_then(|h| h.as_i64())).collect();+    let now = observe(os.as_ref()).unwrap_or_default();+    IN_FLIGHT.lock().unwrap_or_else(|e| e.into_inner()).retain(|f| f.id != id);+    let fresh: Vec<Value> = now.into_iter().filter(|s| s.get("hwnd").and_then(|h| h.as_i64()).map(|h| !before.contains(&h)).unwrap_or(false)).collect();+    for s in &fresh {+        crate::ledger::record("dialog_appeared", json!({"hwnd": s["hwnd"], "title": s["title"], "className": s["className"],+                                                         "appearedDuring": s["appearedDuring"], "owner": s["owner"]}));+    }+    fresh+}++/// Does `dispatch` bracket this verb? Every known verb except the ones that+/// only read the bridge's own state (they never touch SOLIDWORKS).+pub fn brackets(verb: &str) -> bool {+    !matches!(verb, "swx_progress" | "swx_action_ledger" | "swx_verb_times" | "swx_describe")+}++/// The standard Windows FILE dialog with an empty name, provoked by the bridge:+/// the one dialog the bridge may cancel. `Err` says why not.+pub fn may_cancel(hwnd: i64, os: &dyn crate::launchos::LaunchOs) -> Result<(String, Value), String> {+    let s = sightings().into_iter().find(|s| s["hwnd"] == hwnd).ok_or("no census saw it appear: its origin is unknown")?;+    let now = now_ms();+    let input_now = os.seconds_since_last_input().ok().map(|x| now.saturating_sub(x * 1000));+    let verb = bridge_caused(&s, input_now).ok_or(+        "it is not PROVABLY the bridge's: it did not appear during a bridge verb, or the user has given input since that verb began",+    )?;+    let fd = os.file_dialog(hwnd).map_err(|e| format!("whether it is a file dialog could not be read ({e})"))?;+    if !fd.standard {+        return Err("it is not the standard Windows file dialog".into());+    }+    match fd.file_name.as_deref() {+        None => return Err("its file-name box could not be read".into()),+        Some(t) if !t.trim().is_empty() => return Err(format!("its file-name box holds text ({} characters): somebody typed into it", t.chars().count())),+        _ => {}+    }+    Ok((verb, s))+}++#[cfg(test)]+mod tests {+    use super::*;++    #[test]+    fn only_a_dialog_proved_absent_after_a_verb_began_with_no_input_since_is_the_bridges() {+        let s = json!({"absentAtMs": 1000, "lastInputMs": 500, "appearedDuring": [{"verb": "swx_start", "startedMs": 900}]});+        assert_eq!(bridge_caused(&s, Some(500)), Some("swx_start".to_string()));+        // Input after the verb began: the user may have opened it.+        assert_eq!(bridge_caused(&s, Some(950)), None, "input since the verb began");+        assert_eq!(bridge_caused(&json!({"absentAtMs": 1000, "lastInputMs": 950, "appearedDuring": [{"verb": "v", "startedMs": 900}]}), Some(950)), None);+        // Last proved absent BEFORE the verb began: it may predate the verb.+        assert_eq!(bridge_caused(&json!({"absentAtMs": 800, "lastInputMs": 500, "appearedDuring": [{"verb": "v", "startedMs": 900}]}), Some(500)), None);+        // Appeared between verbs, or never proved absent, or input unreadable.+        assert_eq!(bridge_caused(&json!({"absentAtMs": 1000, "lastInputMs": 500, "appearedDuring": []}), Some(500)), None);+        assert_eq!(bridge_caused(&json!({"absentAtMs": null, "lastInputMs": 500, "appearedDuring": [{"verb": "v", "startedMs": 900}]}), Some(500)), None);+        assert_eq!(bridge_caused(&s, None), None, "an unreadable input time proves nothing");+    }+}
src/launchos.rs+136
@@ -295,6 +295,16 @@ impl WindowInfo {     } } +/// THE STANDARD WINDOWS FILE DIALOG (0.11.1): is this window one (it has the+/// file-name combo, control 1148 — or the old-style edit, 1152 — and a Cancel+/// button, control 2), and what is in its file-name box (`None` = could not be+/// read, never "empty").+#[derive(Clone, Debug, Default, PartialEq)]+pub struct FileDialogState {+    pub standard: bool,+    pub file_name: Option<String>,+}+ /// The readable contents of a dialog: what it says, and what it offers. #[derive(Debug, Clone, Default, PartialEq)] pub struct DialogContents {@@ -532,6 +542,18 @@ pub trait LaunchOs: Send + Sync {         None     } +    /// Is `hwnd` the standard file dialog, and what is typed in it? READ-ONLY.+    fn file_dialog(&self, _hwnd: i64) -> Result<FileDialogState, String> {+        Err("this host cannot read a file dialog".into())+    }++    /// CANCEL a dialog: WM_COMMAND IDCANCEL, as Esc or its Cancel button would.+    /// Used ONLY on a file dialog the bridge provably raised, with an empty file+    /// name (dialogs::may_cancel). `Ok` only when the window is gone after.+    fn cancel_dialog(&self, _hwnd: i64) -> Result<(), String> {+        Err("this host cannot cancel a dialog".into())+    }+     /// This process's own pid — recorded on a launch attempt so a LATER bridge     /// can tell "somebody is supervising this" from "this was left behind".     fn self_pid(&self) -> u32 {@@ -1327,6 +1349,33 @@ impl LaunchOs for FakeOs {     /// The scenario's row for `hwnd` may carry `ownerEnabledReads: [false,     /// false, true]` — one value per re-read, the last repeating; otherwise its     /// `ownerEnabled` holds for every read.+    /// `file-dialogs.json`: {"<hwnd>": {"standard": bool, "fileName": "..." | null}}.+    fn file_dialog(&self, hwnd: i64) -> Result<FileDialogState, String> {+        let all = self.read_json("file-dialogs.json");+        let row = all.get(hwnd.to_string()).ok_or("simulated: not a scripted file dialog")?;+        Ok(FileDialogState {+            standard: row.get("standard").and_then(|v| v.as_bool()).unwrap_or(false),+            file_name: row.get("fileName").and_then(|v| v.as_str()).map(String::from),+        })+    }++    /// Records the cancel (`cancelled-dialogs.jsonl`) and takes the window out+    /// of `windows.json`, as Windows would; `cancel-dialog-fails` leaves it up.+    fn cancel_dialog(&self, hwnd: i64) -> Result<(), String> {+        use std::io::Write;+        if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open(self.dir.join("cancelled-dialogs.jsonl")) {+            let _ = writeln!(f, "{}", json!({"hwnd": hwnd}));+        }+        if self.dir.join("cancel-dialog-fails").exists() {+            return Err("simulated: the dialog was still up after IDCANCEL".into());+        }+        if let Some(rows) = self.read_json("windows.json").as_array() {+            let kept: Vec<Value> = rows.iter().filter(|r| r.get("hwnd").and_then(|h| h.as_i64()) != Some(hwnd)).cloned().collect();+            let _ = std::fs::write(self.dir.join("windows.json"), Value::Array(kept).to_string());+        }+        Ok(())+    }+     fn owner_enabled_of(&self, hwnd: i64) -> Option<bool> {         let rows = self.read_json("windows.json");         let row = rows.as_array()?.iter().find(|r| r.get("hwnd").and_then(|h| h.as_i64()) == Some(hwnd))?.clone();@@ -2026,6 +2075,93 @@ mod win {             }         } +        /// The file-name combo (control 1148, `cmb13`) or the old-style edit+        /// (1152, `edt1`), and the Cancel button (IDCANCEL, 2), found among ALL+        /// descendants; the name read with WM_GETTEXT, which Windows marshals+        /// across processes (GetWindowText would not read another process's edit).+        fn file_dialog(&self, hwnd: i64) -> Result<FileDialogState, String> {+            use windows::core::BOOL;+            use windows::Win32::Foundation::{LPARAM, WPARAM};+            use windows::Win32::UI::WindowsAndMessaging::{+                EnumChildWindows, GetClassNameW, GetDlgCtrlID, GetParent, SendMessageTimeoutW, SMTO_ABORTIFHUNG, WM_GETTEXT,+                WM_GETTEXTLENGTH,+            };+            static KIDS: std::sync::Mutex<Vec<i64>> = std::sync::Mutex::new(Vec::new());+            unsafe extern "system" fn kid(h: HWND, _: LPARAM) -> BOOL {+                if let Ok(mut g) = KIDS.lock() {+                    g.push(h.0 as i64);+                }+                BOOL(1)+            }+            let _serialised = ENUM_LOCK.lock().unwrap_or_else(|e| e.into_inner());+            KIDS.lock().unwrap_or_else(|e| e.into_inner()).clear();+            unsafe {+                let _ = EnumChildWindows(Some(HWND(hwnd as *mut std::ffi::c_void)), Some(kid), LPARAM(0));+            }+            let kids = KIDS.lock().unwrap_or_else(|e| e.into_inner()).clone();+            if kids.is_empty() {+                return Err("the dialog reported no child controls".into());+            }+            let class_of = |h: i64| -> String {+                let mut c = [0u16; 128];+                let n = unsafe { GetClassNameW(HWND(h as *mut std::ffi::c_void), &mut c) };+                String::from_utf16_lossy(&c[..n.max(0) as usize])+            };+            let id_of = |h: i64| unsafe { GetDlgCtrlID(HWND(h as *mut std::ffi::c_void)) };+            let combo = kids.iter().copied().find(|h| id_of(*h) == 1148 && class_of(*h).starts_with("ComboBox"));+            let old_edit = kids.iter().copied().find(|h| id_of(*h) == 1152 && class_of(*h) == "Edit");+            let cancel = kids.iter().any(|h| id_of(*h) == 2 && class_of(*h) == "Button");+            let edit = old_edit.or_else(|| {+                let c = combo?;+                kids.iter().copied().find(|h| {+                    class_of(*h) == "Edit" && {+                        let mut p = unsafe { GetParent(HWND(*h as *mut std::ffi::c_void)) }.ok();+                        let mut found = false;+                        for _ in 0..4 {+                            match p {+                                Some(x) if x.0 as i64 == c => {+                                    found = true;+                                    break;+                                }+                                Some(x) => p = unsafe { GetParent(x) }.ok(),+                                None => break,+                            }+                        }+                        found+                    }+                })+            });+            let standard = (combo.is_some() || old_edit.is_some()) && cancel;+            let file_name = edit.and_then(|e| unsafe {+                let h = HWND(e as *mut std::ffi::c_void);+                let mut len: usize = 0;+                let r = SendMessageTimeoutW(h, WM_GETTEXTLENGTH, WPARAM(0), LPARAM(0), SMTO_ABORTIFHUNG, 500, Some(&mut len));+                if r.0 == 0 {+                    return None;+                }+                let mut buf = vec![0u16; len + 1];+                let mut got: usize = 0;+                let r = SendMessageTimeoutW(h, WM_GETTEXT, WPARAM(buf.len()), LPARAM(buf.as_mut_ptr() as isize), SMTO_ABORTIFHUNG, 500, Some(&mut got));+                (r.0 != 0).then(|| String::from_utf16_lossy(&buf[..got.min(len)]))+            });+            Ok(FileDialogState { standard, file_name })+        }++        fn cancel_dialog(&self, hwnd: i64) -> Result<(), String> {+            use windows::Win32::Foundation::{LPARAM, WPARAM};+            use windows::Win32::UI::WindowsAndMessaging::{IsWindow, PostMessageW, WM_COMMAND};+            let h = HWND(hwnd as *mut std::ffi::c_void);+            // IDCANCEL (2), BN_CLICKED (0) in the high word: what Esc does.+            unsafe { PostMessageW(Some(h), WM_COMMAND, WPARAM(2), LPARAM(0)) }.map_err(|e| format!("PostMessage failed: {e}"))?;+            for _ in 0..30 {+                std::thread::sleep(std::time::Duration::from_millis(100));+                if !unsafe { IsWindow(Some(h)) }.as_bool() {+                    return Ok(());+                }+            }+            Err("the dialog was still up 3 s after IDCANCEL".into())+        }+         fn owner_enabled_of(&self, hwnd: i64) -> Option<bool> {             use windows::Win32::UI::WindowsAndMessaging::{GetWindow, GetWindowLongPtrW, GWL_STYLE, GW_OWNER};             unsafe {
src/lib.rs+1
@@ -47,6 +47,7 @@ pub mod presence; pub mod quit; pub mod readiness; pub mod render;+pub mod dialogs; pub mod disk; pub mod selftest; pub mod scratch;
src/quit.rs+52−3
@@ -411,6 +411,16 @@ pub fn button_like_captions(c: &crate::launchos::DialogContents) -> Vec<String> pub struct DialogCensus {     pub blocking: Option<BlockingDialog>,     pub notifications: Vec<BlockingDialog>,+    /// EVERY OTHER blocking dialog (0.11.1): `blocking` is the first, which+    /// decides a refusal; a census that reports never drops the rest.+    pub more_blocking: Vec<BlockingDialog>,+}++impl DialogCensus {+    /// Every blocking dialog, the first first.+    pub fn all_blocking(&self) -> Vec<&BlockingDialog> {+        self.blocking.iter().chain(self.more_blocking.iter()).collect()+    } }  /// How many times, and how far apart, a disabled owner is re-read before a@@ -458,7 +468,7 @@ fn settle_owner(w: &WindowInfo, contents: Option<&crate::launchos::DialogContent }  pub fn dialog_census(windows: &[WindowInfo], os: &dyn crate::launchos::LaunchOs) -> DialogCensus {-    let mut out = DialogCensus { blocking: None, notifications: Vec::new() };+    let mut out = DialogCensus { blocking: None, notifications: Vec::new(), more_blocking: Vec::new() };     for w in windows.iter().filter(|w| w.is_dialog() && w.visible) {         let contents = os.dialog_contents(w.hwnd).ok();         // A body that could not be read is judged as unread (0.9.12).@@ -499,6 +509,8 @@ pub fn dialog_census(windows: &[WindowInfo], os: &dyn crate::launchos::LaunchOs)         if blocking {             if out.blocking.is_none() {                 out.blocking = Some(d);+            } else {+                out.more_blocking.push(d);             }         } else {             out.notifications.push(d);@@ -547,9 +559,42 @@ impl BlockingDialog {             "ownerDisabledTransiently": self.owner_settle.as_ref().and_then(|o| o.get("ownerDisabledTransiently").cloned()).unwrap_or(json!(false)),             "ownerReads": self.owner_settle.as_ref().and_then(|o| o.get("reads").cloned()).unwrap_or(Value::Null),             "neverAnswer": true,+            // WHICH WINDOW, AND WHERE IT CAME FROM (0.11.1): an operator facing a+            // refusal sees exactly which dialog, whose it is, how long it has been+            // up, and whether a bridge verb raised it.+            "className": self.window.as_ref().map(|w| w.class_name.clone()),+            "owner": self.window.as_ref().and_then(|w| w.owner),+            "rect": self.window.as_ref().and_then(|w| w.rect),+            "visible": self.window.as_ref().map(|w| w.visible),+            "attribution": crate::dialogs::attribution(self.hwnd, crate::launchos::os().as_ref()),         })     } +    /// `to_json`, plus what the bridge may do about it: the standard file+    /// dialog it provably raised, with an empty name, is the one it may cancel.+    pub fn report(&self, os: &dyn crate::launchos::LaunchOs) -> Value {+        let mut v = self.to_json();+        let fd = os.file_dialog(self.hwnd).ok();+        v["fileDialog"] = match &fd {+            Some(f) => json!({"standard": f.standard, "fileNameEmpty": f.file_name.as_ref().map(|n| n.trim().is_empty())}),+            None => Value::Null,+        };+        match crate::dialogs::may_cancel(self.hwnd, os) {+            Ok((verb, _)) => {+                v["bridgeMayCancel"] = json!(true);+                v["cancelBecause"] = json!(format!(+                    "the standard file dialog with an EMPTY file name, raised during {verb} with no user input since:                      swx_dismiss_blocking_dialogs {{close:true, confirm:true, reason}} cancels it (Cancel changes nothing)"+                ));+            }+            Err(why) => {+                v["bridgeMayCancel"] = json!(false);+                v["notCancellableBecause"] = json!(why);+                v["whoAnswers"] = json!("the user cancels it");+            }+        }+        v+    }+     /// READ WHAT IT SAYS. Read-only; a failure leaves `contents` as `None`.     pub fn read_contents(&mut self, os: &dyn crate::launchos::LaunchOs) {         self.contents = os.dialog_contents(self.hwnd).ok();@@ -876,9 +921,13 @@ pub fn check_preconditions(         return Err(Refusal::Known {             code: d.error_code(),             message: format!(-                "SOLIDWORKS is showing a dialog ({}): \"{}\" — this bridge never answers a \+                "SOLIDWORKS is showing a dialog ({}): \"{}\" (hwnd {}, class {}, owned by {}) — this bridge never answers a \                  dialog, so the quit stops here",-                d.kind, d.title+                d.kind,+                d.title,+                d.hwnd,+                d.window.as_ref().map(|w| w.class_name.as_str()).unwrap_or("?"),+                d.window.as_ref().and_then(|w| w.owner).map(|o| o.to_string()).unwrap_or_else(|| "no window".into())             ),         });     }
src/verbs.rs+73−27
@@ -717,35 +717,55 @@ fn swx_state(ctx: &Ctx, args: &Value) -> Value {     if docs.is_null() {         docs = json!([]);     }-    // The RENDER-ME half: a background screenshot plus the dialog census, which-    // is what makes one call enough to see what SOLIDWORKS is showing. Both go-    // through ab's generic verbs and neither is allowed to fail the verb.+    // THE DIALOG CENSUS (0.11.1): NATIVE and DETERMINISTIC — every top-level+    // window of SOLIDWORKS (and its CEF children), judged, every time, whether or+    // not a screenshot is wanted. MEASURED 2026-10-15: at 19:30Z this verb said+    // `blockingDialogs: []` while a File ▸ Open dialog (hwnd 205698) was up+    // before and after — the census went through ab's flat window list, and was+    // SKIPPED entirely (reporting a known-empty []) whenever screenshot:false.+    // ab's list is the fallback only where the native one cannot be read.     let want_shot = arg_bool(args, "screenshot", true);     let hwnd_i64 = main_hwnd.as_i64();     let mut windows_error: Option<String> = None;-    let (screenshot, dialogs) = if want_shot && (detection.running || crate::is_mock()) {-        // AN UNREACHABLE ab IS NOT "NO DIALOGS". `unwrap_or_default()` turned a-        // failed window enumeration into an empty census, and `blockingDialogs:-        // []` is what a caller reads as "nothing is in the way" right before it-        // drives a SOLIDWORKS that is sitting on a modal.-        let windows = match crate::abwin::list_windows(Duration::from_secs(20)) {-            Ok(w) => Some(w),-            Err(e) => {-                windows_error = Some(e);-                None+    let os = crate::launchos::os();+    let mut census_source = "native";+    let mut other_windows: Vec<Value> = Vec::new();+    let mut notifications: Vec<Value> = Vec::new();+    // A census that finds NO SOLIDWORKS process while COM says one is running+    // cannot vouch for anything: it is not "no dialogs".+    let sw_seen_running = detection.running || !main_hwnd.is_null();+    let dialogs: Option<Vec<Value>> = match crate::dialogs::census_windows(os.as_ref()).filter(|(_, pids)| !pids.is_empty() || !sw_seen_running) {+        Some((rows, _)) => {+            let judged = crate::quit::dialog_census(&rows, os.as_ref());+            notifications = judged.notifications.iter().map(|d| d.report(os.as_ref())).collect();+            // Owned popups that are not dialogs: listed, never judged away.+            other_windows = rows.iter().filter(|w| !w.is_dialog()).map(|w| w.to_json()).collect();+            Some(judged.all_blocking().iter().map(|d| d.report(os.as_ref())).collect())+        }+        None if detection.running || crate::is_mock() => {+            census_source = "ab";+            // AN UNREACHABLE ab IS NOT "NO DIALOGS": null, never [].+            match crate::abwin::list_windows(Duration::from_secs(20)) {+                Ok(w) => {+                    let sw_pid = ctx.call("app_state", json!({}), 20).ok().and_then(|s| s.get("processId").and_then(|p| p.as_i64()));+                    Some(crate::abwin::dialog_census(&w, hwnd_i64, sw_pid))+                }+                Err(e) => {+                    windows_error = Some(e);+                    None+                }             }-        };-        let sw_pid = ctx-            .call("app_state", json!({}), 20)-            .ok()-            .and_then(|s| s.get("processId").and_then(|p| p.as_i64()));-        let census = windows-            .as_ref()-            .map(|w| crate::abwin::dialog_census(w, hwnd_i64, sw_pid));-        let shot = hwnd_i64.map(|h| crate::abwin::screenshot(h, Duration::from_secs(45)));-        (shot.unwrap_or(Value::Null), census)+        }+        None => {+            census_source = "none";+            windows_error = Some("the window census could not be taken".into());+            None+        }+    };+    let screenshot = if want_shot && (detection.running || crate::is_mock()) {+        hwnd_i64.map(|h| crate::abwin::screenshot(h, Duration::from_secs(45))).unwrap_or(Value::Null)     } else {-        (Value::Null, Some(Vec::new()))+        Value::Null     };     ok(json!({         "running": detection.running,@@ -759,6 +779,9 @@ fn swx_state(ctx: &Ctx, args: &Value) -> Value {         "blockingDialogs": dialogs.clone().map(Value::from).unwrap_or(Value::Null),         "blockingDialogsKnown": dialogs.is_some(),         "blockingDialogsError": windows_error,+        "dialogCensusSource": census_source,+        "dialogNotifications": notifications,+        "ownedWindows": other_windows,         "screenshot": screenshot,         "timings": crate::ledger::stages(),         "focusEvents": [],@@ -1178,6 +1201,8 @@ struct StopEvidence {     /// Windows the census judged NOT blocking — notifications, reported with     /// their evidence and never answered (0.9.4).     notifications: Vec<crate::quit::BlockingDialog>,+    /// Every blocking dialog after the first (0.11.1): reported, never dropped.+    more_dialogs: Vec<crate::quit::BlockingDialog>,     /// The pids the dialog census actually covered — SOLIDWORKS plus its CEF     /// subprocesses, which is where a 3DEXPERIENCE sign-in window lives.     dialog_pids: Vec<u32>,@@ -1228,6 +1253,7 @@ impl StopEvidence {         let mut census_readable = false;         let mut dialog = None;         let mut notifications: Vec<crate::quit::BlockingDialog> = Vec::new();+        let mut more_dialogs: Vec<crate::quit::BlockingDialog> = Vec::new();         if let Some(id) = identity.as_ref() {             if let Some(pids) = quit::dialog_census_pids(census.as_ref(), id) {                 let mut all_read = true;@@ -1253,6 +1279,7 @@ impl StopEvidence {                 let judged = quit::dialog_census(&windows, os);                 dialog = judged.blocking;                 notifications = judged.notifications;+                more_dialogs = judged.more_blocking;             }         }         // P5: responding, without COM.@@ -1269,6 +1296,7 @@ impl StopEvidence {             identity,             dialog,             notifications,+            more_dialogs,             dialog_pids,             census_readable,             responding,@@ -1301,7 +1329,10 @@ impl StopEvidence {             "listedDocuments": self.listed_docs,             "documentsTruncated": self.docs_truncated,             "unsavedDocuments": self.unsaved,-            "dialog": self.dialog.as_ref().map(|d| d.to_json()),+            // WHICH dialog, whose, how old, and whether a bridge verb raised it+            // (0.11.1) — and whether the bridge may cancel it.+            "dialog": self.dialog.as_ref().map(|d| d.report(crate::launchos::os().as_ref())),+            "moreBlockingDialogs": self.more_dialogs.iter().map(|d| d.report(crate::launchos::os().as_ref())).collect::<Vec<_>>(),             "notifications": self.notifications.iter().map(|d| d.to_json()).collect::<Vec<_>>(),             "dialogCensusReadable": self.census_readable,             "dialogCensusPids": self.dialog_pids,@@ -1331,7 +1362,11 @@ fn stop_refusal_hint(code: &str) -> &'static str {         "stop_blocked_by_dialog" =>             "SOLIDWORKS is asking something. This bridge never answers a dialog — closing a stuck \              modal through the window manager took the whole application down on 2026-09-02. \-             swx_state takes a background screenshot; show the user and let them answer.",+             preconditions.dialog names it (hwnd, class, owner, ageSeconds, attribution). ONE \+             exception: the standard file dialog with an EMPTY file name that a bridge verb \+             provably raised (bridgeMayCancel:true) — swx_dismiss_blocking_dialogs {close:true, \+             confirm:true, reason} cancels it, a no-op. Otherwise the user cancels it: swx_state \+             takes a background screenshot to show them.",         "stop_preconditions_unknown" =>             "Nothing was attempted. Every precondition must be MEASURED, and an unanswerable one \              is not permission — quitting on a guess is how a session ends up half-closed with the \@@ -3164,7 +3199,7 @@ pub const CATALOG: &[(&str, &str, &str)] = &[     ("swx_kill", "process", "force-kill SOLIDWORKS (reason + confirm; discards unsaved work)"),     ("swx_com_respawn", "process", "respawn ONLY the bridge's own COM host child (reason): every COM reference the bridge holds is released; SOLIDWORKS is never called; refused while a verb is in flight"),     ("swx_window_info", "read", "SOLIDWORKS' windows and its main hwnd (read-only, nothing raised)"),-    ("swx_dismiss_blocking_dialogs", "exec", "census of blocking SOLIDWORKS dialogs; close:true is opt-in, gated, and never touches an unattributed window"),+    ("swx_dismiss_blocking_dialogs", "exec", "census of blocking SOLIDWORKS dialogs (native: every window of the process, with where each came from); close:true is opt-in, gated, and never touches an unattributed window — the one dialog it may CANCEL without the write opt-in is a standard file dialog with an empty name that a bridge verb provably raised"),     ("swx_close_window", "exec", "close one window by hwnd (reason + confirm)"),     ("swx_check_recovery", "read", "is SOLIDWORKS showing the Document Recovery dialog?"),     ("swx_action_ledger", "read", "append-only record of what the bridge did in the background"),@@ -3479,7 +3514,18 @@ pub fn dispatch(ctx: &Ctx, command: &str, args: &Value) -> Value {     // WHAT WAS OPEN BEFORE THE VERB — so the baseline grows only by what the     // verb itself opened or created (0.9.10, adversary pass 46).     let before = if changes_documents { crate::presence::snapshot(ctx) } else { None };+    // EVERY VERB IS BRACKETED BY A DIALOG CENSUS (0.11.1): a SOLIDWORKS dialog+    // that appears while it runs is attributed to it (`dialogs::observe`).+    let bracket = (is_known(command) && crate::dialogs::brackets(command)).then(|| crate::dialogs::begin(command));     let mut out = dispatch_after_gate(ctx, command, args);+    if let Some(id) = bracket {+        let fresh = crate::dialogs::end(id);+        if !fresh.is_empty() {+            if let Value::Object(m) = &mut out {+                m.insert("dialogsAppeared".into(), json!(fresh));+            }+        }+    }     if let (Some(summary), Value::Object(m)) = (at_gate, &mut out) {         m.entry("readiness").or_insert(summary);     }
src/verbs3.rs+61
@@ -3339,8 +3339,63 @@ pub fn dismiss_blocking_dialogs(ctx: &Ctx, args: &Value) -> Value {             return g;         }     }+    // THE NATIVE CENSUS FIRST (0.11.1): SOLIDWORKS' own windows, judged, with+    // where each one came from. The ONE dialog this bridge may cancel is in it:+    // the standard file dialog with an EMPTY file name that a bridge verb+    // provably raised (dialogs::may_cancel) — Cancel is a no-op, and it is the+    // bridge's own mess. Everything else is the user's to answer.+    let os = crate::launchos::os();+    let native: Option<Vec<crate::quit::BlockingDialog>> = crate::dialogs::census_windows(os.as_ref()).map(|(rows, _)| {+        let c = crate::quit::dialog_census(&rows, os.as_ref());+        let mut all: Vec<crate::quit::BlockingDialog> = c.blocking.into_iter().chain(c.more_blocking).collect();+        all.extend(c.notifications);+        all+    });+    let only_hwnds: Vec<i64> = args.get("hwnds").and_then(|v| v.as_array()).map(|a| a.iter().filter_map(|v| v.as_i64()).collect()).unwrap_or_default();+    let mut cancelled: Vec<Value> = Vec::new();+    let mut cancel_refused: Vec<Value> = Vec::new();+    if close {+        let reason = arg_str(args, "reason").unwrap_or("").to_string();+        for d in native.iter().flatten() {+            if !only_hwnds.is_empty() && !only_hwnds.contains(&d.hwnd) {+                continue;+            }+            match crate::dialogs::may_cancel(d.hwnd, os.as_ref()) {+                Ok((verb, sighting)) => {+                    let r = os.cancel_dialog(d.hwnd);+                    let attribution = crate::dialogs::attribution(d.hwnd, os.as_ref());+                    crate::ledger::record(+                        "dialog_cancelled",+                        json!({"hwnd": d.hwnd, "title": d.title, "raisedDuring": verb, "sighting": sighting,+                               "ok": r.is_ok(), "error": r.as_ref().err(), "reason": reason}),+                    );+                    cancelled.push(json!({"hwnd": d.hwnd, "title": d.title, "ok": r.is_ok(), "error": r.err(),+                                          "raisedDuring": verb, "attribution": attribution,+                                          "how": "WM_COMMAND IDCANCEL — what Esc or its Cancel button does; nothing is opened or changed"}));+                }+                Err(why) => cancel_refused.push(json!({"hwnd": d.hwnd, "title": d.title, "why": why})),+            }+        }+    }+    let native_report: Option<Vec<Value>> = crate::dialogs::census_windows(os.as_ref()).map(|(rows, _)| {+        let c = crate::quit::dialog_census(&rows, os.as_ref());+        c.all_blocking().iter().map(|d| d.report(os.as_ref())).chain(c.notifications.iter().map(|d| d.report(os.as_ref()))).collect()+    });     let (hwnd, pid) = window_identity(ctx);     let all = match crate::abwin::list_windows(AB_TIMEOUT) {+        Err(e) if native_report.is_some() => {+            return ok(json!({+                "dialogs": native_report,+                "dialogsKnown": true,+                "censusSource": "native",+                "cancelled": cancelled,+                "cancelRefused": cancel_refused,+                "closed": [],+                "abError": e,+                "_hint": "The native census (SOLIDWORKS' own windows) is complete; ab could not be \+                          reached, so nothing beyond a bridge-raised empty file dialog could be closed.",+            }))+        }         Err(e) => {             return ok(json!({                 // NULL, not []. An empty census reads as "nothing is in the@@ -3357,10 +3412,13 @@ pub fn dismiss_blocking_dialogs(ctx: &Ctx, args: &Value) -> Value {         Ok(w) => w,     };     let dialogs = crate::abwin::dialog_census(&all, hwnd, pid);+    // A dialog cancelled above is gone; the ab list was read after it.+    let dialogs: Vec<Value> = dialogs.into_iter().filter(|d| !cancelled.iter().any(|c| c["hwnd"] == d["hwnd"] && c["ok"] == true)).collect();     if !close {         return ok(json!({             "dialogs": dialogs,             "dialogCount": dialogs.len(),+            "nativeDialogs": native_report,             "closed": [],             "mainHwnd": hwnd,             "_hint": "REPORT ONLY. Pass close:true (plus confirm:true and a reason) to close \@@ -3472,6 +3530,9 @@ pub fn dismiss_blocking_dialogs(ctx: &Ctx, args: &Value) -> Value {     }     ok(json!({         "dialogs": dialogs,+        "nativeDialogs": native_report,+        "cancelled": cancelled,+        "cancelRefused": cancel_refused,         "closed": closed,         "refused": refused,         "_hint": "Only dialogs PROVABLY owned by SOLIDWORKS are closed (`ownerConfirmed:true`). \
tests/dialogs.rsadded+219
@@ -0,0 +1,219 @@+//! WHO PUT THAT DIALOG UP? (0.11.1) — the File ▸ Open of 2026-10-15.+//!+//! MEASURED on adom_drew2: title "Open " (trailing space), `#32770`, hwnd+//! 205698, owned by the SOLIDWORKS main frame, style 0x96CC02C4, exstyle+//! 0x10101, rect (0,0)-(853,533), file name EMPTY. It appeared between 14:57+//! and 15:00Z during a bridge battery with Drew away, and blocked every+//! swx_stop after it; the census called it `kind:"unknown"`,+//! `bridgeCaused:false`, and swx_state once reported `blockingDialogs: []`+//! while it was up. Everything here runs in the simulator.++mod common;++use common::{Bridge, TempDir};+use serde_json::{json, Value};++fn s(v: &Value) -> String {+    serde_json::to_string_pretty(v).unwrap_or_default()+}++/// The window as measured, owned by the simulated main frame (6622700) and+/// with that frame DISABLED — a modal.+fn file_open(hwnd: i64) -> Value {+    json!({"hwnd": hwnd, "className": "#32770", "title": "Open ", "visible": true,+           "owner": 6622700, "ownerEnabled": false, "style": 0x96CC_02C4u64, "exStyle": 0x10101u64, "rect": [0, 0, 853, 533]})+}++struct Machine {+    dir: TempDir,+}++impl Machine {+    fn new(tag: &str) -> Machine {+        let dir = TempDir::new(tag);+        std::fs::write(dir.join("quit-script.json"), json!({"a": "exited"}).to_string()).unwrap();+        std::fs::write(dir.join("sldworks.json"), json!({"responding": true, "hwnd": 6622700, "privateMB": 900, "handles": 4000}).to_string()).unwrap();+        std::fs::write(dir.join("windows.json"), "[]").unwrap();+        // Nobody has touched the machine for an hour.+        std::fs::write(dir.join("last-input-secs"), "3600").unwrap();+        Machine { dir }+    }+    fn write(&self, name: &str, v: Value) {+        std::fs::write(self.dir.join(name), v.to_string()).unwrap();+    }+    fn boot(&self) -> Bridge {+        let b = Bridge::start_in(+            TempDir::new("dialogs-scratch"),+            &[+                ("SWX_LAUNCH_FAKE_DIR", Box::leak(self.dir.path().into_boxed_str())),+                ("SWX_ALLOW_USER_WRITES", "1"),+                ("SWX_QUIT_SETTLE_MS", "80"),+                ("SWX_QUIT_EXIT_WAIT_SECS", "3"),+                ("SWX_OWNER_SETTLE_MS", "10"),+            ],+        );+        b.call("swx_start", json!({}));+        b+    }+    fn windows(&self) -> Vec<Value> {+        serde_json::from_str::<Vec<Value>>(&std::fs::read_to_string(self.dir.join("windows.json")).unwrap()).unwrap()+    }+}++fn stop(b: &Bridge) -> Value {+    b.call("swx_stop", json!({"userAuthorized": true, "confirm": true, "reason": "test: end the session", "maxRung": "a"}))+}++fn cancel(b: &Bridge) -> Value {+    b.call("swx_dismiss_blocking_dialogs", json!({"close": true, "confirm": true, "reason": "test: cancel the bridge's own Open dialog"}))+}++#[test]+fn the_census_is_native_deterministic_and_never_drops_a_visible_modal() {+    let m = Machine::new("dlg-census");+    let mut second = file_open(205699);+    second["title"] = json!("SOLIDWORKS");+    m.write("windows.json", json!([file_open(205698), second]));+    let b = m.boot();+    let mut seen = Vec::new();+    for shot in [true, false, false] {+        let r = b.call("swx_state", json!({"screenshot": shot}));+        let hw: Vec<i64> = r["blockingDialogs"].as_array().cloned().unwrap_or_default().iter().filter_map(|d| d["hwnd"].as_i64()).collect();+        seen.push(hw.clone());+        check!(+            b,+            &format!("swx_state (screenshot:{shot}): BOTH visible modals, from the native census, every time"),+            r["dialogCensusSource"] == "native" && r["blockingDialogsKnown"] == true && hw == vec![205698, 205699],+            s(&r)+        );+    }+    let d = &b.call("swx_state", json!({"screenshot": false}))["blockingDialogs"][0];+    check!(+        b,+        "...each with hwnd, title, class, owner, rect and visibility",+        d["hwnd"] == 205698 && d["title"] == "Open " && d["className"] == "#32770" && d["owner"] == 6622700+            && d["rect"] == json!([0, 0, 853, 533]) && d["visible"] == true,+        s(d)+    );+    let st = stop(&b);+    check!(+        b,+        "swx_stop refuses on the first, and REPORTS the second too",+        st["errorCode"] == "stop_blocked_by_dialog" && st["preconditions"]["moreBlockingDialogs"][0]["hwnd"] == 205699,+        s(&st)+    );+}++#[test]+fn a_file_dialog_a_bridge_verb_raised_is_attributed_and_the_bridge_may_cancel_it() {+    let m = Machine::new("dlg-ours");+    let b = m.boot();+    // The File ▸ Open goes up while swx_docs runs.+    m.write("dialog-on-docs.json", json!([file_open(205698)]));+    m.write("file-dialogs.json", json!({"205698": {"standard": true, "fileName": ""}}));+    let docs = b.call("swx_docs", json!({}));+    check!(+        b,+        "swx_docs says a dialog appeared while it ran, and which",+        docs["dialogsAppeared"][0]["hwnd"] == 205698 && docs["dialogsAppeared"][0]["appearedDuring"][0]["verb"] == "swx_docs",+        s(&docs)+    );+    let st = stop(&b);+    let d = &st["preconditions"]["dialog"];+    check!(+        b,+        "swx_stop's refusal names the dialog: title, hwnd, owner, age and attribution — bridge-caused, by swx_docs",+        st["errorCode"] == "stop_blocked_by_dialog"+            && st["error"].as_str().unwrap_or("").contains("hwnd 205698")+            && st["error"].as_str().unwrap_or("").contains("owned by 6622700")+            && d["attribution"]["bridgeCaused"] == true+            && d["attribution"]["bridgeCausedBy"] == "swx_docs"+            && d["attribution"]["ageSeconds"].is_u64()+            && d["bridgeMayCancel"] == true+            && d["fileDialog"]["fileNameEmpty"] == true,+        s(&st)+    );+    let c = cancel(&b);+    check!(+        b,+        "swx_dismiss_blocking_dialogs CANCELS it (IDCANCEL), without the process-level opt-in it needs for anything else",+        c["cancelled"][0]["hwnd"] == 205698 && c["cancelled"][0]["ok"] == true && c["cancelled"][0]["raisedDuring"] == "swx_docs",+        s(&c)+    );+    check!(b, "...the window is gone", m.windows().is_empty(), "");+    let led = b.call("swx_action_ledger", json!({"limit": 50}));+    check!(+        b,+        "...and the cancel is ledgered with its attribution",+        led["actions"].as_array().map(|a| a.iter().any(|e| e["action"] == "dialog_cancelled" && e["detail"]["raisedDuring"] == "swx_docs")).unwrap_or(false)+            && led["actions"].as_array().map(|a| a.iter().any(|e| e["action"] == "dialog_appeared")).unwrap_or(false),+        s(&led)+    );+    let after = stop(&b);+    check!(b, "and swx_stop goes through", after["success"] == true && after["stopped"] == true, s(&after));+}++#[test]+fn a_dialog_with_text_typed_or_input_since_or_of_unknown_origin_is_never_cancelled() {+    // 1. Text typed into the file name: the user's.+    let m = Machine::new("dlg-typed");+    let b = m.boot();+    m.write("dialog-on-docs.json", json!([file_open(205698)]));+    m.write("file-dialogs.json", json!({"205698": {"standard": true, "fileName": "crate-v3"}}));+    b.call("swx_docs", json!({}));+    let c = cancel(&b);+    check!(+        b,+        "typed text: refused, and the window stays",+        c["cancelled"].as_array().map(|a| a.is_empty()).unwrap_or(false)+            && c["cancelRefused"][0]["why"].as_str().unwrap_or("").contains("typed")+            && m.windows().len() == 1+            && !m.dir.join("cancelled-dialogs.jsonl").exists(),+        s(&c)+    );+    let st = stop(&b);+    check!(b, "...and swx_stop says the user cancels it", st["preconditions"]["dialog"]["whoAnswers"] == "the user cancels it", s(&st));++    // 2. The user gave input after the verb began.+    let m = Machine::new("dlg-input");+    let b = m.boot();+    m.write("dialog-on-docs.json", json!([file_open(205698)]));+    m.write("file-dialogs.json", json!({"205698": {"standard": true, "fileName": ""}}));+    b.call("swx_docs", json!({}));+    std::fs::write(m.dir.join("last-input-secs"), "0").unwrap();+    let c = cancel(&b);+    check!(b, "input since: refused", c["cancelled"].as_array().map(|a| a.is_empty()).unwrap_or(false) && m.windows().len() == 1, s(&c));++    // 3. Up before the bridge ever looked: origin unknown.+    let m = Machine::new("dlg-before");+    m.write("windows.json", json!([file_open(205698)]));+    m.write("file-dialogs.json", json!({"205698": {"standard": true, "fileName": ""}}));+    let b = m.boot();+    let st = stop(&b);+    check!(+        b,+        "a dialog up before the first census: unattributed, never claimed",+        st["preconditions"]["dialog"]["attribution"]["bridgeCaused"] == false && st["preconditions"]["dialog"]["bridgeMayCancel"] == false,+        s(&st["preconditions"]["dialog"])+    );+    let c = cancel(&b);+    check!(b, "...and never cancelled", c["cancelled"].as_array().map(|a| a.is_empty()).unwrap_or(false) && m.windows().len() == 1, s(&c));++    // 4. Not the standard file dialog.+    let m = Machine::new("dlg-other");+    let b = m.boot();+    let mut other = file_open(205700);+    other["title"] = json!("Rebuild Errors");+    m.write("dialog-on-docs.json", json!([other]));+    m.write("file-dialogs.json", json!({"205700": {"standard": false, "fileName": ""}}));+    b.call("swx_docs", json!({}));+    let c = cancel(&b);+    check!(+        b,+        "another dialog (even raised by a bridge verb, nothing typed): never cancelled",+        c["cancelled"].as_array().map(|a| a.is_empty()).unwrap_or(false)+            && c["cancelRefused"][0]["why"].as_str().unwrap_or("").contains("not the standard Windows file dialog")+            && m.windows().len() == 1,+        s(&c)+    );+}
tests/e2e.rs+6−4
@@ -1160,12 +1160,14 @@ fn every_verb_over_http_against_the_real_binary() {         r["blockingDialogs"].is_array() || r["blockingDialogs"].is_null(),         s(&r)     );+    // 0.11.1: the census is NATIVE (the process's own windows), so ab being+    // unreachable no longer leaves it unknown; where the native census cannot be+    // read and ab is the fallback, an unreachable ab is still UNKNOWN.     check!(         b,-        "...and an unreachable ab makes it UNKNOWN, never an empty 'nothing in the way'",-        r["blockingDialogs"].is_null()-            && r["blockingDialogsKnown"] == false-            && r["blockingDialogsError"].is_string(),+        "...native and known, or (ab fallback, ab unreachable) UNKNOWN — never an empty 'nothing in the way' it could not see",+        (r["dialogCensusSource"] == "native" && r["blockingDialogsKnown"] == true && r["blockingDialogs"].is_array())+            || (r["blockingDialogs"].is_null() && r["blockingDialogsKnown"] == false && r["blockingDialogsError"].is_string()),         s(&r)     );     check!(b, "state carries this machine's measured timings", r["timings"].is_object(), s(&r));
uninstall.sh