app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
//! MEMORY (0.10.5) — the commit charge, SOLIDWORKS's share of it, and who else
//! holds it.
//!
//! MEASURED 2026-10-14 on adom_drew2 (31.4 GB RAM, an automatic 46 GB page
//! file): the SOLIDWORKS Resource Monitor warned "Available committed memory is
//! critically low" at 72.3 of 76.6 GB committed (94 %), 4.2 GB available. The
//! holders were Fusion 360 (20.7 GB, the user's, two days old), the
//! bridge-started SOLIDWORKS (10.6 GB with ZERO documents open, after about a
//! hundred scratch parts were built and closed in it — SOLIDWORKS does not give
//! memory back when a document closes), Chrome (10.2 GB over 51 processes) and
//! WSL (4.7 GB). Quitting that SOLIDWORKS (swx_stop, rung a, 14 s) returned about
//! 16 GB of commit.
//!
//! So this module:
//! * REPORTS — `swx_status.memory`, and a sentence naming the largest holders
//! in every reply's `_hint` while pressure is not ok;
//! * GUARDS — verbs that create or load geometry are refused while commit is
//! critical (`low_commit_memory`), changing nothing, with the remedy that is
//! actually safe;
//! * lets LONG RUNS RECYCLE a session the bridge started (the self-test) —
//! never one the user is working in, and never any other application.
//!
//! Everything about other processes is READ-ONLY. Nothing here acts on any
//! process; the only action anywhere in this file's orbit is `swx_stop` on the
//! bridge's own, empty SOLIDWORKS session, and that verb keeps every one of its
//! own refusals.
use serde_json::{json, Value};
use crate::launchos::{LaunchOs, ProcPrivate, SystemMemory};
/// SOLIDWORKS's own committed memory above which a long run recycles a session
/// the bridge started (6 GB: a sensible budget on a 32 GB PC).
pub const DEFAULT_BUDGET_MB: u64 = 6144;
pub const ENV_BUDGET: &str = "SWX_SW_MEMORY_BUDGET_MB";
/// CRITICAL: commit available under 8 % of the limit, or under 4 GB.
pub const CRITICAL_FRACTION: f64 = 0.08;
pub const CRITICAL_FLOOR_MB: u64 = 4096;
/// HIGH: commit available under 15 % of the limit (or SOLIDWORKS over budget).
pub const HIGH_FRACTION: f64 = 0.15;
/// How many holders a report names.
pub const TOP_N: usize = 5;
/// A recycle is worth its minutes only when SOLIDWORKS's own share is at least
/// this much (or over budget); below it the memory is held elsewhere and a
/// restart would not relieve anything.
pub const RECYCLE_WORTH_MB: u64 = 2048;
pub const READ_ONLY_NOTE: &str = "Facts, read-only. The bridge never closes, stops or kills a \
process that is not SOLIDWORKS, whatever it holds — report the largest holders to the user \
and let them decide. The only memory the bridge returns itself is a SOLIDWORKS session it \
started, with no documents open (swx_stop, then swx_start).";
/// The session budget: `SWX_SW_MEMORY_BUDGET_MB`, else 6144.
pub fn budget() -> (u64, &'static str) {
match std::env::var(ENV_BUDGET).ok().and_then(|v| v.trim().parse::<u64>().ok()).filter(|v| *v >= 256) {
Some(v) => (v, ENV_BUDGET),
None => (DEFAULT_BUDGET_MB, "default"),
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Pressure {
Ok,
High,
Critical,
/// The commit charge could not be read. Never reported as "ok".
Unknown,
}
impl Pressure {
pub fn name(self) -> &'static str {
match self {
Pressure::Ok => "ok",
Pressure::High => "high",
Pressure::Critical => "critical",
Pressure::Unknown => "unknown",
}
}
pub fn pressing(self) -> bool {
matches!(self, Pressure::High | Pressure::Critical)
}
}
pub fn gb(mb: u64) -> String {
format!("{:.1} GB", mb as f64 / 1024.0)
}
/// THE RULE. Critical: commit available under 8 % of the limit or under 4 GB.
/// High: under 15 %, or SOLIDWORKS over its session budget. A commit charge
/// that cannot be read is Unknown (or High, if SOLIDWORKS alone is over budget).
pub fn pressure(sys: Option<&SystemMemory>, sw_private_mb: Option<u64>, budget_mb: u64) -> (Pressure, Vec<String>) {
let over = sw_private_mb.filter(|p| *p > budget_mb);
let over_why = |p: u64| format!("SOLIDWORKS holds {} — over its {} session budget", gb(p), gb(budget_mb));
let Some(s) = sys.filter(|s| s.commit_limit_mb > 0) else {
return match over {
Some(p) => (Pressure::High, vec![over_why(p), "the commit charge could not be read".into()]),
None => (Pressure::Unknown, vec!["the commit charge could not be read".into()]),
};
};
let avail = s.commit_avail_mb as f64;
let limit = s.commit_limit_mb as f64;
let mut why = Vec::new();
if avail < CRITICAL_FRACTION * limit {
why.push(format!("{} of commit available is under 8 % of the {} limit", gb(s.commit_avail_mb), gb(s.commit_limit_mb)));
}
if s.commit_avail_mb < CRITICAL_FLOOR_MB {
why.push(format!("{} of commit available is under 4 GB", gb(s.commit_avail_mb)));
}
if !why.is_empty() {
if let Some(p) = over {
why.push(over_why(p));
}
return (Pressure::Critical, why);
}
if avail < HIGH_FRACTION * limit {
why.push(format!("{} of commit available is under 15 % of the {} limit", gb(s.commit_avail_mb), gb(s.commit_limit_mb)));
}
if let Some(p) = over {
why.push(over_why(p));
}
if why.is_empty() {
(Pressure::Ok, why)
} else {
(Pressure::High, why)
}
}
/// The largest single processes.
pub fn top_processes(list: &[ProcPrivate], n: usize) -> Vec<ProcPrivate> {
let mut v = list.to_vec();
v.sort_by(|a, b| b.private_mb.cmp(&a.private_mb).then(a.pid.cmp(&b.pid)));
v.truncate(n);
v
}
/// One application's total (Chrome is 51 processes).
#[derive(Clone, Debug, PartialEq)]
pub struct ImageTotal {
pub name: String,
pub processes: usize,
pub private_mb: u64,
}
/// The largest applications, by image name (case-insensitive).
pub fn top_images(list: &[ProcPrivate], n: usize) -> Vec<ImageTotal> {
let mut totals: Vec<ImageTotal> = Vec::new();
for p in list {
match totals.iter_mut().find(|t| t.name.eq_ignore_ascii_case(&p.name)) {
Some(t) => {
t.processes += 1;
t.private_mb += p.private_mb;
}
None => totals.push(ImageTotal { name: p.name.clone(), processes: 1, private_mb: p.private_mb }),
}
}
totals.sort_by(|a, b| b.private_mb.cmp(&a.private_mb).then(a.name.cmp(&b.name)));
totals.truncate(n);
totals
}
/// "Fusion360.exe 20.7 GB, SLDWORKS.exe 10.6 GB, chrome.exe 10.2 GB (51 processes)".
pub fn holders_sentence(images: &[ImageTotal]) -> String {
images
.iter()
.map(|i| {
if i.processes > 1 {
format!("{} {} ({} processes)", i.name, gb(i.private_mb), i.processes)
} else {
format!("{} {}", i.name, gb(i.private_mb))
}
})
.collect::<Vec<_>>()
.join(", ")
}
/// One cheap reading: the commit pair and SOLIDWORKS's own share.
#[derive(Clone, Debug)]
pub struct Sample {
pub sys: Result<SystemMemory, String>,
pub sw_pid: Option<u32>,
pub sw_private_mb: Option<u64>,
pub sw_handles: Option<u32>,
pub budget_mb: u64,
pub budget_source: &'static str,
}
impl Sample {
pub fn pressure(&self) -> (Pressure, Vec<String>) {
pressure(self.sys.as_ref().ok(), self.sw_private_mb, self.budget_mb)
}
pub fn commit_avail_mb(&self) -> Option<u64> {
self.sys.as_ref().ok().map(|s| s.commit_avail_mb)
}
}
/// The SOLIDWORKS whose memory is reported: the one COM is attached to, when it
/// is in the census; else the newest sldworks.exe.
pub fn solidworks_pid(os: &dyn LaunchOs) -> Option<u32> {
let census = os.processes().ok()?;
let sw: Vec<_> = census.iter().filter(|p| p.is("SLDWORKS.exe")).collect();
if let Some(a) = crate::launch::attached_solidworks_pid() {
if sw.iter().any(|p| p.pid == a) {
return Some(a);
}
}
sw.iter().max_by_key(|p| p.created_at.unwrap_or(0)).map(|p| p.pid)
}
pub fn sample(os: &dyn LaunchOs) -> Sample {
let (budget_mb, budget_source) = budget();
let sw_pid = solidworks_pid(os);
let (sw_private_mb, sw_handles) = sw_pid.map(|p| os.memory_of(p)).unwrap_or((None, None));
Sample { sys: os.system_memory(), sw_pid, sw_private_mb, sw_handles, budget_mb, budget_source }
}
/// The `memory` block.
pub fn report_of(s: &Sample, procs: Result<Vec<ProcPrivate>, String>) -> Value {
let (p, why) = s.pressure();
let mut v = json!({
"pressure": p.name(),
"pressureWhy": why,
"sessionBudgetMb": s.budget_mb,
"sessionBudgetSource": s.budget_source,
"solidworks": s.sw_pid.map(|pid| json!({
"pid": pid,
"privateMb": s.sw_private_mb,
"handles": s.sw_handles,
"overBudget": s.sw_private_mb.map(|m| m > s.budget_mb),
})),
"readOnly": READ_ONLY_NOTE,
"rule": "critical: commit available under 8 % of the limit or under 4 GB; high: under 15 %, \
or SOLIDWORKS over sessionBudgetMb (SWX_SW_MEMORY_BUDGET_MB). Verbs that create or \
load geometry are refused at critical (low_commit_memory).",
});
if let Value::Object(m) = &mut v {
match &s.sys {
Ok(sys) => {
let used = sys.commit_limit_mb.saturating_sub(sys.commit_avail_mb);
m.insert("commitUsedMb".into(), json!(used));
m.insert("commitLimitMb".into(), json!(sys.commit_limit_mb));
m.insert("commitAvailableMb".into(), json!(sys.commit_avail_mb));
m.insert(
"commitUsedPercent".into(),
json!(if sys.commit_limit_mb > 0 { (used as f64 * 1000.0 / sys.commit_limit_mb as f64).round() / 10.0 } else { 0.0 }),
);
m.insert("physicalTotalMb".into(), json!(sys.phys_total_mb));
m.insert("physicalAvailableMb".into(), json!(sys.phys_avail_mb));
}
Err(e) => {
m.insert("error".into(), json!(e));
}
}
match procs {
Ok(list) => {
let images = top_images(&list, TOP_N);
m.insert(
"topProcesses".into(),
json!(top_processes(&list, TOP_N)
.iter()
.map(|p| json!({"name": p.name, "pid": p.pid, "privateMb": p.private_mb}))
.collect::<Vec<_>>()),
);
m.insert(
"topImages".into(),
json!(images.iter().map(|i| json!({"name": i.name, "processes": i.processes, "privateMb": i.private_mb})).collect::<Vec<_>>()),
);
m.insert("largestHolders".into(), json!(holders_sentence(&images)));
}
Err(e) => {
m.insert("topProcesses".into(), Value::Null);
m.insert("topProcessesError".into(), json!(e));
}
}
}
v
}
pub fn report(os: &dyn LaunchOs) -> Value {
report_of(&sample(os), os.private_by_process())
}
/// The numbers in one clause: "72.3 GB of 76.6 GB committed (94.4 %), 4.2 GB
/// available".
pub fn commit_clause(report: &Value) -> String {
match (report["commitUsedMb"].as_u64(), report["commitLimitMb"].as_u64(), report["commitAvailableMb"].as_u64()) {
(Some(u), Some(l), Some(a)) => format!(
"{} of {} committed ({} %), {} available",
gb(u),
gb(l),
report["commitUsedPercent"].as_f64().unwrap_or(0.0),
gb(a)
),
_ => "the commit charge could not be read".to_string(),
}
}
/// The sentence every reply carries while pressure is high or critical.
pub fn pressure_hint(report: &Value) -> Option<String> {
let p = report["pressure"].as_str()?;
if p != "high" && p != "critical" {
return None;
}
let sw = match (report.pointer("/solidworks/privateMb").and_then(|v| v.as_u64()), report["sessionBudgetMb"].as_u64()) {
(Some(m), Some(b)) => format!(" SOLIDWORKS holds {} (its session budget is {}).", gb(m), gb(b)),
_ => String::new(),
};
let holders = report["largestHolders"].as_str().filter(|h| !h.is_empty()).unwrap_or("could not be read");
let what = if p == "critical" {
"Verbs that create or load geometry are refused until it recovers (low_commit_memory)."
} else {
"Split or pause long runs."
};
Some(format!(
"MEMORY {}: {}.{sw} Largest holders: {holders}. {what} A SOLIDWORKS session the bridge \
started, with no documents open, returns its memory with swx_stop {{userAuthorized:true}} \
then swx_start. NEVER close the user's applications; tell the user. See swx_status.memory \
and the solidworks-memory skill.",
p.to_uppercase(),
commit_clause(report),
))
}
/// EVERY REPLY, while memory is pressing: the hint, and the numbers. Cheap when
/// it is not (one GlobalMemoryStatusEx and SOLIDWORKS's own counters); the
/// all-process read happens only when there is something to say.
pub fn annotate(out: &mut Value) {
let os = crate::launchos::os();
let s = sample(os.as_ref());
if !s.pressure().0.pressing() {
return;
}
let report = report_of(&s, os.private_by_process());
let Some(sentence) = pressure_hint(&report) else { return };
if let Value::Object(m) = out {
match m.get_mut("_hint") {
Some(Value::String(h)) if !h.contains("MEMORY HIGH") && !h.contains("MEMORY CRITICAL") => {
h.push(' ');
h.push_str(&sentence);
}
Some(Value::String(_)) => {}
Some(_) => {
m.insert("memoryHint".into(), json!(sentence));
}
None => {
m.insert("_hint".into(), json!(sentence));
}
}
m.entry("memoryPressure").or_insert(json!({
"pressure": report["pressure"],
"commitAvailableMb": report["commitAvailableMb"],
"commitUsedPercent": report["commitUsedPercent"],
"solidworksPrivateMb": report.pointer("/solidworks/privateMb").cloned().unwrap_or(Value::Null),
"largestHolders": report["largestHolders"],
}));
}
}
/// The ledger's memory sample: commit available and SOLIDWORKS's share, or
/// nothing when neither can be read.
pub fn ledger_sample() -> Option<Value> {
let os = crate::launchos::os();
let s = sample(os.as_ref());
let avail = s.commit_avail_mb();
if avail.is_none() && s.sw_private_mb.is_none() {
return None;
}
Some(json!({"commitAvailMb": avail, "swPrivateMb": s.sw_private_mb}))
}
/// The same, for the one log line per verb.
pub fn log_suffix() -> String {
let os = crate::launchos::os();
let s = sample(os.as_ref());
match (s.commit_avail_mb(), s.sw_private_mb) {
(None, None) => String::new(),
(a, w) => format!(
" | mem: commit available {}, SOLIDWORKS {}",
a.map(gb).unwrap_or_else(|| "unknown".into()),
w.map(gb).unwrap_or_else(|| "not running".into())
),
}
}
// ── hidden documents (0.10.6) ───────────────────────────────────────────────
/// What the hidden-creation bracket did.
#[derive(Debug)]
pub struct Bracket<T, E> {
pub made: Result<T, E>,
/// DocumentVisible(false) succeeded (if not, nothing was created).
pub set_ok: bool,
/// DocumentVisible(true) succeeded afterwards.
pub restored: bool,
}
/// THE HIDDEN-CREATION BRACKET, shared by the COM host and the simulator:
/// `set_visible(false)`, then `make` (only if that worked), then
/// `set_visible(true)` — ALWAYS, whatever `make` did, retried up to `tries`
/// times. ISldWorks::DocumentVisible is a SESSION setting: left false, every
/// document of that type the user opens next would be invisible.
pub fn hidden_bracket<T, E>(
mut set_visible: impl FnMut(bool) -> Result<(), E>,
make: impl FnOnce() -> Result<T, E>,
tries: usize,
) -> Bracket<T, E> {
let off = set_visible(false);
let set_ok = off.is_ok();
let made = match off {
Ok(()) => make(),
Err(e) => Err(e),
};
let mut restored = false;
for _ in 0..tries.max(1) {
if set_visible(true).is_ok() {
restored = true;
break;
}
}
Bracket { made, set_ok, restored }
}
// ── the guard ───────────────────────────────────────────────────────────────
/// Verbs that create or load geometry.
pub fn guarded(command: &str, args: &Value) -> bool {
match command {
"swx_new_document" | "swx_open" | "swx_import" | "swx_render_image" | "swx_show_part" | "swx_show_assembly"
| "swx_show_drawing" => true,
"swx_selftest" => args.get("level").and_then(|l| l.as_str()) == Some("write"),
"swx_run_script" => args
.get("script")
.and_then(|s| s.as_array())
.map(|steps| steps.iter().any(|st| matches!(st.get("op").and_then(|o| o.as_str()), Some("new_part" | "new_assembly"))))
.unwrap_or(false),
_ => false,
}
}
/// A caller's override: `ignoreMemoryPressure:true` with a reason of 8+ characters.
pub fn override_reason(args: &Value) -> Result<Option<String>, ()> {
if args.get("ignoreMemoryPressure").and_then(|v| v.as_bool()) != Some(true) {
return Ok(None);
}
match args.get("reason").and_then(|r| r.as_str()).map(str::trim).filter(|r| r.chars().count() >= 8) {
Some(r) => Ok(Some(r.to_string())),
None => Err(()),
}
}
/// What the guard does with a pressure and an override.
#[derive(Clone, Debug, PartialEq)]
pub enum GuardDecision {
Proceed,
/// Critical, run anyway: the caller said so, with a reason (ledgered).
Overridden(String),
Refuse { override_without_reason: bool },
}
pub fn guard_decision(p: Pressure, over: Result<Option<String>, ()>) -> GuardDecision {
if p != Pressure::Critical {
return GuardDecision::Proceed;
}
match over {
Ok(Some(r)) => GuardDecision::Overridden(r),
Ok(None) => GuardDecision::Refuse { override_without_reason: false },
Err(()) => GuardDecision::Refuse { override_without_reason: true },
}
}
/// Whose SOLIDWORKS is this, and is anything open in it?
#[derive(Clone, Debug, Default, PartialEq)]
pub struct SessionFacts {
pub com_pid: Option<u32>,
pub open_docs: Option<u64>,
/// `None` = cannot tell, which is never read as "the bridge's".
pub bridge_started: Option<bool>,
}
/// Pure: the launch record's claim against the session COM answers for. Both
/// pid AND creation time, like the quit ladder — a pid alone is reusable.
pub fn bridge_started(claim: Option<(u32, u64)>, com_pid: Option<u32>, created: Option<u64>) -> Option<bool> {
match (com_pid, created) {
(Some(pid), Some(c)) => Some(claim == Some((pid, c))),
_ => None,
}
}
pub fn session_facts(ctx: &crate::verbs::Ctx, os: &dyn LaunchOs) -> SessionFacts {
let Ok(state) = ctx.call("app_state", json!({}), 20) else { return SessionFacts::default() };
if state.get("running").and_then(|v| v.as_bool()) == Some(false) {
return SessionFacts::default();
}
let com_pid = state.get("processId").and_then(|v| v.as_u64()).map(|p| p as u32);
let open_docs = state.get("documentCount").and_then(|v| v.as_u64());
// A launch that attached but never named its SOLIDWORKS is attributed now
// (0.10.7) — on the Connected edition that was every launch.
if com_pid.is_some() {
crate::launch::attribute_on_attach(os, com_pid);
}
let created = com_pid.and_then(|pid| os.processes().ok()?.iter().find(|p| p.pid == pid).and_then(|p| p.created_at));
SessionFacts { com_pid, open_docs, bridge_started: bridge_started(crate::launch::attributed_solidworks(), com_pid, created) }
}
/// THE SAFE REMEDY, and only the safe one.
pub fn remedy(bridge_started: Option<bool>, open_docs: Option<u64>, sw_private_mb: Option<u64>) -> String {
match (bridge_started, open_docs) {
(Some(true), Some(0)) => format!(
"This SOLIDWORKS session was started by the bridge and has no documents open: \
swx_stop {{\"userAuthorized\": true, \"reason\": \"...\"}} then swx_start frees about {} \
(SOLIDWORKS keeps memory after its documents close).",
sw_private_mb.map(gb).unwrap_or_else(|| "its memory".into())
),
(Some(true), Some(n)) => format!(
"This SOLIDWORKS session was started by the bridge but has {n} document(s) open. Close \
the bridge's own (swx_reset_workspace); once none are open, swx_stop \
{{\"userAuthorized\": true}} then swx_start returns its memory. A document the user \
opened is theirs: tell the user which applications hold the most, and never close \
them."
),
_ => "Tell the user which applications hold the most memory (largestHolders) and let \
them decide. Never close them — not their SOLIDWORKS, not Fusion, not the browser, \
nothing."
.to_string(),
}
}
/// The guard, for the dispatcher. `Ok(Some(override))` runs the verb and says it
/// was overridden; `Err(refusal)` changes nothing.
pub fn guard(ctx: &crate::verbs::Ctx, command: &str, args: &Value) -> Result<Option<Value>, Value> {
let os = crate::launchos::os();
let s = sample(os.as_ref());
let (p, why) = s.pressure();
let decision = guard_decision(p, override_reason(args));
if decision == GuardDecision::Proceed {
return Ok(None);
}
let report = report_of(&s, os.private_by_process());
if let GuardDecision::Overridden(reason) = &decision {
crate::ledger::record(
"memory_guard_overridden",
json!({"verb": command, "reason": reason, "pressure": p.name(), "why": why,
"commitAvailableMb": report["commitAvailableMb"], "commitLimitMb": report["commitLimitMb"],
"solidworksPrivateMb": s.sw_private_mb}),
);
return Ok(Some(json!({"overridden": true, "reason": reason, "pressure": p.name(), "commit": commit_clause(&report)})));
}
let facts = session_facts(ctx, os.as_ref());
let fix = remedy(facts.bridge_started, facts.open_docs, s.sw_private_mb);
crate::ledger::record(
"memory_guard_refused",
json!({"verb": command, "why": why, "commitAvailableMb": report["commitAvailableMb"],
"commitLimitMb": report["commitLimitMb"], "solidworksPrivateMb": s.sw_private_mb}),
);
let without_reason = matches!(decision, GuardDecision::Refuse { override_without_reason: true });
let mut out = crate::verbs::err_code(
"low_commit_memory",
format!(
"{command} refused: available committed memory is critically low — {}. Creating or \
loading geometry now risks allocation failures in every application on this PC. \
Nothing was changed.{}",
commit_clause(&report),
if without_reason { " (ignoreMemoryPressure needs a reason of 8+ characters.)" } else { "" }
),
format!(
"Largest holders: {}. {fix} To run it anyway, pass ignoreMemoryPressure:true with a \
reason; the override is ledgered. See the solidworks-memory skill.",
report["largestHolders"].as_str().unwrap_or("could not be read")
),
);
if let Value::Object(m) = &mut out {
m.insert("memory".into(), report.clone());
m.insert("largestHolders".into(), report["largestHolders"].clone());
m.insert("remedy".into(), json!(fix));
m.insert(
"solidworksSession".into(),
json!({"pid": facts.com_pid, "bridgeStarted": facts.bridge_started, "openDocuments": facts.open_docs}),
);
m.insert("changedNothing".into(), json!(true));
}
Err(out)
}
// ── long runs ───────────────────────────────────────────────────────────────
/// Between two checks of a long run.
#[derive(Clone, Debug, PartialEq)]
pub enum Between {
Continue,
/// Carry on, and say so: commit is HIGH, but SOLIDWORKS is within its
/// budget — the memory is the other applications', and stopping would only
/// throttle the run (0.10.7, measured: a run stopped before T01 at 86.9 %
/// committed while SOLIDWORKS held 1.5 GB).
Note(String),
Recycle(String),
Stop(String),
}
/// Does this reading call for a decision that needs the SESSION's facts (whose
/// it is, what is open)? Only SOLIDWORKS over its budget, or commit CRITICAL.
pub fn needs_attention(p: Pressure, sw_private_mb: Option<u64>, budget_mb: u64) -> bool {
p == Pressure::Critical || sw_private_mb.map(|m| m > budget_mb).unwrap_or(false)
}
/// THE RULE BETWEEN CHECKS (0.10.7):
/// * SOLIDWORKS OVER ITS BUDGET: recycle it if it is the bridge's own, empty
/// session; otherwise STOP — it is SOLIDWORKS that is growing.
/// * commit CRITICAL: recycle the bridge's own empty session if SOLIDWORKS
/// holds enough to matter; otherwise STOP.
/// * commit HIGH with SOLIDWORKS within budget: CARRY ON and report it — the
/// reused document keeps SOLIDWORKS flat, and the memory is elsewhere.
pub fn between_checks(p: Pressure, sw_private_mb: Option<u64>, budget_mb: u64, bridge_started: Option<bool>, open_docs: Option<u64>) -> Between {
let over = sw_private_mb.filter(|m| *m > budget_mb);
let not_recyclable = |why: String| -> Between {
match (bridge_started, open_docs) {
(Some(true), Some(n)) if n > 0 => {
Between::Stop(format!("{why}; the session is the bridge's but {n} document(s) are open, so it cannot be recycled safely"))
}
(Some(true), None) => Between::Stop(format!("{why}; the open-document count could not be read, so it cannot be recycled safely")),
(Some(false), _) => Between::Stop(format!("{why}; this SOLIDWORKS was not started by the bridge, so the bridge will not end it")),
(None, _) => Between::Stop(format!(
"{why}; whose SOLIDWORKS session this is could not be established, so the bridge will not end it"
)),
(Some(true), Some(_)) => Between::Stop(why),
}
};
let ours_and_empty = matches!((bridge_started, open_docs), (Some(true), Some(0)));
if let Some(m) = over {
let why = format!("SOLIDWORKS holds {} (budget {})", gb(m), gb(budget_mb));
return if ours_and_empty { Between::Recycle(why) } else { not_recyclable(why) };
}
match p {
Pressure::Critical => {
let why = "committed memory is critical".to_string();
let worth = sw_private_mb.map(|m| m >= RECYCLE_WORTH_MB).unwrap_or(false);
if ours_and_empty && worth {
Between::Recycle(why)
} else if ours_and_empty {
Between::Stop(format!(
"{why}; SOLIDWORKS itself holds only {} — the memory is held by other applications \
(largestHolders), so recycling SOLIDWORKS would not relieve it",
sw_private_mb.map(gb).unwrap_or_else(|| "an unknown amount".into())
))
} else {
not_recyclable(why)
}
}
Pressure::High => Between::Note(format!(
"committed memory is high, but SOLIDWORKS holds {} — within its {} budget — so the run carries on \
(the memory is held by other applications; see largestHolders)",
sw_private_mb.map(gb).unwrap_or_else(|| "an unknown amount".into()),
gb(budget_mb)
)),
_ => Between::Continue,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sys(limit: u64, avail: u64) -> SystemMemory {
SystemMemory { commit_limit_mb: limit, commit_avail_mb: avail, phys_total_mb: 32_000, phys_avail_mb: 4_000 }
}
fn pp(name: &str, pid: u32, mb: u64) -> ProcPrivate {
ProcPrivate { name: name.into(), pid, private_mb: mb }
}
/// The numbers measured on 2026-10-14, before and after the quit.
#[test]
fn the_measured_incident_is_critical_and_the_quit_made_it_ok() {
// 72.3 of 76.6 GB committed, 4.2 GB available, SOLIDWORKS 10.6 GB.
let before = sys(78_438, 4_300);
let (p, why) = pressure(Some(&before), Some(10_854), DEFAULT_BUDGET_MB);
assert_eq!(p, Pressure::Critical, "{why:?}");
assert!(why.iter().any(|w| w.contains("under 8 %")) && why.iter().any(|w| w.contains("over its 6.0 GB")), "{why:?}");
// After the quit: 56.1 of 73.7 GB, 17.7 GB available, no SOLIDWORKS.
assert_eq!(pressure(Some(&sys(75_469, 18_125)), None, DEFAULT_BUDGET_MB).0, Pressure::Ok);
}
#[test]
fn thresholds() {
let b = DEFAULT_BUDGET_MB;
// Under 8 % is critical even with plenty of absolute room on a big PC...
assert_eq!(pressure(Some(&sys(200_000, 15_000)), None, b).0, Pressure::Critical);
// ...and under 4 GB is critical even when that is over 8 % of a small one.
assert_eq!(pressure(Some(&sys(40_000, 4_000)), None, b).0, Pressure::Critical);
assert_eq!(pressure(Some(&sys(40_000, 4_096)), None, b).0, Pressure::High, "10 % is high, not critical");
// 8..15 % is high.
assert_eq!(pressure(Some(&sys(100_000, 10_000)), None, b).0, Pressure::High);
assert_eq!(pressure(Some(&sys(100_000, 15_000)), None, b).0, Pressure::Ok, "15 % exactly is ok");
// SOLIDWORKS over its budget is high on an otherwise roomy machine.
assert_eq!(pressure(Some(&sys(100_000, 50_000)), Some(b + 1), b).0, Pressure::High);
assert_eq!(pressure(Some(&sys(100_000, 50_000)), Some(b), b).0, Pressure::Ok, "at the budget is not over it");
// Unreadable commit is UNKNOWN, never ok — or high if SOLIDWORKS alone says so.
assert_eq!(pressure(None, None, b).0, Pressure::Unknown);
assert_eq!(pressure(Some(&sys(0, 0)), Some(100), b).0, Pressure::Unknown);
assert_eq!(pressure(None, Some(b + 1), b).0, Pressure::High);
}
#[test]
fn the_largest_holders_are_named_per_process_and_per_application() {
let mut list = vec![pp("Fusion360.exe", 10, 21_197), pp("SLDWORKS.exe", 20, 10_854), pp("vmmemWSL", 30, 4_812)];
for i in 0..51 {
list.push(pp("chrome.exe", 100 + i, 200));
}
let top = top_processes(&list, TOP_N);
assert_eq!(top.iter().map(|p| p.pid).collect::<Vec<_>>(), [10, 20, 30, 100, 101], "largest first, ties by pid");
let images = top_images(&list, TOP_N);
assert_eq!(images[0], ImageTotal { name: "Fusion360.exe".into(), processes: 1, private_mb: 21_197 });
assert_eq!(images[2], ImageTotal { name: "chrome.exe".into(), processes: 51, private_mb: 10_200 });
assert_eq!(
holders_sentence(&images),
"Fusion360.exe 20.7 GB, SLDWORKS.exe 10.6 GB, chrome.exe 10.0 GB (51 processes), vmmemWSL 4.7 GB"
);
// Case-insensitive grouping.
assert_eq!(top_images(&[pp("Chrome.exe", 1, 10), pp("chrome.EXE", 2, 5)], 5)[0].processes, 2);
}
#[test]
fn the_guard_refuses_only_at_critical_and_an_override_needs_a_reason() {
assert_eq!(guard_decision(Pressure::Ok, Ok(None)), GuardDecision::Proceed);
assert_eq!(guard_decision(Pressure::High, Ok(None)), GuardDecision::Proceed, "high warns, never refuses");
assert_eq!(guard_decision(Pressure::Unknown, Ok(None)), GuardDecision::Proceed);
assert_eq!(guard_decision(Pressure::Critical, Ok(None)), GuardDecision::Refuse { override_without_reason: false });
assert_eq!(guard_decision(Pressure::Critical, Err(())), GuardDecision::Refuse { override_without_reason: true });
assert_eq!(guard_decision(Pressure::Critical, Ok(Some("the user said go".into()))), GuardDecision::Overridden("the user said go".into()));
assert_eq!(override_reason(&json!({})), Ok(None));
assert_eq!(override_reason(&json!({"ignoreMemoryPressure": true})), Err(()));
assert_eq!(override_reason(&json!({"ignoreMemoryPressure": true, "reason": " short "})), Err(()));
assert_eq!(override_reason(&json!({"ignoreMemoryPressure": true, "reason": "Drew asked for it"})), Ok(Some("Drew asked for it".into())));
assert_eq!(override_reason(&json!({"ignoreMemoryPressure": false, "reason": "Drew asked for it"})), Ok(None));
}
#[test]
fn guarded_verbs_are_the_ones_that_create_or_load_geometry() {
for v in ["swx_new_document", "swx_open", "swx_import", "swx_render_image", "swx_show_part", "swx_show_assembly", "swx_show_drawing"] {
assert!(guarded(v, &json!({})), "{v}");
}
assert!(guarded("swx_selftest", &json!({"level": "write"})));
assert!(!guarded("swx_selftest", &json!({"level": "read"})) && !guarded("swx_selftest", &json!({})));
assert!(guarded("swx_run_script", &json!({"script": [{"op": "new_part"}, {"op": "rebuild"}]})));
assert!(guarded("swx_run_script", &json!({"script": [{"op": "new_assembly"}]})));
assert!(!guarded("swx_run_script", &json!({"script": [{"op": "rebuild"}]})), "a script on an open part loads nothing new");
for v in ["swx_status", "swx_stop", "swx_close_doc", "swx_reset_workspace", "swx_inspect_bodies", "swx_export_step", "swx_docs"] {
assert!(!guarded(v, &json!({})), "{v} frees or reads; never refused for memory");
}
}
#[test]
fn the_remedy_is_the_safe_one() {
let ours = remedy(Some(true), Some(0), Some(10_854));
assert!(ours.contains("swx_stop") && ours.contains("swx_start") && ours.contains("10.6 GB"), "{ours}");
for (b, d) in [(Some(false), Some(0)), (None, None), (Some(false), Some(3)), (None, Some(0))] {
let r = remedy(b, d, Some(10_854));
assert!(r.contains("Tell the user") && r.contains("Never close them") && !r.contains("swx_stop"), "{b:?} {d:?}: {r}");
}
let busy = remedy(Some(true), Some(2), Some(10_854));
assert!(busy.contains("2 document(s)") && busy.contains("never close"), "{busy}");
}
#[test]
fn only_the_bridges_own_session_counts_as_the_bridges() {
assert_eq!(bridge_started(Some((700, 1_000)), Some(700), Some(1_000)), Some(true));
assert_eq!(bridge_started(Some((700, 1_000)), Some(700), Some(1_001)), Some(false), "pid reused: a different process");
assert_eq!(bridge_started(Some((700, 1_000)), Some(701), Some(1_000)), Some(false));
assert_eq!(bridge_started(None, Some(700), Some(1_000)), Some(false), "no launch record: not the bridge's");
assert_eq!(bridge_started(Some((700, 1_000)), Some(700), None), None, "cannot tell is never yes");
assert_eq!(bridge_started(Some((700, 1_000)), None, None), None);
}
#[test]
fn a_long_run_recycles_only_its_own_empty_session_and_otherwise_stops() {
let b = DEFAULT_BUDGET_MB;
assert_eq!(between_checks(Pressure::Ok, Some(1_000), b, Some(false), Some(4)), Between::Continue);
// OVER BUDGET: the bridge's own empty session is recycled; anything else stops.
assert!(matches!(between_checks(Pressure::Ok, Some(b + 1), b, Some(true), Some(0)), Between::Recycle(_)));
for (bs, d, says) in [
(Some(true), Some(1), "1 document(s) are open"),
(Some(true), None, "could not be read"),
(Some(false), Some(0), "not started by the bridge"),
(None, Some(0), "could not be established"),
] {
match between_checks(Pressure::High, Some(b + 1), b, bs, d) {
Between::Stop(why) => assert!(why.contains("SOLIDWORKS holds") && why.contains(says), "{why}"),
other => panic!("{bs:?} {d:?}: {other:?} — only the bridge's own empty session is recycled"),
}
}
// CRITICAL: recycled when it is ours, empty and SOLIDWORKS holds enough to matter...
assert!(matches!(between_checks(Pressure::Critical, Some(RECYCLE_WORTH_MB), b, Some(true), Some(0)), Between::Recycle(_)));
// ...otherwise it stops.
for sw in [Some(1_000), None] {
match between_checks(Pressure::Critical, sw, b, Some(true), Some(0)) {
Between::Stop(why) => assert!(why.contains("held by other applications"), "{why}"),
other => panic!("{other:?}"),
}
}
assert!(matches!(between_checks(Pressure::Critical, Some(1_500), b, Some(false), Some(0)), Between::Stop(_)));
// HIGH WITH SOLIDWORKS WITHIN BUDGET: the run CARRIES ON and says so —
// measured 2026-10-14: 86.9 % committed, Fusion 20.4 GB, SOLIDWORKS 1.5 GB,
// and 0.10.6 stopped before T01. Whoever's session it is.
for bs in [Some(false), Some(true), None] {
match between_checks(Pressure::High, Some(1_536), b, bs, Some(0)) {
Between::Note(why) => assert!(why.contains("carries on") && why.contains("1.5 GB"), "{why}"),
other => panic!("{bs:?}: {other:?} — high pressure that is not SOLIDWORKS's must not throttle the run"),
}
}
assert!(!needs_attention(Pressure::High, Some(1_536), b), "high alone needs no session facts");
assert!(needs_attention(Pressure::Critical, Some(10), b) && needs_attention(Pressure::Ok, Some(b + 1), b));
assert_eq!(between_checks(Pressure::Unknown, Some(10), b, None, None), Between::Continue);
}
#[test]
fn the_hidden_bracket_always_puts_the_setting_back() {
use std::cell::RefCell;
let calls = RefCell::new(Vec::new());
// Creation fails: the setting is still restored.
let b = hidden_bracket(
|v| {
calls.borrow_mut().push(v);
Ok::<(), String>(())
},
|| Err::<u32, String>("NewDocument failed".into()),
3,
);
assert!(b.made.is_err() && b.set_ok && b.restored);
assert_eq!(*calls.borrow(), vec![false, true], "off, then on — after a failure too");
// The restore fails twice, then works: retried, and reported restored.
let n = RefCell::new(0);
let b = hidden_bracket(
|v| {
if v {
*n.borrow_mut() += 1;
if *n.borrow() < 3 {
return Err("busy".to_string());
}
}
Ok(())
},
|| Ok::<u32, String>(7),
3,
);
assert_eq!((b.made, b.restored, *n.borrow()), (Ok(7), true, 3));
// It never works: reported NOT restored, after every try.
let n = RefCell::new(0);
let b = hidden_bracket(|v| if v { *n.borrow_mut() += 1; Err("no".to_string()) } else { Ok(()) }, || Ok::<u32, String>(1), 3);
assert!(!b.restored && *n.borrow() == 3);
// DocumentVisible(false) fails: nothing is created — and it is still set back.
let made = RefCell::new(false);
let calls = RefCell::new(Vec::new());
let b = hidden_bracket(
|v| {
calls.borrow_mut().push(v);
if v { Ok(()) } else { Err("refused".to_string()) }
},
|| {
*made.borrow_mut() = true;
Ok::<u32, String>(1)
},
3,
);
assert!(!b.set_ok && b.made.is_err() && !*made.borrow() && b.restored);
assert_eq!(*calls.borrow(), vec![false, true]);
}
#[test]
fn the_report_and_its_hint_name_the_holders() {
let s = Sample {
sys: Ok(sys(78_438, 4_300)),
sw_pid: Some(20),
sw_private_mb: Some(10_854),
sw_handles: Some(4_000),
budget_mb: DEFAULT_BUDGET_MB,
budget_source: "default",
};
let r = report_of(&s, Ok(vec![pp("Fusion360.exe", 10, 21_197), pp("SLDWORKS.exe", 20, 10_854)]));
assert_eq!(r["pressure"], "critical");
assert_eq!(r["commitUsedMb"], 78_438 - 4_300);
assert_eq!(r["commitUsedPercent"], 94.5);
assert_eq!(r["solidworks"]["overBudget"], true);
assert_eq!(r["topProcesses"][0]["name"], "Fusion360.exe");
let h = pressure_hint(&r).unwrap();
assert!(h.starts_with("MEMORY CRITICAL: 72.4 GB of 76.6 GB committed (94.5 %), 4.2 GB available."), "{h}");
assert!(h.contains("Fusion360.exe 20.7 GB, SLDWORKS.exe 10.6 GB") && h.contains("NEVER close the user's applications"), "{h}");
// Nothing to say when it is fine.
let ok = report_of(&Sample { sys: Ok(sys(78_438, 40_000)), sw_private_mb: Some(1_000), ..s.clone() }, Ok(vec![]));
assert_eq!(pressure_hint(&ok), None);
// An unreadable process list is said, not hidden.
let r = report_of(&s, Err("no".into()));
assert!(r["topProcesses"].is_null() && r["topProcessesError"] == "no");
}
}