app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
Drew Owens
0.9.7 source: adversary pass 44 (no marker history, carve-out pinned, internal args stripped, one dialog classifier, lo…
8b1ae9e
20d ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
//! ADVERSARIAL REVIEW OF 0.9.5 — pass forty-four.
//!
//! readiness.rs, perflog.rs, quit.rs (judge_window) and the dispatch gate
//! compile here and were exercised. com.rs (the real StartupProcessCompleted)
//! and LiveOs are cfg(windows): READ, NOT RUN.
//!
//! The log lines below use the format the coordinator measured on Drew's
//! machine, verbatim:
//! <AD2> "LOAD_ADDIN" "SWK3DCompassAddin.dll" "3DEXPERIENCE for WORKS" [2.18] "6.34.0.2027" 14 COM.new 1770885081 13
//! <AD2> "LOAD_ADDIN" "TaskPaneCacheExplorerAddin.DLL" "3DEXPERIENCE files on This PC" …
//!
//! WHAT CHECKS OUT
//! * (R4) The exempt verbs: window_info / dismiss_blocking_dialogs /
//! close_window / screenshot / set_driving_badge make no com-host call;
//! swx_kill reads `app_state` (a count, not a document command), as its
//! exemption says. dismiss_blocking_dialogs refuses anything with
//! neverAutoAnswer (defaulting TRUE when absent), then needs the
//! process-level write opt-in, then ownerConfirmed. close_window needs
//! confirm + reason. One residual: dismiss classifies by TITLE only
//! (`abwin::classify`), where judge_window now reads the body — a window
//! titled "…Warning…" / "…Rebuild…" (kind `message`, answerable) whose body
//! is a save question is closable by dismiss under the opt-in. A window
//! close is normally Cancel, not Discard; sev-3, the two classifiers should
//! be one.
//! * (R5) swx_stop's startup carve-out only skips COM rungs a/b/e; the
//! preconditions (P2 zero unsaved, no blocking dialog) run at verbs.rs:1189
//! before the ladder for every quit. Note `_startupNotVerified` is an
//! ordinary argument a caller can pass on a READY session; it only skips the
//! gentle COM rungs. Strip it from caller args.
//! * (R6) Captions count only as whole-control matches and it takes TWO, so
//! the word "OK" inside a sentence, or a lone "Close", does not block; a
//! notice with separate "OK" and "Close" controls would (safe direction).
//! Non-English captions match nothing and fall through to the modality
//! evidence — a localised custom-drawn save prompt would again read as a
//! notification. Drew's install is English.
//! * (R1) The code ASSUMES the latest marker belongs to the latest start
//! (position, no timestamps) and binds only by file mtime >= process start.
//! Under the REWRITTEN-per-session behaviour you measured (1271 -> 107 lines)
//! that holds. Under an APPENDED log it does not: a new session's first
//! non-LOAD_ADDIN line refreshes the mtime and the previous session's marker
//! reads `complete`. Nothing in the code detects which model the file
//! follows; recording the file length when a session is first seen (a
//! rewrite shrinks it) would. Not tested — not reachable on the measured
//! behaviour.
mod common;
use common::*;
use serde_json::{json, Value};
fn s(v: &Value) -> String {
serde_json::to_string_pretty(v).unwrap_or_default()
}
fn session(scratch: &TempDir) {
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({"running": true, "untitled": 1, "active": Value::Null, "docs": [], "files": {}}).to_string(),
)
.unwrap();
}
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()
}
const COMPASS: &str = r#"<AD2> "LOAD_ADDIN" "SWK3DCompassAddin.dll" "3DEXPERIENCE for WORKS" [2.18] "6.34.0.2027" 14 COM.new 1770885081 13"#;
const TASKPANE: &str = r#"<AD2> "LOAD_ADDIN" "TaskPaneCacheExplorerAddin.DLL" "3DEXPERIENCE files on This PC" [1.0] "6.34.0.2027" 14 COM.new 1770885090 13"#;
const TOOLBOX: &str = r#"<AD2> "LOAD_ADDIN" "SWToolbox.dll" "SOLIDWORKS Toolbox Library" [1.0] "34.0" 14 COM.new 1770885070 13"#;
const MARKER: &str = "<3DXLOAD_TIME>24000</3DXLOAD_TIME>";
/// SEVERITY 2 — "NAMES 3DEXPERIENCE" MATCHES THE WRONG ADD-IN.
///
/// The on-demand rule refuses to call a post-marker LOAD_ADDIN "on demand" if
/// the line contains "3DEXPERIENCE", reasoning that the 3DEXPERIENCE add-in is
/// what writes the marker, so loading it again means a start. Two add-ins on
/// Drew's machine contain that word. The one that writes the marker is
/// `SWK3DCompassAddin.dll` ("3DEXPERIENCE for WORKS"). The other,
/// `TaskPaneCacheExplorerAddin.DLL` ("3DEXPERIENCE files on This PC"), is a task-
/// pane browser — exactly the kind of add-in that loads on demand. When it does,
/// after the marker, the session reads `loading` however quiet the log is, COM's
/// "completed" is out-voted, and every COM verb is refused for the rest of the
/// session: the pass-42 never-ready bug, back for this one add-in. (And if this
/// add-in happens to load AFTER the marker during an ordinary start — the order
/// was not measured — no session on that machine would ever be ready.)
///
/// Key the rule on the DLL that writes the marker (`SWK3DCompassAddin.dll`), not
/// on a word in the description.
#[test]
fn the_3dexperience_file_browser_loaded_on_demand_is_not_a_start() {
let scratch = TempDir::new("adv42-taskpane");
session(&scratch);
let text = format!("{TOOLBOX}\n{COMPASS}\n{MARKER}\n{TASKPANE}\n");
let logp = write_log(&scratch, &text, 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"}));
assert!(
r["readiness"]["startupProcessCompleted"] == json!(true) || r["success"] == json!(true),
"precondition: COM says the start completed: {}", s(&r)
);
assert!(
r["errorCode"] != json!("host_app_starting"),
"COM says the start completed, the marker was written by SWK3DCompassAddin.dll, and five \
quiet minutes later the only post-marker LOAD_ADDIN is TaskPaneCacheExplorerAddin.DLL \
(\"3DEXPERIENCE files on This PC\") — a file browser loaded on demand. Because its \
description contains the word 3DEXPERIENCE, the session is \"loading\" for good and every \
COM verb is refused. Key on the DLL that writes the marker. {}",
s(&r)
);
}
/// SEVERITY 2 — A USER WHO DISABLES THE 3DEXPERIENCE ADD-IN CAN NEVER USE THE
/// BRIDGE AGAIN.
///
/// Once `perflog-markers-seen` exists, a session whose log holds no marker reads
/// `loading` ("none is in the log since this session began") — forever, since
/// the add-in that writes it never loads. Disabling that add-in is a common thing
/// to do (it is the slow one: 24 s of the measured start). Every COM verb is then
/// refused for every session, `readinessTimedOut` arrives at five minutes, and
/// nothing promotes it. The flag is permanent in the data root.
///
/// A start that did not load SWK3DCompassAddin.dll has no marker to wait for. If
/// the log shows this session's add-ins loaded, no 3DEXPERIENCE start in it, and
/// has gone quiet — and COM says complete — that is a finished start.
#[test]
fn a_session_without_the_3dexperience_add_in_is_not_starting_forever() {
let scratch = TempDir::new("adv42-disabled");
session(&scratch);
std::fs::write(scratch.join("perflog-markers-seen"), "1").unwrap();
let logp = write_log(&scratch, &format!("{TOOLBOX}\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"}));
assert!(
r["errorCode"] != json!("host_app_starting"),
"the 3DEXPERIENCE add-in is disabled, so this session never loads it and never writes a \
marker. COM says the start completed, the log shows only Toolbox loading and has been \
quiet for five minutes — and the session is \"loading\" forever because this machine has \
EVER seen a marker. Every COM verb refused for every session from now on. {}",
s(&r)
);
}
/// SEVERITY 2 — THE FIRST START AFTER INSTALLING 0.9.5 IS THE CRASH WINDOW.
///
/// With no `perflog-markers-seen` flag yet, a log with no marker is `unavailable`
/// and COM decides alone — and COM was MEASURED true before the marker on a real
/// start. The flag is new in 0.9.5, and under the rewritten-per-session log the
/// previous session's markers are gone by the time a new start is assessed. So
/// on every machine, the first launch under 0.9.5 that is asked for readiness
/// while loading has COM alone deciding: ready, during add-in load — the
/// 2026-10-01 crash. (The rescue held that day; the crash is not data loss, which
/// is why this is sev-2.)
///
/// A log that THIS session is actively writing with LOAD_ADDIN lines is evidence
/// of loading on its own, marker history or not. Treat it as `loading` until it
/// goes quiet or a marker arrives, with a timeout that says so.
#[test]
fn a_log_being_written_with_add_ins_loading_holds_the_first_start() {
let scratch = TempDir::new("adv42-firststart");
session(&scratch);
assert!(!scratch.join("perflog-markers-seen").exists(), "precondition: no marker ever seen here");
let logp = write_log(&scratch, &format!("{TOOLBOX}\n{COMPASS}\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"}));
assert!(
r["errorCode"] == json!("host_app_starting"),
"the log was written this second, its last lines are LOAD_ADDIN — the 3DEXPERIENCE add-in \
among them — and no marker yet: a start in progress. Because this machine has never had a \
marker RECORDED (the flag is new in 0.9.5), the log is ignored and COM — measured true \
BEFORE the marker — decides alone: swx_new_document ran. The first launch under 0.9.5 on \
every machine is the 2026-10-01 crash window. {}",
s(&r)
);
}