main
Drew Owens 0.10.7 source e6a7431 6d ago
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)
    );
}