app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
//! WORKSTREAM B — the graceful quit ladder (`swx_stop` v2).
//!
//! Design and evidence: `docs/QUIT-INVESTIGATION.md` §5. Every test drives a
//! simulated machine whose per-rung outcome is a written-down script
//! (`quit-script.json`), because the mock's old behaviour — rung (a) always
//! quits — made rungs (b)-(f) unreachable from any test, which is the fourth
//! time in this project that "the mock modelled the easy case" hid a defect.
//!
//! Success is measured by PROCESS CENSUS (the pid and its creation time absent),
//! never by a COM call: the whole reason v1 could not tell "declined" from
//! "quit, but pinned by our own pointer".
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()
}
/// A simulated machine with a scripted ladder.
struct Sim {
dir: TempDir,
}
impl Sim {
fn new(tag: &str, script: Value) -> Sim {
let dir = TempDir::new(tag);
std::fs::write(dir.join("quit-script.json"), script.to_string()).unwrap();
// The platform daemons and the per-session children. A graceful exit
// reaps the children and leaves the daemons alone.
std::fs::write(
dir.join("preexisting.json"),
json!([
{"pid": 6076, "image": "3DEXPERIENCELauncher.exe", "ageSeconds": 30000},
{"pid": 90552, "image": "3DEXPERIENCELauncherSysTray.exe", "ageSeconds": 30000},
{"pid": 701, "image": "ENOPLMCSAClient.exe", "ageSeconds": 30000},
{"pid": 702, "image": "EdmServerV6.exe", "ageSeconds": 30000},
{"pid": 800, "image": "sldProcMon.exe", "ageSeconds": 500},
{"pid": 801, "image": "swCefSubProc.exe", "ageSeconds": 500}
])
.to_string(),
)
.unwrap();
std::fs::write(
dir.join("sldworks.json"),
json!({"responding": true, "hwnd": 6622700, "privateMB": 900, "handles": 4000}).to_string(),
)
.unwrap();
Sim { dir }
}
fn windows(self, rows: Value) -> Sim {
std::fs::write(self.dir.join("windows.json"), rows.to_string()).unwrap();
self
}
fn sldworks(self, v: Value) -> Sim {
std::fs::write(self.dir.join("sldworks.json"), v.to_string()).unwrap();
self
}
fn quit_happened(&self) -> bool {
self.dir.join("quit.jsonl").exists()
}
fn boot(&self, extra: &[(&str, &str)]) -> Bridge {
let mut env: Vec<(&str, &str)> = vec![
("SWX_LAUNCH_FAKE_DIR", Box::leak(self.dir.path().into_boxed_str())),
("SWX_ALLOW_USER_WRITES", "1"),
// The ladder waits for the machine to settle after each rung; a test
// does not need a real second and a half.
("SWX_QUIT_SETTLE_MS", "80"),
// ...and does not need a real 30 seconds to watch a simulated
// process fail to exit, either. The WAIT itself is the thing under
// test in `an_exit_that_takes_seconds_is_a_quit_not_a_decline`, which
// sets its own.
("SWX_QUIT_EXIT_WAIT_SECS", "3"),
];
env.extend_from_slice(extra);
Bridge::start_in(TempDir::new("quit-scratch"), &env)
}
}
fn full_args(max_rung: &str) -> Value {
json!({
"confirm": true,
"allowUserWrites": true,
"reason": "the caller is finished with SOLIDWORKS",
"maxRung": max_rung
})
}
#[test]
fn rung_a_quits_a_clean_zero_document_session_and_stops_there() {
let m = Sim::new("quit-a", json!({"a": "exited"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", full_args("f"));
check!(b, "it quit", r["success"] == true && r["stopped"] == true, s(&r));
check!(b, "on rung a", r["winningRung"] == "a", s(&r));
check!(
b,
"THE LADDER STOPS AT THE FIRST RUNG THAT WORKED — no rung runs after success",
r["rungsTried"].as_array().map(|a| a.len() == 1).unwrap_or(false),
s(&r)
);
check!(b, "...and the win was measured by census, not by a COM answer", r["rungsTried"][0]["processGone"] == true, s(&r));
check!(b, "the machine agrees a quit happened", m.quit_happened(), "quit.jsonl missing");
// §2.6: the daemons SURVIVE by design, and that is consent.
let survivors = r["rungsTried"][0]["expectedSurvivorsAlive"].as_array().cloned().unwrap_or_default();
check!(b, "the platform daemons are still alive and that is not a failure", survivors.len() >= 4, s(&r["rungsTried"][0]));
check!(
b,
"...and the per-session children were reaped",
r["rungsTried"][0]["sessionChildrenStillAlive"] == json!([]),
s(&r["rungsTried"][0])
);
check!(
b,
"the hint says the daemons surviving is by design",
r["_hint"].as_str().unwrap_or("").contains("consent"),
s(&r)
);
}
#[test]
fn the_ladder_climbs_and_reports_every_rung_it_tried() {
// (a) and (b) decline, (c) — the WM_CLOSE rung — quits.
let m = Sim::new("quit-abc", json!({"a": "declined", "b": "declined", "c": "exited"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", full_args("f"));
check!(b, "it quit on rung c", r["stopped"] == true && r["winningRung"] == "c", s(&r));
let tried = r["rungsTried"].as_array().cloned().unwrap_or_default();
check!(b, "three rungs are recorded, in order", tried.len() == 3, s(&r));
check!(b, "a declined", tried[0]["rung"] == "a" && tried[0]["result"] == "declined" && tried[0]["processGone"] == false, s(&tried[0]));
check!(b, "b declined", tried[1]["rung"] == "b" && tried[1]["result"] == "declined", s(&tried[1]));
check!(b, "c quit", tried[2]["rung"] == "c" && tried[2]["result"] == "quit", s(&tried[2]));
check!(
b,
"every rung names what it actually did",
tried[0]["action"].as_str().unwrap_or("").contains("ExitApp")
&& tried[1]["action"].as_str().unwrap_or("").contains("2825")
&& tried[2]["action"].as_str().unwrap_or("").contains("WM_CLOSE"),
s(&r)
);
check!(b, "...and the census is reported before and after", r["processCensusBefore"].is_array() && r["processCensusAfter"].is_array(), s(&r));
}
#[test]
fn the_default_is_pinned_at_rung_a_until_the_gate_has_run() {
// Shipping the whole ladder armed on day one is the mistake crash recovery
// avoided by shipping `off`.
let m = Sim::new("quit-pinned", json!({"a": "declined", "b": "exited"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", json!({
"confirm": true, "allowUserWrites": true, "reason": "quit please"
}));
check!(b, "it did not quit, because rung b was never tried", r["success"] == false, s(&r));
check!(b, "only rung a ran", r["rungsTried"].as_array().map(|a| a.len() == 1).unwrap_or(false), s(&r));
check!(b, "...and maxRung says so", r["maxRung"] == "a", s(&r));
check!(b, "SOLIDWORKS is untouched", !m.quit_happened(), "quit.jsonl exists");
check!(
b,
"the hint names the human gesture and never a kill",
r["_hint"].as_str().unwrap_or("").contains("File ▸ Exit")
&& !r["_hint"].as_str().unwrap_or("").contains("swx_kill"),
s(&r)
);
}
#[test]
fn a_dialog_stops_the_ladder_and_is_never_answered() {
// Rung (a) raises a save prompt. The ladder must STOP, report the title, and
// close nothing — on 2026-09-02 closing a stuck modal through the window
// manager took the whole application down.
let m = Sim::new("quit-dialog", json!({"a": "dialog", "b": "exited"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", full_args("f"));
check!(b, "the quit failed", r["success"] == false, s(&r));
check!(
b,
"a save prompt gets its OWN code, because at a verified 0 documents it should be impossible",
r["errorCode"] == "stop_blocked_by_save_prompt",
s(&r)
);
check!(b, "the dialog is reported verbatim", r["blockingDialog"]["title"] == "Save Modified Documents", s(&r));
check!(b, "the ladder stopped at that rung", r["rungsTried"].as_array().map(|a| a.len() == 1).unwrap_or(false), s(&r));
check!(b, "rung b never ran, so nothing escalated past the dialog", !m.quit_happened(), "quit.jsonl exists");
check!(
b,
"the hint says answering it is the user's decision",
r["_hint"].as_str().unwrap_or("").to_lowercase().contains("their decision")
&& r["_hint"].as_str().unwrap_or("").to_lowercase().contains("never answers a dialog"),
s(&r)
);
}
#[test]
fn a_blocked_message_is_reported_as_blocked_not_declined() {
// UIPI failures return SUCCESS, so an unchecked GetLastError would report a
// quit that never happened. An access-denied is a fact about the machine's
// integrity levels, not about SOLIDWORKS' willingness to quit, so the ladder
// stops rather than trying the same blocked gesture another way.
let m = Sim::new("quit-uipi", json!({"a": "declined", "b": "declined", "c": "uipiBlocked", "d": "exited"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", full_args("f"));
let tried = r["rungsTried"].as_array().cloned().unwrap_or_default();
check!(b, "the quit failed", r["success"] == false, s(&r));
check!(b, "rung c is recorded as BLOCKED, not declined", tried[2]["result"] == "uipiBlocked", s(&tried[2]));
check!(b, "and the ladder stopped there", tried.len() == 3, s(&r));
check!(b, "rung d never ran", !m.quit_happened(), "quit.jsonl exists");
}
#[test]
fn a_session_with_open_documents_is_never_quit_even_with_force() {
// THE REGRESSION TEST FOR THE LIVE POSTURE. v1 gated on the UNSAVED count,
// and on 2026-09-08 five of Drew's clean documents were open with
// unsavedDocuments:0 — so a bare swx_stop would have tried to quit them.
let m = Sim::new("quit-opendocs", json!({"a": "exited"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
for _ in 0..5 {
b.call("swx_new_document", json!({"type": "part"}));
}
let mut args = full_args("f");
args["force"] = json!(true);
let r = b.call("swx_stop", args);
check!(b, "it refuses", r["success"] == false, s(&r));
check!(b, "with the open-document code, distinct from unsaved", r["errorCode"] == "open_documents_present", s(&r));
check!(b, "no rung ran at all", r["rungsTried"] == json!([]), s(&r));
check!(b, "SOLIDWORKS is untouched", !m.quit_happened(), "quit.jsonl exists");
check!(
b,
"...and it says force does not waive this",
r["_hint"].as_str().unwrap_or("").contains("force` does not"),
s(&r)
);
// The refusal is RECORDED. v1 wrote no ledger line at its front gate.
let led = b.call("swx_action_ledger", json!({"limit": 50}));
check!(b, "a refusal is in the ledger", s(&led).contains("solidworks_quit_refused"), s(&led));
}
#[test]
fn a_precondition_that_cannot_be_measured_refuses_and_never_proceeds() {
// The window census is unreadable, so "is a dialog up" has no answer — and a
// quit sent at a modal is a different act with a different outcome.
let m = Sim::new("quit-blind", json!({"a": "exited"}));
std::fs::write(m.dir.join("window-census-fails"), "").unwrap();
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", full_args("f"));
check!(b, "it refuses", r["success"] == false, s(&r));
check!(b, "with one branchable unknown code", r["errorCode"] == "stop_preconditions_unknown", s(&r));
check!(b, "naming what could not be measured", r["error"].as_str().unwrap_or("").contains("dialog"), s(&r));
check!(b, "no rung ran", r["rungsTried"] == json!([]), s(&r));
check!(b, "SOLIDWORKS is untouched", !m.quit_happened(), "quit.jsonl exists");
}
#[test]
fn a_not_responding_session_is_never_asked_to_quit() {
// A quit request cannot be delivered to a message loop that is not running,
// and the window rungs aimed at a wedged frame are exactly how a half-closed
// orphan is made.
let m = Sim::new("quit-hung", json!({"a": "exited"}))
.sldworks(json!({"responding": false, "hwnd": 6622700, "privateMB": 17900}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", full_args("f"));
check!(b, "it refuses", r["success"] == false && r["errorCode"] == "host_app_hung", s(&r));
check!(b, "no rung ran", r["rungsTried"] == json!([]), s(&r));
check!(b, "SOLIDWORKS is untouched", !m.quit_happened(), "quit.jsonl exists");
}
#[test]
fn the_restart_manager_rung_is_opt_in_only() {
// ENDSESSION_CLOSEAPP means "save automatically without prompting", so rung
// (f) is the one rung whose failure mode is a silent write rather than a
// dialog we can stop on. It is never taken implicitly.
let m = Sim::new("quit-rm", json!({
"a": "declined", "b": "declined", "c": "declined", "d": "declined", "e": "declined",
"f": "exited"
}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", full_args("f"));
let tried = r["rungsTried"].as_array().cloned().unwrap_or_default();
check!(b, "without the opt-in it does not quit", r["success"] == false, s(&r));
check!(b, "...and rung f is recorded as SKIPPED, not declined", tried[5]["result"] == "skipped", s(&tried[5]));
check!(
b,
"...saying it needs allowRestartManager:true",
tried[5]["detail"].as_str().unwrap_or("").contains("allowRestartManager"),
s(&tried[5])
);
check!(b, "SOLIDWORKS is untouched", !m.quit_happened(), "quit.jsonl exists");
// WITH the opt-in it runs, and it is the last rung.
let mut args = full_args("f");
args["allowRestartManager"] = json!(true);
let r2 = b.call("swx_stop", args);
check!(b, "with the opt-in, rung f quits it", r2["stopped"] == true && r2["winningRung"] == "f", s(&r2));
check!(b, "having tried all six", r2["rungsTried"].as_array().map(|a| a.len() == 6).unwrap_or(false), s(&r2));
}
#[test]
fn a_quit_that_did_not_quit_is_a_failure_with_every_rung_measured() {
let m = Sim::new("quit-declined", json!({
"a": "declined", "b": "declined", "c": "declined", "d": "declined", "e": "declined"
}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", full_args("e"));
check!(b, "it is a FAILURE, not a quiet success", r["success"] == false, s(&r));
check!(b, "with the declined code", r["errorCode"] == "host_app_stop_declined", s(&r));
check!(b, "winningRung is null", r["winningRung"].is_null(), s(&r));
let tried = r["rungsTried"].as_array().cloned().unwrap_or_default();
check!(b, "all five rungs measured", tried.len() == 5, s(&r));
for t in &tried {
check!(b, "each rung reports processGone explicitly", t["processGone"] == false, s(t));
check!(b, "...and how long it took", t["elapsedSec"].is_number(), s(t));
}
check!(b, "elapsed is reported for the whole attempt", r["elapsedSeconds"].is_number(), s(&r));
}
#[test]
fn nothing_is_disowned_until_the_process_is_verifiably_gone() {
// v1 cleared the created-document registry BEFORE it verified, so a declined
// quit left the bridge unable to touch its own documents.
let m = Sim::new("quit-disown", json!({"a": "declined"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let made = b.call("swx_new_document", json!({"type": "part"}));
let title = made["created"]["title"].as_str().unwrap_or("Part1").to_string();
// The quit is refused (documents are open) — and even a rung-level decline
// must not disown anything.
let mut args = full_args("a");
args["force"] = json!(true);
let r = b.call("swx_stop", args);
check!(b, "the quit did not happen", r["stopped"] != json!(true), s(&r));
// The bridge still owns its document.
let w = b.call("swx_props_set", json!({"targetDocument": title, "properties": {"Owner": "bridge"}}));
check!(b, "the bridge still owns what it created", w["success"] == true, s(&w));
}
#[test]
fn nothing_in_the_stop_path_ever_kills_anything() {
// A BEHAVIOURAL assertion: across a full declined ladder, nothing was
// terminated and no expected survivor disappeared.
let m = Sim::new("quit-nokill", json!({
"a": "declined", "b": "declined", "c": "declined", "d": "declined", "e": "declined"
}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let mut args = full_args("f");
args["allowRestartManager"] = json!(true);
let r = b.call("swx_stop", args);
check!(b, "it declined", r["success"] == false, s(&r));
check!(
b,
"NOTHING was terminated — the launch path's kill log is empty",
!m.dir.join("terminated.jsonl").exists(),
"terminated.jsonl exists"
);
let after = r["processCensusAfter"].as_array().cloned().unwrap_or_default();
for want in ["3DEXPERIENCELauncher.exe", "ENOPLMCSAClient.exe", "EdmServerV6.exe", "sldProcMon.exe"] {
check!(
b,
"every process that was alive before is alive after",
after.iter().any(|p| p["image"] == want),
format!("{want} missing from {}", s(&r["processCensusAfter"]))
);
}
// ...and the response never suggests a kill.
let body = s(&r);
check!(b, "the report never mentions swx_kill or taskkill", !body.contains("swx_kill") && !body.contains("taskkill"), body);
}
#[test]
fn the_verification_releases_every_reference_before_it_asks() {
// An automation client's outstanding IDispatch is exactly what keeps a
// SOLIDWORKS alive, so v1 could not tell "declined" from "quit, but pinned
// by our own pointer" — it asked while holding one.
let m = Sim::new("quit-refs", json!({"a": "declined"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", full_args("a"));
let d = r["rungsTried"][0]["detail"].as_str().unwrap_or("").to_string();
check!(
b,
"no rung reports outstanding references at its measurement",
!d.contains("still outstanding"),
s(&r["rungsTried"][0])
);
}
// ════════════════════════════════════════════════════════════════════════════
// ADVERSARY PASS 7 — the once-swept precondition, and the one-sample exit
// ════════════════════════════════════════════════════════════════════════════
/// A REAL `ExitApp` RETURNS AND THE PROCESS THEN TAKES SECONDS TO GO — ten CEF
/// subprocesses, three out-of-process servers and a journal to close. v2 sampled
/// the census once, 1.5 s after the rung, and called anything still present
/// "declined" — and the ladder's answer to a decline is to ESCALATE, sending a
/// window message into an application that was already exiting.
///
/// So the measurement waits, at the settle interval, up to the rung's own
/// budget. `exitAfterMs` in the simulator is what makes that a tested path
/// rather than a comment: the census keeps reporting the process for the delay,
/// exactly as a real slow exit does.
#[test]
fn an_exit_that_takes_seconds_is_a_quit_not_a_decline() {
let m = Sim::new("quit-slow", json!({"a": {"result": "exited", "exitAfterMs": 1200}}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", full_args("f"));
check!(
b,
"a slow exit is measured as a QUIT on rung (a), not escalated past",
r["stopped"] == true && r["winningRung"] == "a",
s(&r)
);
let tried = r["rungsTried"].as_array().cloned().unwrap_or_default();
check!(b, "...and no rung after it was sent", tried.len() == 1, s(&r));
check!(
b,
"...measured over MORE than one census read (the first one saw it alive)",
tried[0]["detail"].as_str().unwrap_or("").contains("census reads"),
s(&tried[0])
);
check!(b, "...and the process really is gone", r["stillRunning"] == false, s(&r));
}
/// The three reap lists are only evidence if the census they came from could be
/// READ. Three empty lists otherwise say "everything was reaped, nothing
/// survived" — the most reassuring possible reading of no information at all.
#[test]
fn the_reap_lists_say_whether_the_census_was_readable() {
let m = Sim::new("quit-censusflag", json!({"a": "exited"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", full_args("a"));
let tried = r["rungsTried"].as_array().cloned().unwrap_or_default();
check!(
b,
"every rung reports whether its census was readable",
tried.iter().all(|t| t["censusReadable"].is_boolean()),
s(&r)
);
check!(b, "...and here it was", tried[0]["censusReadable"] == true, s(&tried[0]));
}
/// The dialog census must cover the pids a 3DEXPERIENCE modal can live in: a
/// sign-in / licence / cloud-save window is a WEB VIEW, and its hwnd belongs to
/// `swCefSubProc.exe` — a child of `sldworks.exe` with its own pid.
#[test]
fn the_dialog_census_covers_the_web_view_processes_too() {
let m = Sim::new("quit-cef", json!({"a": "exited"}));
// The simulated session's OWN children — parented to the simulated
// SOLIDWORKS, whose pid no fixture can know in advance.
std::fs::write(
m.dir.join("sldworks-children.json"),
json!(["swCefSubProc.exe", "swCefSubProc.exe", "sldProcMon.exe"]).to_string(),
)
.unwrap();
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", full_args("a"));
let pids = r["preconditions"]["dialogCensusPids"].as_array().cloned().unwrap_or_default();
check!(
b,
"the census names more than the SOLIDWORKS pid — the CEF child is in it",
pids.len() >= 2,
s(&r["preconditions"])
);
}
/// THE PER-RUNG RE-SWEEP IS NOT DECORATION.
///
/// `swx_stop` re-measures every precondition before every rung, and the launch
/// claim check is part of that sweep. Nothing could demonstrate it: the
/// simulator's attached pid was fixed for the life of a run, so the FRONT GATE
/// would always be what caught a mismatch and the per-rung call site could have
/// been deleted without a single test noticing.
///
/// `mock-com-pid` hands out a different pid on each read. Here the session is the
/// one the launch record claims when rung (a) runs, and a DIFFERENT one by the
/// time rung (b) would — which is the situation the re-sweep exists for: an
/// attribution true at rung (a) is not automatically true at rung (b).
#[test]
fn the_session_changing_between_rungs_is_caught_by_the_per_rung_sweep() {
// Rung (a) declines, so the ladder would go on to rung (b).
let m = Sim::new("rung-identity", json!({"a": "declined", "b": "exited"}));
// A FIXED identity for the session the claim is about, so the fixture can
// name it up front: the pid the mock reports and the creation time the
// census gives it.
std::fs::write(m.dir.join("sldworks-created"), "1700000000").unwrap();
// ...and a SECOND process that exists in the census, so the identity after
// the switch is resolvable (an UNRESOLVABLE one refuses as unknown, which is
// a different rule and would not exercise this one).
std::fs::write(
m.dir.join("census-overrides.json"),
json!([{"pid": 700901, "image": "SLDWORKS.exe", "createdAt": 1700000000u64}]).to_string(),
)
.unwrap();
let scratch = TempDir::new("rung-identity-scratch");
std::fs::write(scratch.join("mock-solidworks-session"), "700900").unwrap();
// The launch record claims the FIRST session, truthfully.
std::fs::write(
scratch.join("launch-state.json"),
json!({
"attempt": {
"id": 1, "startedAt": 1700000000u64, "shortcut": "x.lnk",
"route": "shellExecute", "invokedAt": 1700000000u64,
"stage": "attached", "finished": true, "outcome": "attachedAfterSignIn",
"runBeginSeen": true,
"attributedSolidworks": {"pid": 700900, "createdAt": 1700000000u64},
},
"preferredRoute": "shellExecute", "consecutiveFailures": 0,
})
.to_string(),
)
.unwrap();
// THE SEQUENCE, counted from the start of the `swx_stop` call (the cursor is
// reset immediately before it, below):
// read 1 `app_state` — the USER-PRESENCE guard (0.9.9), before the verb
// read 2 `app_state` — the document snapshot before a changing verb (0.9.10)
// read 3 `status` — the "is anything running" check
// read 4 `app_state` — the FRONT GATE sweep: still the claimed session
// read 5 `app_state` — the PER-RUNG sweep before rung (b): a different one
std::fs::write(scratch.join("mock-com-pid"), "700900 700900 700900 700900 700901").unwrap();
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[
("SWX_LAUNCH_FAKE_DIR", Box::leak(m.dir.path().into_boxed_str())),
("SWX_ALLOW_USER_WRITES", "1"),
("SWX_QUIT_SETTLE_MS", "80"),
("SWX_QUIT_EXIT_WAIT_SECS", "2"),
],
);
b.call("swx_start", json!({}));
// Count from here: everything above (the start, and the host's own boot
// chatter) has already read whatever it needed.
std::fs::write(scratch.join("mock-com-pid.cursor"), "0").unwrap();
let r = b.call("swx_stop", full_args("b"));
check!(
b,
"the front gate let it through — the claim was TRUE when rung (a) ran",
r["rungsTried"].as_array().map(|a| !a.is_empty()).unwrap_or(false),
s(&r)
);
check!(
b,
"...and the PER-RUNG sweep caught the session changing underneath it",
r["errorCode"] == "identity_mismatch" && r["refusedMidLadder"] == true,
s(&r)
);
check!(
b,
"...naming both processes",
r["error"].as_str().unwrap_or("").contains("700900")
&& r["error"].as_str().unwrap_or("").contains("700901"),
s(&r)
);
}
// ── PRIMING A NEVER-HAD-A-DOCUMENT SESSION (measured 2026-10-01) ───────────
/// MEASURED 2026-10-01 on Drew's machine (pid 20144): rung (a) was DECLINED three
/// times on a bridge-launched session that had never held a document — 0
/// documents, no dialog, census readable — and quit in 3.4 s on the SAME session
/// once one scratch part had been created and closed. The simulator models that
/// (`exitedIfEverHadDocument`). Under the user's authorization, on an empty
/// session with no dialog, the ladder PRIMES the session — one bridge-owned
/// scratch part, created and closed — and asks again; both attempts are in
/// `rungsTried`, the second `primed:true`, and the prime is ledgered.
fn authorized(max_rung: &str) -> Value {
json!({"userAuthorized": true, "reason": "the user asked me to quit SOLIDWORKS", "maxRung": max_rung})
}
fn ledger_has(b: &Bridge, action: &str) -> bool {
b.call("swx_action_ledger", json!({"limit": 200}))["actions"]
.as_array()
.map(|a| a.iter().any(|e| e["action"] == action))
.unwrap_or(false)
}
#[test]
fn a_never_had_a_document_session_is_primed_and_then_quits() {
let m = Sim::new("quit-prime", json!({"a": "exitedIfEverHadDocument"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", authorized("a"));
let tries = r["rungsTried"].as_array().cloned().unwrap_or_default();
check!(b, "swx_stop: the primed session quits on rung a", r["success"] == true && r["stopped"] == true, s(&r));
check!(
b,
"swx_stop: ...after a first ExitApp was declined, and the second attempt says it was primed",
tries.len() == 2
&& tries[0]["result"] == "declined"
&& tries[0]["primed"] == false
&& tries[1]["result"] == "quit"
&& tries[1]["primed"] == true,
s(&r)
);
check!(b, "swx_stop: the prime is in the ledger", ledger_has(&b, "quit_prime"), String::new());
// 0.10.7: VISIBLE — a hidden document is not what was measured to prime a
// session, and 0.10.6's hidden prime was reverted with every other default.
check!(
b,
"swx_stop: the scratch part that primed it was created VISIBLE (no DocumentVisible call)",
!b.scratch.join("document-visible.jsonl").exists(),
String::new()
);
}
/// Priming needs the user's authorization: under the write opt-in alone, a
/// declined rung (a) is reported as declined and nothing is created.
#[test]
fn priming_needs_the_users_authorization() {
let m = Sim::new("quit-prime-noauth", json!({"a": "exitedIfEverHadDocument"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", full_args("a"));
let tries = r["rungsTried"].as_array().cloned().unwrap_or_default();
check!(
b,
"swx_stop: without userAuthorized the decline stands, unprimed",
r["stopped"] == false && tries.len() == 1 && tries[0]["result"] == "declined",
s(&r)
);
check!(b, "swx_stop: ...and no scratch part was made to prime it", !ledger_has(&b, "quit_prime"), String::new());
}
/// NEVER PRIME A SESSION SOMEBODY IS WORKING IN. A document that appears between
/// the declined rung and the prime stops it: nothing is created, the user's
/// document stays open, and the attempt says why it was not primed.
#[test]
fn a_session_with_a_document_open_is_never_primed() {
let m = Sim::new("quit-prime-busy", json!({"a": "exitedIfEverHadDocument"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
std::fs::write(b.scratch.join("mock-doc-appears-after-decline"), "").unwrap();
let r = b.call("swx_stop", authorized("a"));
let tries = r["rungsTried"].as_array().cloned().unwrap_or_default();
check!(
b,
"swx_stop: a document opened meanwhile means NO prime — the decline stands",
r["stopped"] == false
&& tries.len() == 1
&& tries[0]["detail"].as_str().unwrap_or("").contains("not primed"),
s(&r)
);
check!(b, "swx_stop: ...nothing was created to prime it", !ledger_has(&b, "quit_prime"), String::new());
let docs = b.call("swx_docs", json!({}));
check!(
b,
"swx_docs: the user's document is still open, untouched",
s(&docs).contains("opened-meanwhile"),
s(&docs)
);
}
// ── A NOTIFICATION IS NOT A MODAL (measured 2026-10-01) ────────────────────
/// The window measured on Drew's machine: hwnd 331370, `#32770`, no title, owner
/// = the main frame and ENABLED for all 20 reads over 95 s, exstyle 0x90080
/// (TOOLWINDOW | LAYERED | CONTROLPARENT), 301x67 px, all children Static — the
/// 3DEXPERIENCE "Connected to …" notice, custom-drawn over template captions.
fn connected_notice(owner_enabled: bool) -> (Value, Value) {
(
json!([{"hwnd": 331370, "className": "#32770", "title": "", "visible": true,
"owner": 4242, "ownerEnabled": owner_enabled, "style": 0x9400_0044u64,
"exStyle": 0x90080u64, "rect": [533, 45, 834, 112]}]),
json!({"hwnd": 331370, "controls": [
["Static", 0, "Title", true], ["Static", 0, "Description", true],
["Static", 0, "Button1", false], ["Static", 0, "Button2", false],
["Static", 0, "Hyperlink Text 1", false], ["Static", 0, "Hyperlink Text 2", false]
]}),
)
}
#[test]
fn a_notification_window_does_not_block_the_quit_and_is_reported() {
let (windows, contents) = connected_notice(true);
let m = Sim::new("quit-notification", json!({"a": "exited"})).windows(windows);
std::fs::write(m.dir.join("dialog-open.json"), contents.to_string()).unwrap();
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", authorized("a"));
check!(
b,
"swx_stop: a notification (owner frame enabled, tool window, nothing to click) does not block the quit",
r["success"] == true && r["stopped"] == true,
s(&r)
);
let n = r["preconditions"]["notifications"][0].clone();
check!(
b,
"swx_stop: ...it is REPORTED, with its evidence, as a notification — never answered",
n["kind"] == "notification"
&& n["blocking"] == false
&& n["neverAnswer"] == true
&& n["evidence"]["window"]["ownerEnabled"] == true
&& n["evidence"]["window"]["exStyle"] == "0x90080"
&& n["evidence"]["window"]["rect"] == json!([533, 45, 834, 112])
&& n["evidence"]["controls"].as_array().map(|a| a.len() == 6).unwrap_or(false),
s(&r["preconditions"])
);
check!(
b,
"swx_stop: ...and its template captions are NOT presented as its words",
n["textIsTemplatePlaceholder"] == true && n["text"].is_null() && n["placeholderCaptions"][0] == "Title",
s(&n)
);
}
/// The same window with its owner frame DISABLED is a modal: the quit stops on
/// it, and the report carries the evidence that decided it.
#[test]
fn the_same_window_over_a_disabled_frame_blocks_and_says_why() {
let (windows, contents) = connected_notice(false);
let m = Sim::new("quit-modal", json!({"a": "exited"})).windows(windows);
std::fs::write(m.dir.join("dialog-open.json"), contents.to_string()).unwrap();
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let r = b.call("swx_stop", authorized("a"));
check!(
b,
"swx_stop: owner frame disabled → a modal → stop_blocked_by_dialog, nothing attempted",
r["success"] == false && r["errorCode"] == "stop_blocked_by_dialog" && !m.quit_happened(),
s(&r)
);
let d = r["preconditions"]["dialog"].clone();
check!(
b,
"swx_stop: ...with the evidence and the reason in the report",
d["blocking"] == true
&& d["evidence"]["window"]["ownerEnabled"] == false
&& d["why"].as_str().unwrap_or("").contains("DISABLED"),
s(&r)
);
}
// ── 0.9.5 (adversary pass 42): PRIMING'S OTHER END, AND QUIT DURING STARTUP ─
/// THE SESSION'S HISTORY IS ADVISORY — NEVER A GUARD ON PRIMING (0.9.6, P1).
/// Live 2026-10-01 (pid 29108): a false `everHadDocument:true` made the guard
/// refuse to prime, and the quit was declined twice. The trigger is the
/// measured condition itself: rung (a) declined, the session empty, no blocking
/// dialog, userAuthorized. Here the history is TRUE (the bridge made and closed
/// a part) and the session is still primed once and asked again.
#[test]
fn a_session_that_has_held_a_document_is_still_primed_when_empty() {
let m = Sim::new("quit-prime-held", json!({"a": "declined"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let made = b.call("swx_new_document", json!({"type": "part"}));
check!(b, "precondition: a document was made", made["success"] == true, s(&made));
let reset = b.call("swx_reset_workspace", json!({}));
check!(b, "precondition: ...and closed", reset["success"] == true, s(&reset));
let st = b.call("swx_status", json!({}));
check!(
b,
"swx_status: the session is recorded as having held a document (advisory)",
st["solidworks"]["sessionHistory"]["everHadDocument"] == true
&& st["solidworks"]["sessionHistory"]["advisory"] == true,
s(&st["solidworks"]["sessionHistory"])
);
let r = b.call("swx_stop", authorized("a"));
let tries = r["rungsTried"].as_array().cloned().unwrap_or_default();
check!(
b,
"swx_stop: declined, primed ONCE, asked again — whatever the history says",
tries.len() == 2
&& tries[0]["result"] == "declined"
&& tries[0]["primed"] == false
&& tries[1]["primed"] == true,
s(&r)
);
check!(b, "swx_stop: the prime is in the ledger", ledger_has(&b, "quit_prime"), String::new());
}
/// A PHANTOM DOCUMENT COUNT NEVER RECORDS HISTORY, AND PRIMING STILL FIRES. The
/// live shape (pid 29108): a session nobody opened a document in read a non-zero
/// count while starting. Simulated twice over: a count of 1 for as long as the
/// start is incomplete, and — after the start — a count of 1 on ONE poll only.
/// Neither is evidence; the declined quit is primed and SOLIDWORKS quits.
#[test]
fn a_phantom_document_count_never_records_history_and_priming_still_fires() {
let m = Sim::new("quit-prime-phantom", json!({"a": "exitedIfEverHadDocument"}));
let b = m.boot(&[]);
std::fs::write(b.scratch.join("mock-startup-incomplete"), "").unwrap();
std::fs::write(b.scratch.join("mock-phantom-doc-while-starting"), "").unwrap();
b.call("swx_start", json!({}));
// Polls 400 ms apart for 1.6 s: a count read while starting, persisting —
// still never evidence.
for _ in 0..5 {
b.call("swx_status", json!({}));
std::thread::sleep(std::time::Duration::from_millis(400));
}
std::fs::remove_file(b.scratch.join("mock-startup-incomplete")).unwrap();
std::fs::write(b.scratch.join("mock-phantom-doc-once"), "").unwrap();
b.call("swx_status", json!({}));
std::thread::sleep(std::time::Duration::from_millis(1100));
let st = b.call("swx_status", json!({}));
let h = &st["solidworks"]["sessionHistory"];
check!(
b,
"swx_status: neither a count read while starting nor a one-poll count records a document",
h["everHadDocument"] != true,
s(h)
);
let r = b.call("swx_stop", authorized("a"));
let tries = r["rungsTried"].as_array().cloned().unwrap_or_default();
check!(
b,
"swx_stop: declined, primed, and then it quits",
r["stopped"] == true && tries.len() == 2 && tries[1]["primed"] == true && tries[1]["result"] == "quit",
s(&r)
);
}
/// `false` MEANS "NONE SEEN BY THIS BRIDGE", and says so.
#[test]
fn ever_had_document_false_is_worded_as_none_seen_by_this_bridge() {
let m = Sim::new("quit-history-wording", json!({"a": "exited"}));
// The bridge LAUNCHED this session: the launch record attributes it, by a
// fixed pid and creation time.
std::fs::write(m.dir.join("sldworks-created"), "1700000000").unwrap();
let scratch = TempDir::new("quit-history-wording-scratch");
std::fs::write(scratch.join("mock-solidworks-session"), "700950").unwrap();
std::fs::write(
scratch.join("launch-state.json"),
json!({
"attempt": {
"id": 1, "startedAt": 1700000000u64, "shortcut": "x.lnk",
"route": "shellExecute", "invokedAt": 1700000000u64,
"stage": "attached", "finished": true, "outcome": "attached",
"runBeginSeen": true,
"attributedSolidworks": {"pid": 700950, "createdAt": 1700000000u64},
},
"preferredRoute": "shellExecute", "consecutiveFailures": 0,
})
.to_string(),
)
.unwrap();
let b = Bridge::start_in(
scratch,
&[("SWX_LAUNCH_FAKE_DIR", Box::leak(m.dir.path().into_boxed_str())), ("SWX_ALLOW_USER_WRITES", "1")],
);
b.call("swx_start", json!({}));
let st = b.call("swx_status", json!({}));
let h = &st["solidworks"]["sessionHistory"];
check!(
b,
"swx_status: a bridge-launched, never-seen-a-document session reads false — 'none seen by this bridge'",
h["everHadDocument"] == false && h["basis"].as_str().unwrap_or("").starts_with("none seen by this bridge"),
s(h)
);
}
/// A PRIME WHOSE CLOSE FAILS names the bridge's own scratch part, where it is,
/// and how to drain it — not just "not empty afterwards".
#[test]
fn a_prime_whose_close_fails_names_the_part_and_how_to_drain_it() {
let m = Sim::new("quit-prime-closefail", json!({"a": "exitedIfEverHadDocument"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
std::fs::write(b.scratch.join("mock-closedoc-fails"), "").unwrap();
let r = b.call("swx_stop", authorized("a"));
let tries = r["rungsTried"].as_array().cloned().unwrap_or_default();
let detail = tries.first().and_then(|t| t["detail"].as_str()).unwrap_or("").to_string();
check!(
b,
"swx_stop: the prime stopped, saying the bridge's own part is still open, where, and to drain it",
r["stopped"] == false
&& detail.contains("could NOT be closed")
&& detail.contains(".SLDPRT")
&& detail.contains("swx_reset_workspace"),
s(&r)
);
}
/// THE WINDOW-MESSAGE RUNGS DO NOT WAIT FOR READINESS. Under the user's
/// authorization, a plan that reaches (c)/(d) runs while SOLIDWORKS is still
/// starting, with the COM rungs skipped; a plan that does not is refused
/// `host_app_starting`, and the refusal names the way out.
#[test]
fn the_window_message_rungs_run_during_startup_with_the_com_rungs_skipped() {
let m = Sim::new("quit-startup-window", json!({"c": "exited"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
std::fs::write(b.scratch.join("mock-startup-incomplete"), "").unwrap();
let refused = b.call("swx_stop", authorized("a"));
check!(
b,
"swx_stop maxRung a during startup: refused, naming the window-message rungs",
refused["errorCode"] == "host_app_starting" && refused["windowMessageRungs"].is_string(),
s(&refused)
);
let noauth = b.call("swx_stop", full_args("d"));
check!(
b,
"...never without the user's authorization: host_app_starting, no rung tried",
noauth["errorCode"] == "host_app_starting" && noauth["rungsTried"].is_null(),
s(&noauth)
);
let r = b.call("swx_stop", authorized("d"));
let tries = r["rungsTried"].as_array().cloned().unwrap_or_default();
check!(
b,
"swx_stop maxRung d during startup: a and b skipped (no COM), c quits",
r["stopped"] == true
&& tries.len() == 3
&& tries[0]["result"] == "skipped"
&& tries[1]["result"] == "skipped"
&& tries[2]["rung"] == "c"
&& tries[2]["result"] == "quit",
s(&r)
);
}
/// INTERNAL ARGUMENTS ARE STRIPPED FROM CALLER INPUT (0.9.7, adversary pass 44
/// F5). `_startupNotVerified` makes the ladder skip its gentle COM rungs; a
/// caller who passes it on a READY session must not get that — rung (a) runs.
#[test]
fn a_caller_cannot_skip_the_gentle_rungs_with_an_internal_argument() {
let m = Sim::new("quit-internal-arg", json!({"a": "exited"}));
let b = m.boot(&[]);
b.call("swx_start", json!({}));
let mut args = authorized("d");
args["_startupNotVerified"] = json!(true);
let r = b.call("swx_stop", args);
let tries = r["rungsTried"].as_array().cloned().unwrap_or_default();
check!(
b,
"swx_stop: rung (a) ran and quit — the internal argument was ignored",
r["stopped"] == true && tries.first().map(|t| t["rung"] == "a" && t["result"] == "quit").unwrap_or(false),
s(&r)
);
}