main
Drew Owens 0.10.6 source e2dca5f 6d ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072
//! ATTACHED IS NOT READY — measured 2026-10-01, and pinned here.
//!
//! On Drew's machine a warm launch attached over COM in 29.7 s; `swx_new_document`
//! 8 s later, `swx_open` at 9 s and `swx_close_all_safely` at 12 s CRASHED
//! SOLIDWORKS while its 3DEXPERIENCE add-in was still initialising (its
//! performance log: LOAD_ADDIN lines still running, then SWEXH / UNHANDLED, the
//! 3DEXPERIENCE marker only AFTER the document commands, SWERR_REPORT). A control
//! launch with no document commands crashed nothing and logged `<3DXLOAD_TIME>`
//! when the platform load finished. So:
//!
//!   * every document-creating, -opening, -saving or -closing verb, and the quit,
//!     refuses `host_app_starting` until SOLIDWORKS has finished starting;
//!   * `swx_start` reports `stage:"ready"` only then, and `swx_status` exposes
//!     `solidworks.ready` with what it is based on;
//!   * two independent signals: COM's `StartupProcessCompleted`, and the
//!     performance log (a 3DEXPERIENCE marker after the last LOAD_ADDIN line);
//!   * a sweep during which SOLIDWORKS dies says `host_app_gone`, with the log's
//!     crash markers, never `dialog_census_unknown`;
//!   * SOLIDWORKS' `~$` lock files are not offered as rescues.

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()
}

fn a_visible_machine(tag: &str) -> TempDir {
    let d = TempDir::new(tag);
    std::fs::write(d.join("windows.json"), "[]").unwrap();
    d
}

fn session(scratch: &TempDir, docs: Value) {
    std::fs::write(
        scratch.join("mock-solidworks-open-docs.json"),
        json!({"running": true, "untitled": 1, "active": Value::Null, "docs": docs, "files": {}}).to_string(),
    )
    .unwrap();
}

/// Minimal arguments for each gated verb — enough to reach the gate, which comes
/// before every one of them.
fn gated_calls() -> Vec<(&'static str, Value)> {
    let reason = "the user asked for it";
    vec![
        ("swx_new_document", json!({"type": "part"})),
        ("swx_open", json!({"path": "C:\\Users\\drew\\work\\bracket.SLDPRT"})),
        ("swx_import", json!({"path": "C:\\Users\\drew\\work\\x.step"})),
        ("swx_save", json!({})),
        ("swx_save_as", json!({"outputPath": "C:\\out\\x.SLDPRT"})),
        ("swx_run_script", json!({"script": []})),
        ("swx_run_macro", json!({"path": "C:\\x.swp", "reason": reason})),
        ("swx_add_component", json!({"path": "C:\\x.SLDPRT"})),
        ("swx_close_doc", json!({"name": "Part1"})),
        ("swx_close_all_documents", json!({"confirm": true})),
        ("swx_close_all_safely", json!({"reason": reason})),
        ("swx_close_import_temps", json!({"reason": reason})),
        ("swx_reset_workspace", json!({})),
        ("swx_shutdown", json!({"reason": reason})),
        ("swx_stop", json!({"userAuthorized": true, "reason": reason})),
    ]
}

#[test]
fn document_verbs_wait_for_solidworks_to_finish_starting() {
    let scratch = TempDir::new("ready-gate");
    session(&scratch, json!([]));
    std::fs::write(scratch.join("mock-startup-incomplete"), "").unwrap();
    let machine = a_visible_machine("ready-gate-machine");
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_LAUNCH_FAKE_DIR", &machine.path())]);

    let started = b.call("swx_start", json!({}));
    check!(
        b,
        "swx_start: attached but still starting — stage `starting`, never `ready`",
        started["success"] == true
            && started["stage"] == "starting"
            && started["ready"] == false
            && started["stillStarting"] == true,
        s(&started)
    );
    let st = b.call("swx_status", json!({}));
    check!(
        b,
        "swx_status: `solidworks.ready` is false, and says what it is based on",
        st["solidworks"]["ready"] == false
            && st["solidworks"]["readiness"]["basedOn"]
                .as_array()
                .map(|a| a.iter().any(|x| x == "StartupProcessCompleted"))
                .unwrap_or(false),
        s(&st["solidworks"])
    );
    for (verb, args) in gated_calls() {
        let r = b.call(verb, args);
        check!(
            b,
            &format!("{verb}: refuses host_app_starting with retryAfterSeconds until SOLIDWORKS is ready"),
            r["success"] == false && r["errorCode"] == "host_app_starting" && r["retryAfterSeconds"] == 5,
            s(&r)
        );
    }
    // A READ verb is gated too while the start is VERIFIED incomplete (0.9.5:
    // every verb not on STARTUP_EXEMPT waits).
    let early = b.call("swx_docs", json!({}));
    check!(b, "swx_docs: a COM read waits too while SOLIDWORKS is verifiably starting", early["errorCode"] == "host_app_starting", s(&early));

    // SOLIDWORKS FINISHES STARTING.
    std::fs::remove_file(scratch.join("mock-startup-incomplete")).unwrap();
    let again = b.call("swx_start", json!({}));
    check!(b, "swx_start: now `ready`", again["stage"] == "ready" && again["ready"] == true, s(&again));
    let docs = b.call("swx_docs", json!({}));
    check!(b, "swx_docs: ...and nothing was sent while it was starting: no document appeared", docs["count"] == 0, s(&docs));
    let made = b.call("swx_new_document", json!({"type": "part"}));
    check!(b, "swx_new_document: runs once SOLIDWORKS is ready", made["success"] == true, s(&made));
}

/// THE PERFORMANCE LOG IS A SECOND, INDEPENDENT WITNESS. COM saying "complete"
/// is not enough while the log shows the latest start's add-ins still loading;
/// the 3DEXPERIENCE marker after them is what completes it. A log that has never
/// held such a marker cannot say, and is not used.
#[test]
fn the_performance_log_is_a_second_independent_witness() {
    let scratch = TempDir::new("ready-perflog");
    session(&scratch, json!([]));
    let log = scratch.join("SolidWorksPerformance.log");
    // An earlier session finished; this one is loading add-ins.
    std::fs::write(
        &log,
        "<LOAD_ADDIN> old\n<3DXLOAD_TIME>23000</3DXLOAD_TIME>\n<LOAD_ADDIN> SolidWorks Toolbox\n<LOAD_ADDIN> 3DEXPERIENCE\n",
    )
    .unwrap();
    let logp = log.to_string_lossy().to_string();
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
    b.call("swx_start", json!({}));
    let r = b.call("swx_new_document", json!({"type": "part"}));
    check!(
        b,
        "swx_new_document: refused while the log shows add-ins still loading, though COM says complete",
        r["errorCode"] == "host_app_starting"
            && r["readiness"]["startupProcessCompleted"] == true
            && r["readiness"]["performanceLog"]["verdict"] == "loading"
            && r["readiness"]["basedOn"].as_array().map(|a| a.len() == 2).unwrap_or(false),
        s(&r)
    );
    std::fs::write(
        &log,
        format!("{}<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n", std::fs::read_to_string(&log).unwrap()),
    )
    .unwrap();
    let r = b.call("swx_new_document", json!({"type": "part"}));
    check!(b, "swx_new_document: runs once the 3DEXPERIENCE marker follows the add-ins", r["success"] == true, s(&r));

    // A SESSION THAT NEVER LOADED THE PLATFORM ADD-IN (0.9.7, F2): no marker
    // is coming. Its log has gone quiet after its add-ins, and COM says the
    // start completed: ready, and it says why.
    let scratch2 = TempDir::new("ready-perflog-none");
    session(&scratch2, json!([]));
    let log2 = scratch2.join("SolidWorksPerformance.log");
    std::fs::write(&log2, "<LOAD_ADDIN> SolidWorks Toolbox\n").unwrap();
    std::fs::File::options()
        .write(true)
        .open(&log2)
        .unwrap()
        .set_modified(std::time::SystemTime::now() - std::time::Duration::from_secs(300))
        .unwrap();
    let logp2 = log2.to_string_lossy().to_string();
    let b2 = Bridge::start_in(TempDir::borrowed(&scratch2.0), &[("SWX_PERF_LOG", &logp2)]);
    // The FIRST assessment says what it was based on; later ones say the session
    // was already found ready.
    let st = b2.call("swx_start", json!({}));
    check!(
        b2,
        "swx_start: platform add-in never loaded, log quiet, COM true => ready, basis platformAddInNotLoaded",
        st["ready"] == true
            && st["readiness"]["performanceLog"]["verdict"] == "platformAddInNotLoaded"
            && st["readiness"]["basedOn"] == json!(["StartupProcessCompleted", "performanceLog"]),
        s(&st)
    );
    let st2 = b2.call("swx_status", json!({}));
    check!(
        b2,
        "swx_status: ...and a session found ready stays ready",
        st2["solidworks"]["ready"] == true
            && st2["solidworks"]["readiness"]["basedOn"] == json!(["alreadyReadyThisSession"]),
        s(&st2["solidworks"])
    );
}

/// WITH NO SIGNAL AT ALL, a grace after first sight stands in — and says so. It
/// is not "ready" before the grace has passed.
#[test]
fn with_no_readiness_signal_the_grace_stands_in_and_says_so() {
    let scratch = TempDir::new("ready-grace");
    session(&scratch, json!([]));
    std::fs::write(scratch.join("mock-startup-unreadable"), "").unwrap();
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
    b.call("swx_start", json!({}));
    let r = b.call("swx_new_document", json!({"type": "part"}));
    check!(
        b,
        "swx_new_document: no signal and a session just seen — still starting, based on the grace",
        r["errorCode"] == "host_app_starting"
            && r["readiness"]["basedOn"] == json!(["graceAfterFirstSeen"])
            && r["readiness"]["startupProcessCompleted"].is_null(),
        s(&r)
    );
}

/// SOLIDWORKS DIES MID-SWEEP: `host_app_gone`, with what was preserved (verified)
/// and closed, and the crash markers from its performance log — never
/// `dialog_census_unknown`.
#[test]
fn a_sweep_during_which_solidworks_dies_says_host_app_gone() {
    let scratch = TempDir::new("ready-gone");
    session(
        &scratch,
        json!([
            {"docType": "part", "path": "untitled:a", "title": "Part1", "saved": false, "dirty": true},
            {"docType": "part", "path": "untitled:b", "title": "Part2", "saved": false, "dirty": true}
        ]),
    );
    let log = scratch.join("SolidWorksPerformance.log");
    std::fs::write(
        &log,
        "<SWEXH>an older session's crash</SWEXH>\n<LOAD_ADDIN> 3DEXPERIENCE\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n\
         <DOC_OPEN> \".prtdot\"\n<SWEXH>\n<UNHANDLED>\n</SWEXH>\n<RUNTIME>18</RUNTIME>\n<SWERR_REPORT>\n",
    )
    .unwrap();
    let logp = log.to_string_lossy().to_string();
    let machine = a_visible_machine("ready-gone-machine");
    let b = Bridge::start_in(
        TempDir::borrowed(&scratch.0),
        &[("SWX_LAUNCH_FAKE_DIR", &machine.path()), ("SWX_PERF_LOG", &logp)],
    );
    b.call("swx_start", json!({}));
    std::fs::write(scratch.join("mock-crash-after-first-save"), "").unwrap();
    let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "the user asked me to close SOLIDWORKS"}));
    let evidence: Vec<String> = r["crashEvidence"]
        .as_array()
        .map(|a| a.iter().filter_map(|x| x.as_str().map(|s| s.to_string())).collect())
        .unwrap_or_default();
    check!(
        b,
        "swx_close_all_safely: SOLIDWORKS died mid-sweep — host_app_gone, not dialog_census_unknown",
        r["success"] == false && r["errorCode"] == "host_app_gone" && !s(&r).contains("dialogCensusUnknown"),
        s(&r)
    );
    check!(
        b,
        "swx_close_all_safely: ...with what was preserved before it went, and what was closed",
        r["preserved"] == 1 && r["closed"] == 0,
        s(&r)
    );
    check!(
        b,
        "swx_close_all_safely: ...and this session's crash markers from the performance log, not an older session's",
        evidence.iter().any(|l| l.contains("SWERR_REPORT"))
            && evidence.iter().any(|l| l.contains("UNHANDLED"))
            && !evidence.iter().any(|l| l.contains("older session")),
        s(&r["crashEvidence"])
    );
}

/// SOLIDWORKS' `~$` LOCK FILE is not a rescue, and not a failed one.
#[test]
fn a_lock_file_in_a_recovery_folder_is_named_apart() {
    let scratch = TempDir::new("ready-lock");
    let dir = scratch.join("recovery").join("20261001-104300");
    std::fs::create_dir_all(&dir).unwrap();
    let mut body = vec![0x9f, 0xe4, 0x18, 0x9f, 0, 0, 0, 4];
    body.resize(26_129, b'x');
    std::fs::write(dir.join("Part1.SLDPRT"), &body).unwrap();
    std::fs::write(dir.join("~$Part1.SLDPRT"), b"lock").unwrap();
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
    let l = b.call("swx_recover_list", json!({}));
    let sweep = l["sweeps"][0].clone();
    check!(
        b,
        "swx_recover_list: the lock file is listed apart, as a lock file — never as a file to open",
        !sweep["files"].as_array().map(|a| a.iter().any(|f| f["name"] == "~$Part1.SLDPRT")).unwrap_or(true)
            && sweep["lockFiles"][0]["name"] == "~$Part1.SLDPRT"
            && sweep["lockFiles"][0]["lockFile"] == true
            && sweep["lockFiles"][0]["lockOf"] == "Part1.SLDPRT",
        s(&sweep)
    );
}

/// A NOTIFICATION DOES NOT STOP A SWEEP (measured 2026-10-01): the 3DEXPERIENCE
/// "Connected to …" notice — owner frame enabled, a tool window, nothing to click
/// — appeared ~18 s after a document create and stayed for minutes. Under 0.9.3
/// it blocked every quit, sweep and import in that window. Now the sweep runs and
/// the notice is reported with its evidence; it is never answered.
#[test]
fn a_notification_does_not_stop_a_sweep_and_is_reported() {
    let scratch = TempDir::new("notice-sweep");
    session(
        &scratch,
        json!([{"docType": "part", "path": "untitled:a", "title": "Part1", "saved": false, "dirty": true}]),
    );
    let machine = TempDir::new("notice-sweep-machine");
    std::fs::write(
        machine.join("windows.json"),
        json!([{"hwnd": 331370, "className": "#32770", "title": "", "visible": true,
                "owner": 4242, "ownerEnabled": true, "style": 0x9400_0044u64,
                "exStyle": 0x90080u64, "rect": [533, 45, 834, 112]}])
        .to_string(),
    )
    .unwrap();
    std::fs::write(
        machine.join("dialog-open.json"),
        json!({"hwnd": 331370, "controls": [["Static", 0, "Title", true], ["Static", 0, "Description", true],
                                            ["Static", 0, "Button1", false], ["Static", 0, "Button2", false]]})
        .to_string(),
    )
    .unwrap();
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_LAUNCH_FAKE_DIR", &machine.path())]);
    b.call("swx_start", json!({}));
    let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "the user asked me to close SOLIDWORKS"}));
    check!(
        b,
        "swx_close_all_safely: the notification does not stop the sweep",
        r["success"] == true && r["done"] == true && r["stoppedByDialog"].is_null(),
        s(&r)
    );
    check!(
        b,
        "swx_close_all_safely: ...and is reported, with its evidence, never answered",
        r["notifications"][0]["kind"] == "notification"
            && r["notifications"][0]["neverAnswer"] == true
            && r["notifications"][0]["evidence"]["window"]["toolWindow"] == true,
        s(&r["notifications"])
    );
}

/// THE SESSION'S DOCUMENT HISTORY IS IN swx_status (0.9.5), so a caller can
/// predict whether a quit will need priming: null with its basis for a session
/// the bridge did not watch from its start, true once a document has been open.
#[test]
fn swx_status_reports_the_sessions_document_history() {
    let scratch = TempDir::new("history-status");
    session(&scratch, json!([]));
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
    b.call("swx_start", json!({}));
    let st = b.call("swx_status", json!({}));
    let h = st["solidworks"]["sessionHistory"].clone();
    check!(
        b,
        "swx_status: a session the bridge attached to has an UNKNOWN history, and says why",
        h.get("everHadDocument").map(|v| v.is_null()).unwrap_or(false)
            && h["basis"].as_str().unwrap_or("").contains("did not watch"),
        s(&st["solidworks"])
    );
    let made = b.call("swx_new_document", json!({"type": "part"}));
    assert!(made["success"] == true, "precondition: a document was made: {}", s(&made));
    let st = b.call("swx_status", json!({}));
    check!(
        b,
        "swx_status: ...and once a document has been open in it, everHadDocument is true",
        st["solidworks"]["sessionHistory"]["everHadDocument"] == true,
        s(&st["solidworks"]["sessionHistory"])
    );
}

// ── 0.9.5: GATED BY DEFAULT, THE LOG BOUND TO THE SESSION (adversary pass 42) ─

/// The exemption list, name by name. A verb not here waits for SOLIDWORKS to
/// finish starting — a NEW verb included — so adding one here is a decision
/// this test makes visible. tests/wiring.rs pins the same list against the
/// source.
const EXEMPT: &[&str] = &[
    "swx_status", "swx_readiness", "swx_start", "swx_describe", "swx_progress",
    "swx_action_ledger", "swx_verb_times", "swx_uninstall", "swx_find_files",
    "swx_recover_list", "swx_window_info", "swx_dismiss_blocking_dialogs",
    "swx_close_window", "swx_screenshot", "swx_set_driving_badge", "swx_kill", "swx_user_presence", "swx_sign_in",
    "swx_selftest", "swx_com_respawn",
];

#[test]
fn every_verb_not_exempt_waits_for_startup_by_default() {
    let scratch = TempDir::new("ready-default");
    session(&scratch, json!([]));
    std::fs::write(scratch.join("mock-startup-incomplete"), "").unwrap();
    let machine = a_visible_machine("ready-default-machine");
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_LAUNCH_FAKE_DIR", &machine.path())]);
    b.call("swx_start", json!({}));
    let verbs: Vec<String> = b.call("swx_describe", json!({}))["verbs"]
        .as_array()
        .unwrap()
        .iter()
        .map(|v| v["verb"].as_str().unwrap().to_string())
        .collect();
    check!(b, "the catalog is the whole surface", verbs.len() > 60, format!("{verbs:?}"));
    let mut through = Vec::new();
    for v in verbs.iter().filter(|v| !EXEMPT.contains(&v.as_str())) {
        let r = b.call(v, json!({}));
        if r["errorCode"] != "host_app_starting" {
            through.push(format!("{v}: {}", s(&r)));
        }
    }
    check!(b, "every verb not on the exemption list is held at the gate while SOLIDWORKS starts", through.is_empty(), through.join("\n"));
    let mut held = Vec::new();
    for v in EXEMPT.iter().filter(|v| !matches!(**v, "swx_uninstall" | "swx_start")) {
        let r = b.call(v, json!({}));
        if r["errorCode"] == "host_app_starting" {
            held.push(v.to_string());
        }
    }
    check!(b, "...and no exempt verb is", held.is_empty(), format!("{held:?}"));
}

/// THE GRACE UNLOCKS READS, NEVER DOCUMENT COMMANDS. With no signal that can
/// verify the start, a read verb runs once the grace is over; a verb that
/// creates a document still refuses — "cannot verify startup completed".
#[test]
fn the_grace_lets_reads_run_and_never_a_document_command() {
    let scratch = TempDir::new("ready-grace-reads");
    session(&scratch, json!([]));
    std::fs::write(scratch.join("mock-startup-unreadable"), "").unwrap();
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_READINESS_GRACE_SECS", "0")]);
    b.call("swx_start", json!({}));
    let docs = b.call("swx_docs", json!({}));
    check!(b, "swx_docs: a READ runs once the grace is over", docs["success"] != false && docs["count"] == 0, s(&docs));
    let r = b.call("swx_new_document", json!({"type": "part"}));
    check!(
        b,
        "swx_new_document: still refused — the clock never authorises a document command",
        r["errorCode"] == "host_app_starting"
            && r["startingUnverifiable"] == true
            && r["_hint"].as_str().unwrap_or("").contains("Cannot verify startup completed")
            && r["readiness"]["state"] == "unverifiable"
            && r["readiness"]["readsAllowed"] == true,
        s(&r)
    );
    let st = b.call("swx_status", json!({}));
    check!(b, "swx_status: `ready` is false on an unverifiable session", st["solidworks"]["ready"] == false, s(&st["solidworks"]));

    // Before the grace is over, even a read waits.
    let scratch2 = TempDir::new("ready-grace-early");
    session(&scratch2, json!([]));
    std::fs::write(scratch2.join("mock-startup-unreadable"), "").unwrap();
    let b2 = Bridge::start_in(TempDir::borrowed(&scratch2.0), &[]);
    b2.call("swx_start", json!({}));
    let early = b2.call("swx_docs", json!({}));
    check!(b2, "swx_docs: inside the grace a read waits too", early["errorCode"] == "host_app_starting", s(&early));
}

fn write_log(scratch: &TempDir, text: &str, age_secs: u64) -> String {
    let log = scratch.join("SolidWorksPerformance.log");
    std::fs::write(&log, text).unwrap();
    std::fs::File::options()
        .write(true)
        .open(&log)
        .unwrap()
        .set_modified(std::time::SystemTime::now() - std::time::Duration::from_secs(age_secs))
        .unwrap();
    log.to_string_lossy().to_string()
}

/// COM ALONE IS NEVER ENOUGH WHILE THE MACHINE WRITES MARKERS. Measured
/// 2026-10-01: StartupProcessCompleted was TRUE before the marker. A log whose
/// marker predates this process cannot vouch for it — and does not hand the
/// verdict to COM either.
#[test]
fn com_alone_never_vouches_for_a_session_with_no_marker_of_its_own() {
    // The log's last write predates the (hour-old) process: an earlier session.
    let scratch = TempDir::new("ready-com-alone");
    session(&scratch, json!([]));
    let logp = write_log(&scratch, "<LOAD_ADDIN> 3DEXPERIENCE\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n", 7200);
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
    b.call("swx_start", json!({}));
    let r = b.call("swx_new_document", json!({"type": "part"}));
    check!(
        b,
        "swx_new_document: COM true + a log from before this process => refused, unverifiable",
        r["errorCode"] == "host_app_starting"
            && r["readiness"]["startupProcessCompleted"] == true
            && r["readiness"]["performanceLog"]["verdict"] == "notThisSession"
            && r["startingUnverifiable"] == true,
        s(&r)
    );

    // This session is writing the log right now, with add-ins loading and no
    // marker yet: still loading (0.9.7: no marker history needed).
    let scratch2 = TempDir::new("ready-no-marker-yet");
    session(&scratch2, json!([]));
    let logp2 = write_log(&scratch2, "<LOAD_ADDIN> SolidWorks Toolbox\n<LOAD_ADDIN> 3DEXPERIENCE\n", 0);
    let b2 = Bridge::start_in(TempDir::borrowed(&scratch2.0), &[("SWX_PERF_LOG", &logp2)]);
    b2.call("swx_start", json!({}));
    let r2 = b2.call("swx_new_document", json!({"type": "part"}));
    check!(
        b2,
        "swx_new_document: a log being written with add-ins loading and no marker yet => loading, refused",
        r2["errorCode"] == "host_app_starting" && r2["readiness"]["performanceLog"]["verdict"] == "loading",
        s(&r2)
    );
}

/// AN ON-DEMAND ADD-IN IS TOLD FROM A START BY QUIET AND BY WHICH ADD-IN. A
/// LOAD_ADDIN after the marker in a log written moments ago is a start still
/// loading; one that names the 3DEXPERIENCE add-in is a START (wedged, however
/// quiet), never an on-demand load.
#[test]
fn an_on_demand_add_in_needs_quiet_and_is_never_the_3dexperience_add_in() {
    for (tag, text, age, why) in [
        (
            "ready-ondemand-fresh",
            "<LOAD_ADDIN> 3DEXPERIENCE\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n<LOAD_ADDIN> SOLIDWORKS Simulation\n",
            0u64,
            "a LOAD_ADDIN written moments ago",
        ),
        (
            "ready-ondemand-3dx",
            "<LOAD_ADDIN> 3DEXPERIENCE\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n<LOAD_ADDIN> SolidWorks Toolbox\n\
             <LOAD_ADDIN> \"SWK3DCompassAddin.dll\" \"3DEXPERIENCE for WORKS\"\n",
            300,
            "a quiet log whose LOAD_ADDIN after the marker is the platform add-in (SWK3DCompassAddin.dll)",
        ),
        (
            "ready-header-scope",
            "<LOAD_ADDIN> old\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n; SolidWorks generated log file - do not edit.\n\
             <VERSION> 6</VERSION>\n<PROC_NUM>8</PROC_NUM>\n",
            0,
            "a marker from BEFORE the start's header (the file is rewritten at every start)",
        ),
    ] {
        let scratch = TempDir::new(tag);
        session(&scratch, json!([]));
        // A START is a young process (0.9.11: past ten minutes, a session with
        // COM true and quiet startup lines is long-running, never starting).
        created_ago(&scratch, 400);
        let logp = write_log(&scratch, text, age);
        let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
        b.call("swx_start", json!({}));
        let r = b.call("swx_new_document", json!({"type": "part"}));
        check!(
            b,
            &format!("swx_new_document: {why} is a start still loading, refused"),
            r["errorCode"] == "host_app_starting" && r["readiness"]["performanceLog"]["verdict"] == "loading",
            s(&r)
        );
    }
    // An on-demand-looking log with COM UNABLE to answer: the on-demand rule
    // needs COM's "completed" as well, so this stays unverifiable.
    let scratch = TempDir::new("ready-ondemand-nocom");
    session(&scratch, json!([]));
    std::fs::write(scratch.join("mock-startup-unreadable"), "").unwrap();
    let logp = write_log(
        &scratch,
        "<LOAD_ADDIN> 3DEXPERIENCE\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n<LOAD_ADDIN> SOLIDWORKS Simulation\n",
        120,
    );
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
    b.call("swx_start", json!({}));
    let r = b.call("swx_new_document", json!({"type": "part"}));
    check!(
        b,
        "swx_new_document: an on-demand-looking log without COM's word is not a verified start",
        r["errorCode"] == "host_app_starting" && r["startingUnverifiable"] == true,
        s(&r)
    );
    // An add-in whose NAME carries "3DEXPERIENCE" but is not the platform —
    // measured 2026-10-01: SaveFolderTo3DEXPERIENCEAddin2.DLL — loaded after the
    // marker in a quiet log is on-demand, not a start.
    let scratch = TempDir::new("ready-ondemand-savefolder");
    session(&scratch, json!([]));
    let logp = write_log(
        &scratch,
        "<LOAD_ADDIN> \"SWK3DCompassAddin.dll\" \"3DEXPERIENCE for WORKS\"\n<3DXINITERROR>0\n\
         <LOAD_ADDIN> \"SaveFolderTo3DEXPERIENCEAddin2.DLL\" \"Batch Save to 3DEXPERIENCE\"\n",
        120,
    );
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
    let st = b.call("swx_start", json!({}));
    check!(
        b,
        "swx_start: a non-platform add-in naming 3DEXPERIENCE, after the marker, quiet => on demand, ready",
        st["ready"] == true && st["readiness"]["performanceLog"]["verdict"] == "onDemandAddInAfterComplete",
        s(&st)
    );
    // ...and the on-demand case itself, with the rule's basis in the answer.
    let scratch = TempDir::new("ready-ondemand-ok");
    session(&scratch, json!([]));
    let logp = write_log(
        &scratch,
        "<LOAD_ADDIN> 3DEXPERIENCE\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n<LOAD_ADDIN> SOLIDWORKS Simulation\n",
        120,
    );
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
    let st = b.call("swx_start", json!({}));
    check!(
        b,
        "swx_start: COM true + marker + a quiet non-3DEXPERIENCE LOAD_ADDIN => ready, saying why",
        st["ready"] == true
            && st["readiness"]["performanceLog"]["verdict"] == "onDemandAddInAfterComplete"
            && st["readiness"]["performanceLog"]["why"].as_str().unwrap_or("").contains("on demand"),
        s(&st)
    );
}

/// A DOCUMENT THAT PERSISTS ACROSS TWO POLLS IS RECORDED (0.9.6). The user's
/// own document, open when the bridge first looks, never passed through a verb
/// that "made one": the readiness polls record it — once the count has been
/// > 0 on two distinct polls of the STARTED session, never from one read.
#[test]
fn a_document_that_persists_across_two_polls_marks_the_session() {
    let scratch = TempDir::new("ready-poll-marks");
    session(
        &scratch,
        json!([{"docType": "part", "path": "C:\\Users\\drew\\work\\bracket.SLDPRT",
                "title": "bracket.SLDPRT", "saved": true, "dirty": false}]),
    );
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
    b.call("swx_start", json!({}));
    let first = b.call("swx_status", json!({}));
    let h1 = first["solidworks"]["sessionHistory"].clone();
    check!(
        b,
        "swx_status: what the poll saw is recorded, with its count",
        h1["observations"].as_array().map(|a| a.iter().any(|o| o["count"] == 1)).unwrap_or(false),
        s(&h1)
    );
    std::thread::sleep(std::time::Duration::from_millis(1100));
    let st = b.call("swx_status", json!({}));
    check!(
        b,
        "swx_status: the count persisted across two polls — the session has held a document",
        st["solidworks"]["sessionHistory"]["everHadDocument"] == true
            && st["solidworks"]["sessionHistory"]["advisory"] == true,
        s(&st["solidworks"])
    );
}

/// EVERY GATED VERB SAYS WHAT THE GATE SAW (0.9.6, G3) — on success as well as
/// on refusal — so a live run can prove no COM verb ran while starting. Including
/// the arms that return straight from the handler table (swx_show_*, exports).
#[test]
fn every_gated_reply_carries_the_readiness_the_gate_evaluated() {
    let scratch = TempDir::new("ready-at-gate");
    session(&scratch, json!([]));
    std::fs::write(scratch.join("mock-startup-incomplete"), "").unwrap();
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
    b.call("swx_start", json!({}));
    let refused = b.call("swx_docs", json!({}));
    check!(
        b,
        "refusal: readiness.ready false, with basedOn",
        refused["errorCode"] == "host_app_starting"
            && refused["readiness"]["ready"] == false
            && refused["readiness"]["basedOn"].as_array().map(|a| !a.is_empty()).unwrap_or(false),
        s(&refused)
    );
    std::fs::remove_file(scratch.join("mock-startup-incomplete")).unwrap();
    for (verb, args) in [
        ("swx_docs", json!({})),
        ("swx_new_document", json!({"type": "part"})),
        ("swx_show_part", json!({"path": "C:\\Users\\drew\\work\\nothing-here.SLDPRT"})),
        ("swx_get_app_state", json!({})),
    ] {
        let r = b.call(verb, args);
        check!(
            b,
            &format!("{verb}: the reply carries readiness as evaluated at the gate"),
            r["readiness"]["evaluatedAt"] == "gate"
                && r["readiness"]["ready"] == true
                && r["readiness"]["basedOn"].as_array().map(|a| !a.is_empty()).unwrap_or(false),
            s(&r)
        );
    }
    let free = b.call("swx_find_files", json!({"root": "/nonexistent"}));
    check!(b, "an exempt verb is not gated and says nothing of a gate", free["readiness"].is_null(), s(&free));
}

/// THE TWO-POLL RULE, both edges (0.9.6). Two counts > 0 read within a second
/// are one blip, not persistence; and a zero between two counts breaks the run.
#[test]
fn a_blip_or_an_interrupted_count_never_records_a_document() {
    let scratch = TempDir::new("ready-blip");
    session(&scratch, json!([]));
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
    b.call("swx_start", json!({}));
    // A blip: a count of 1 on two reads close together, then 0.
    std::fs::write(scratch.join("mock-phantom-doc-once"), "2").unwrap();
    b.call("swx_docs", json!({}));
    b.call("swx_docs", json!({}));
    std::thread::sleep(std::time::Duration::from_millis(1100));
    let st = b.call("swx_status", json!({}));
    check!(
        b,
        "two counts > 0 within a second, then zero: not a document",
        st["solidworks"]["sessionHistory"]["everHadDocument"] != true,
        s(&st["solidworks"]["sessionHistory"])
    );
    // Interrupted: 1 ... 0 ... (a second later) 1.
    std::fs::write(scratch.join("mock-phantom-doc-once"), "1").unwrap();
    std::fs::remove_file(scratch.join("mock-phantom-doc-once.used")).unwrap();
    b.call("swx_docs", json!({}));
    b.call("swx_docs", json!({}));
    std::thread::sleep(std::time::Duration::from_millis(1100));
    std::fs::remove_file(scratch.join("mock-phantom-doc-once.used")).unwrap();
    let st = b.call("swx_status", json!({}));
    check!(
        b,
        "a count, a zero, a count a second later: the zero breaks the run — not a document",
        st["solidworks"]["sessionHistory"]["everHadDocument"] != true,
        s(&st["solidworks"]["sessionHistory"])
    );
}

/// THE PLATFORM ADD-IN LOADED AND ITS MARKER NEVER CAME: held, however quiet
/// the log (a dialog, a sign-in) — and the log's SHAPE is recorded: a file that
/// begins with the start header is rewritten per start; two headers is an
/// appended log, of which only the last section is read (0.9.7, F2/F7).
#[test]
fn a_platform_load_without_its_marker_is_held_and_the_logs_shape_is_recorded() {
    const COMPASS: &str = r#"<AD2> "LOAD_ADDIN" "SWK3DCompassAddin.dll" "3DEXPERIENCE for WORKS" [2.18] "6.34.0.2027" 14 COM.new 1770885081 13"#;
    let scratch = TempDir::new("ready-platform-no-marker");
    session(&scratch, json!([]));
    created_ago(&scratch, 400); // a start: a young process (0.9.11), its log written since
    let logp = write_log(
        &scratch,
        &format!("; SolidWorks generated log file - do not edit.\n<VERSION> 6</VERSION>\n{COMPASS}\n"),
        300,
    );
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
    b.call("swx_start", json!({}));
    let r = b.call("swx_new_document", json!({"type": "part"}));
    check!(
        b,
        "swx_new_document: the platform add-in loaded, no marker, quiet => loading, refused",
        r["errorCode"] == "host_app_starting"
            && r["readiness"]["performanceLog"]["verdict"] == "loading"
            && r["readiness"]["performanceLog"]["shape"]["model"] == "rewrittenPerStart"
            && r["readiness"]["performanceLog"]["lengthAtFirstSight"].is_u64(),
        s(&r)
    );

    // No platform add-in, a quiet log — but COM cannot answer: that verdict
    // needs COM's "completed" too, so the session is unverifiable.
    let scratch = TempDir::new("ready-no-platform-no-com");
    session(&scratch, json!([]));
    std::fs::write(scratch.join("mock-startup-unreadable"), "").unwrap();
    let logp = write_log(&scratch, "<LOAD_ADDIN> \"SWToolbox.dll\"\n", 300);
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
    b.call("swx_start", json!({}));
    let r = b.call("swx_new_document", json!({"type": "part"}));
    check!(
        b,
        "platformAddInNotLoaded without COM's word is not a verified start",
        r["errorCode"] == "host_app_starting"
            && r["startingUnverifiable"] == true
            && r["readiness"]["performanceLog"]["verdict"] == "platformAddInNotLoaded",
        s(&r)
    );

    let scratch = TempDir::new("ready-appended");
    session(&scratch, json!([]));
    let logp = write_log(
        &scratch,
        "; SolidWorks generated log file - do not edit.\n<LOAD_ADDIN> old\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n\
         ; SolidWorks generated log file - do not edit.\n<VERSION> 6</VERSION>\n<LOAD_ADDIN> \"SWToolbox.dll\"\n",
        0,
    );
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
    b.call("swx_start", json!({}));
    let r = b.call("swx_new_document", json!({"type": "part"}));
    check!(
        b,
        "an APPENDED log: the earlier start's marker is not read; this start is loading",
        r["errorCode"] == "host_app_starting"
            && r["readiness"]["performanceLog"]["verdict"] == "loading"
            && r["readiness"]["performanceLog"]["shape"]["model"] == "appended",
        s(&r)
    );
}

/// MODALITY IS A PERSISTENT MEASUREMENT, NOT ONE READ (0.9.8). MEASURED
/// 2026-10-01 on 0.9.7: the 3DEXPERIENCE Connected notice — hwnd 4329196,
/// `#32770`, style 0x94000044, exstyle 0x90080, 301x67, nine Static controls
/// (3 visible, 6 hidden) reading the template placeholders — was read with its
/// owner frame DISABLED while SOLIDWORKS was busy inside the sweep's own close,
/// and stopped `swx_close_all_safely` as a modal; seconds later the frame was
/// enabled again. The census now re-reads the owner when it is the ONLY fact
/// that blocks: disabled then enabled -> a notification, reported with the read
/// history; disabled on every read -> still a modal. And a window with a
/// Button-class control is never settled at all.
fn sweep_with_notice(tag: &str, owner_reads: Value, extra_controls: Value) -> (Bridge, Value, TempDir, TempDir) {
    let scratch = TempDir::new(tag);
    session(
        &scratch,
        json!([{"docType": "part", "path": "untitled:a", "title": "Part1", "saved": false, "dirty": true}]),
    );
    let machine = TempDir::new(&format!("{tag}-machine"));
    std::fs::write(
        machine.join("windows.json"),
        json!([{"hwnd": 4329196, "className": "#32770", "title": "", "visible": true,
                "owner": 856844, "ownerEnabled": false, "ownerEnabledReads": owner_reads,
                "style": 0x9400_0044u64, "exStyle": 0x90080u64, "rect": [533, 123, 834, 190]}])
        .to_string(),
    )
    .unwrap();
    let mut controls = vec![
        json!(["Static", 0, "Title", true]), json!(["Static", 0, "Description", true]), json!(["Static", 0, "", true]),
        json!(["Static", 0, "Button1", false]), json!(["Static", 0, "Button2", false]),
        json!(["Static", 0, "Hyperlink Text 1", false]), json!(["Static", 0, "Hyperlink Text 2", false]),
        json!(["Static", 0, "", false]), json!(["Static", 0, "", false]),
    ];
    controls.extend(extra_controls.as_array().cloned().unwrap_or_default());
    std::fs::write(
        machine.join("dialog-open.json"),
        json!({"hwnd": 4329196, "controls": controls}).to_string(),
    )
    .unwrap();
    let b = Bridge::start_in(
        TempDir::borrowed(&scratch.0),
        &[("SWX_LAUNCH_FAKE_DIR", &machine.path()), ("SWX_OWNER_SETTLE_MS", "30")],
    );
    b.call("swx_start", json!({}));
    let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "the user asked me to close SOLIDWORKS"}));
    (b, r, scratch, machine)
}

#[test]
fn an_owner_disabled_for_an_instant_does_not_make_a_notice_a_modal() {
    let (b, r, _s, _m) = sweep_with_notice("settle-transient", json!([false, false, true]), json!([]));
    check!(
        b,
        "swx_close_all_safely: the notice whose owner was busy for an instant does not stop the sweep",
        r["success"] == true && r["done"] == true && r["stoppedByDialog"].is_null(),
        s(&r)
    );
    let n = &r["notifications"][0];
    check!(
        b,
        "...and it is reported as a notification, ownerDisabledTransiently, with the read history",
        n["kind"] == "notification"
            && n["ownerDisabledTransiently"] == true
            && n["ownerReads"].as_array().map(|a| a.len() >= 2 && a[0]["enabled"] == false
                && a.last().map(|l| l["enabled"] == true).unwrap_or(false)).unwrap_or(false),
        s(&r["notifications"])
    );
}

#[test]
fn an_owner_disabled_on_every_read_is_still_a_modal() {
    let (b, r, _s, _m) = sweep_with_notice("settle-persistent", json!([false]), json!([]));
    check!(
        b,
        "swx_close_all_safely: disabled on every re-read => stopped_by_dialog, as before",
        r["errorCode"] == "stopped_by_dialog"
            && r["stoppedByDialog"]["ownerDisabledTransiently"] == false
            && r["stoppedByDialog"]["ownerReads"].as_array().map(|a| a.len() == 6).unwrap_or(false),
        s(&r)
    );
}

#[test]
fn a_window_with_a_button_is_never_settled() {
    // Same evidence, plus a Button-class control: something is on offer. The
    // owner turning enabled later does not make it a notification.
    let (b, r, _s, m) =
        sweep_with_notice("settle-button", json!([false, true]), json!([["Button", 0x50010000u64, "OK", true]]));
    check!(
        b,
        "swx_close_all_safely: a window with a Button is a modal whatever its owner later reads",
        r["errorCode"] == "stopped_by_dialog" && r["stoppedByDialog"]["ownerReads"].is_null(),
        s(&r)
    );
    check!(b, "...and its owner was never re-read", !m.join("owner-reads-4329196.cursor").exists(), String::new());
}

/// A CONFIGURED LOG PATH THAT IS MISSING is unreadable, not absent (0.9.9,
/// adversary pass 45 V1): COM alone does not decide, and the refusal says why.
#[test]
fn a_missing_configured_log_is_unverifiable_not_absent() {
    let scratch = TempDir::new("ready-log-missing");
    session(&scratch, json!([]));
    let missing = scratch.join("no-such-dir").join("SolidWorksPerformance.log");
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &missing.to_string_lossy())]);
    b.call("swx_start", json!({}));
    let r = b.call("swx_new_document", json!({"type": "part"}));
    check!(
        b,
        "swx_new_document: refused, unverifiable, saying the configured log is missing",
        r["errorCode"] == "host_app_starting"
            && r["startingUnverifiable"] == true
            && r["readiness"]["performanceLog"]["verdict"] == "unreadable"
            && r["error"].as_str().unwrap_or("").contains("performance log path is missing"),
        s(&r)
    );
}

// ── 0.9.11: MEASURED 2026-10-01 — a three-hour-old session locked out ──────

/// The performance log of Drew's session pid 26924 (started 14:59:33, 34
/// documents open, nearly three hours old), as measured — lines 93–101 add-in
/// loads (100 = the platform add-in), 106/107 the markers, 114–120 add-in
/// UNLOADS (119 = the platform add-in), 124 an on-demand load, then periodic
/// `<PROCMEM> CMD:AppRunTime` lines. 0.9.10 read `"UNLOAD_ADDIN"` as containing
/// `LOAD_ADDIN`, called line 119 the platform add-in LOADING after the marker —
/// "a start" — and refused every gated verb on this session.
fn drews_log() -> String {
    let mut l = vec![
        "; SolidWorks generated log file - do not edit.".to_string(),
        "<VERSION> 6</VERSION>".into(),
        "<PROC_NUM>16</PROC_NUM>".into(),
        "<PROC_ARCH>9</PROC_ARCH>".into(),
        "<OS>10.0.26100</OS>".into(),
    ];
    for (dll, name) in [
        ("BendSequenceSwu.dll", "Bend Sequence"),
        ("SldAuraEx.DLL", "SOLIDWORKS Aura"),
        ("SWToolbox.dll", "SOLIDWORKS Toolbox Library"),
        ("SaveFolderTo3DEXPERIENCEAddin2.DLL", "Batch Save to 3DEXPERIENCE"),
        ("TaskPaneCacheExplorerAddin.DLL", "3DEXPERIENCE files on This PC"),
        ("SWK3DCompassAddin.dll", "3DEXPERIENCE for WORKS"),
    ] {
        l.push(format!(r#"<AD2> "LOAD_ADDIN" "{dll}" "{name}" [2.18] "6.34.0.2027" 14 COM.new 1770885081 13"#));
    }
    l.push("<3DXINITERROR>0".into());
    l.push("<3DXLOAD_TIME>19000</3DXLOAD_TIME>".into());
    l.push(r#"<AD2> "UNLOAD_ADDIN" "BendSequenceSwu.dll" "" [0.00] 0"#.into());
    l.push(r#"<AD2> "UNLOAD_ADDIN" "SWK3DCompassAddin.dll" "3DEXPERIENCE for WORKS" [0.50] 13"#.into());
    l.push(r#"<AD2> "UNLOAD_ADDIN" "SldAuraEx.DLL" "" [0.00] 0"#.into());
    l.push(r#"<AD2> "LOAD_ADDIN" "sldfuncfeat.dll" "sldFuncFeat" [0.16] "34.0" 14 COM.new 1770885300 13"#.into());
    for _ in 0..4 {
        l.push("<PROCMEM> CMD:AppRunTime 1832.5 12873 MB".into());
    }
    l.join("\n") + "\n"
}

fn created_ago(scratch: &TempDir, secs: u64) {
    let now = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs();
    std::fs::write(scratch.join("mock-sldworks-created"), (now - secs).to_string()).unwrap();
}

#[test]
fn an_unload_is_never_read_as_a_load_and_the_measured_log_is_ready() {
    let scratch = TempDir::new("ready-drews-log");
    session(&scratch, json!([]));
    created_ago(&scratch, 3 * 3600);
    let logp = write_log(&scratch, &drews_log(), 900);
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
    let st = b.call("swx_start", json!({}));
    check!(
        b,
        "swx_start on the measured log: ready, BY THE LOG (an on-demand add-in after the marker) — \
         the platform add-in's UNLOAD is not a load",
        st["ready"] == true
            && st["readiness"]["performanceLog"]["verdict"] == "onDemandAddInAfterComplete"
            && !st["readiness"]["basedOn"].to_string().contains("longRunningSession"),
        s(&st["readiness"])
    );
}

/// A SESSION CANNOT BE STARTING FOR HOURS (L2): the platform add-in loaded after
/// the marker (the shape of a start), COM says complete, and no startup line has
/// changed for five minutes — on a process three hours old that is ready
/// (`longRunningSession`); on one two minutes old it still holds.
#[test]
fn a_long_running_session_is_never_locked_out_by_a_log_rule() {
    let text = "<LOAD_ADDIN> \"SWK3DCompassAddin.dll\"\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n\
                <AD2> \"LOAD_ADDIN\" \"SWK3DCompassAddin.dll\" \"3DEXPERIENCE for WORKS\"\n";
    let scratch = TempDir::new("ready-long-running");
    session(&scratch, json!([]));
    created_ago(&scratch, 3 * 3600);
    let logp = write_log(&scratch, text, 300);
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
    let st = b.call("swx_start", json!({}));
    check!(
        b,
        "a three-hour-old session, COM true, startup lines quiet 300 s: ready, basis longRunningSession",
        st["ready"] == true && st["readiness"]["basedOn"].to_string().contains("longRunningSession"),
        s(&st["readiness"])
    );

    let scratch = TempDir::new("ready-young-holds");
    session(&scratch, json!([]));
    created_ago(&scratch, 120);
    let logp = write_log(&scratch, text, 100);
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
    let r = b.call("swx_new_document", json!({"type": "part"}));
    check!(
        b,
        "the same log on a two-minute-old process: still loading, refused",
        r["errorCode"] == "host_app_starting" && r["readiness"]["performanceLog"]["verdict"] == "loading",
        s(&r)
    );
}

/// PERIODIC RUN-TIME LINES ARE NOT STARTUP ACTIVITY (L3): an active session
/// keeps writing `<PROCMEM> CMD:AppRunTime`, so the FILE is never quiet. The
/// quiet window counts only add-in loads and markers.
#[test]
fn periodic_runtime_lines_do_not_keep_a_session_starting() {
    let scratch = TempDir::new("ready-procmem");
    session(&scratch, json!([]));
    created_ago(&scratch, 300);
    let base = "<LOAD_ADDIN> \"SWK3DCompassAddin.dll\"\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n\
                <AD2> \"LOAD_ADDIN\" \"SWToolbox.dll\" \"SOLIDWORKS Toolbox Library\"\n\
                <PROCMEM> CMD:AppRunTime 300.1 9000 MB\n";
    let logp = write_log(&scratch, base, 0);
    let b = Bridge::start_in(
        TempDir::borrowed(&scratch.0),
        &[("SWX_PERF_LOG", &logp), ("SWX_STARTUP_QUIET_SECS", "2")],
    );
    let first = b.call("swx_start", json!({}));
    check!(b, "first look: the startup lines are new to the bridge — held", first["ready"] == false, s(&first["readiness"]));
    std::thread::sleep(std::time::Duration::from_millis(2500));
    // The session keeps working: another run-time line, written NOW.
    write_log(&scratch, &format!("{base}<PROCMEM> CMD:AppRunTime 302.6 9001 MB\n"), 0);
    let st = b.call("swx_status", json!({}));
    check!(
        b,
        "a fresh PROCMEM line does not reset the quiet window: an on-demand add-in, ready",
        st["solidworks"]["ready"] == true
            && st["solidworks"]["readiness"]["performanceLog"]["verdict"] == "onDemandAddInAfterComplete",
        s(&st["solidworks"]["readiness"])
    );
}

/// THE LATCH SURVIVES A BRIDGE RESPAWN (L5): every update respawns the bridge;
/// a session already seen ready stays ready under the new one, whatever its
/// log looks like now.
#[test]
fn a_session_seen_ready_stays_ready_across_a_bridge_restart() {
    let scratch = TempDir::new("ready-latch-persist");
    session(&scratch, json!([]));
    created_ago(&scratch, 300);
    let logp = write_log(&scratch, "<LOAD_ADDIN> x\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n", 0);
    {
        let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
        let st = b.call("swx_start", json!({}));
        check!(b, "precondition: ready under the first bridge", st["ready"] == true, s(&st["readiness"]));
    }
    // The update respawns the bridge; the log now looks like loading.
    write_log(&scratch, "<LOAD_ADDIN> x\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n<LOAD_ADDIN> \"SWK3DCompassAddin.dll\"\n", 0);
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
    let st = b.call("swx_start", json!({}));
    check!(
        b,
        "under the respawned bridge: still ready, because this session was",
        st["ready"] == true && st["readiness"]["basedOn"].to_string().contains("readyBeforeThisBridgeRestarted"),
        s(&st["readiness"])
    );
}

/// swx_user_presence IS EXEMPT (L4) and works while SOLIDWORKS is starting:
/// foreground and last input are measured, the document part is unknown, and
/// unknown counts as present.
#[test]
fn user_presence_answers_during_startup_with_documents_unknown() {
    let scratch = TempDir::new("ready-presence-starting");
    session(&scratch, json!([]));
    std::fs::write(scratch.join("mock-startup-incomplete"), "").unwrap();
    let machine = a_visible_machine("ready-presence-starting-m");
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_LAUNCH_FAKE_DIR", &machine.path())]);
    b.call("swx_start", json!({}));
    b.call("swx_status", json!({}));
    let p = b.call("swx_user_presence", json!({}));
    check!(
        b,
        "swx_user_presence during startup: answered, present, documents unknown, foreground measured",
        p["success"] == true
            && p["present"] == true
            && p["evidence"]["documentsUnknown"] == true
            && p["evidence"]["foregroundWindow"].is_object(),
        s(&p)
    );
}