← Commit history
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.10.6+0.10.7
Cargo.lock+1−1
@@ -122,7 +122,7 @@ dependencies = [  [[package]] name = "swx-bridge"-version = "0.10.6"+version = "0.10.7" dependencies = [  "miniz_oxide",  "serde",
Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "swx-bridge"-version = "0.10.6"+version = "0.10.7" edition = "2021" rust-version = "1.80" description = "Adom SOLIDWORKS bridge — swx_* verbs over the SOLIDWORKS COM API, as a single native binary"
README.md+35−1
@@ -498,7 +498,41 @@ not.  ## Roadmap -- v0.10.6 (this): **fixing the memory growth at its root.** Measured+- v0.10.7 (this): **what the live run decided.** 0.10.6 was measured on+  SOLIDWORKS 2026:+  - Hidden documents cannot be modelled. SOLIDWORKS selects only in the active+    document, and a hidden one cannot be activated.+  - Twelve builds in ONE reused, visible part left SOLIDWORKS flat (3260 to+    3246 MB, handles 4373 to 4398). Each new part costs about 85 MB and 125+    handles.+  - `swx_com_respawn` freed about 210 MB while the handles did not drop, so+    the bridge does not pin documents.+  - The Connected edition's launcher re-parents, so 0.10.6 never recognised its+    own launch.++  So this release:+  - **Visible by default.** The self-test, the quit ladder's scratch part,+    `swx_new_document` and scripts are all visible. A script that would model a+    hidden part is refused up front with `document_hidden`.+  - **One visible reused document per battery.** The self-test uses it,+    reports `documentsCreated`, and ends by respawning the COM host+    (`comRespawn.freedMb`). A `reuseDocument` that cannot be used says so by+    name. Measured, it read "needs a document", because the host looked only at+    the active document.+  - **Attribution at attach.** First ancestry; failing that, the ONE+    sldworks.exe that was not in the launch's baseline and was created between+    the attempt's start and the attach. Never with two candidates, never a+    process other than COM's. Recorded as+    `attributedSolidworks.how:"newProcessInLaunchWindow"`.+  - **No throttling.** The self-test carries on when commit is high but+    SOLIDWORKS is within its budget, and reports it (`memoryPressureSeen`). It+    recycles the bridge's own empty session when SOLIDWORKS itself is over+    budget. It stops only at critical, or when SOLIDWORKS is over budget in a+    session it may not end. The verb guard is unchanged (critical only).+  - `clear_part` is now marked measured live.++  628 tests.+- v0.10.6: **fixing the memory growth at its root.** Measured   2026-10-14 on 0.10.5 (12 parts built and closed, on a fresh SOLIDWORKS   2026): SOLIDWORKS's private bytes rose 520 MB, 913, 1191, then +83 MB per part   to 2035 MB. Handles went 2845 to 4539 (about +125 per part).
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "solidworks",   "displayName": "SOLIDWORKS",-  "version": "0.10.6",+  "version": "0.10.7",   "description": "Bridge for SOLIDWORKS desktop CAD — create and open documents, a modeling DSL, file management (find/open/save/export/Pack and Go), custom properties, BOM generation, design analysis (mass properties, bodies, parameters, interference, rebuild health) and background window/dialog etiquette, over the SOLIDWORKS COM API. Single native binary: no interpreter, no PowerShell, ~10ms cold start.",   "author": "Adom Inc.",   "license": "MIT",
docs/LIVE-RESULTS.md+12
@@ -78,6 +78,18 @@ and projects the origin onto the face, so their circles were moved to sketch | T39 | a linear pattern in two directions (X +12, Z -12): 4 bosses on the block | #1.6 | 4084.823 | 4084.823 | [-10, 0, -10] to [10, 13, 10] | PASS | | T42 | a shell outward 1 mm with the top face removed: the block becomes the cavity | #1.6 | 1324.0 | 1324.0 | [-11, -1, -11] to [11, 10, 11] | PASS | +## Memory, measured 2026-10-14 (bridges 0.10.5 and 0.10.6)++| What | Measured |+|---|---|+| A new part per build (12 builds, each closed) | SOLIDWORKS private bytes +83 to +85 MB per part (520 to 2035 MB; again 538 to 1697 MB over 8); handles +~125 per part; `GetDocumentCount` 0 after every close |+| ONE reused part (`reuseDocument`, 12 builds) | 3260 to 3246 MB, handles 4373 to 4398: flat |+| A part created hidden (`visible:false`), then modelled | refused by SOLIDWORKS: a hidden document cannot be activated, and modelling selects |+| `swx_com_respawn` | about 210 MB freed (1697 to 1487 MB), handles unchanged (4022 to 4056): the bridge does not pin documents |+| Creating the reuse part (once) | +1.95 GB while open (1487 to 3437 MB), +285 MB after its close — observed, unexplained |++`clear_part` (the reuse step) is marked measured on 2026 in the variants table.+ ## Not yet measured  - SOLIDWORKS **2021** (and any version other than 2026): supported by the API audit in `docs/COMPATIBILITY.md`, not yet run. `swx_selftest {"level":"write"}` on that install answers it.
install.sh

  
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "solidworks-bridge",-  "version": "0.10.6",+  "version": "0.10.7",   "type": "app",   "description": "Skills for your container so your AI knows how to drive the SOLIDWORKS bridge (swx_* verbs). The bridge runtime itself is the release zip — a single native swx-bridge.exe; Adom Bridge loads that.",   "tags": [
references/modelling-and-export.md+9−14
@@ -40,20 +40,15 @@ adom-bridge --ai-thread "<thread>" swx_save_as '{"outputPath":"C:\\parts\\bracke  - `swx_new_document` gives you an **unsaved** document: it has a title (`Part1`)   and **no path** until `swx_save_as`. Target it by `name`, not by path.-- **Hidden documents and reuse (0.10.6).** Measured 2026-10-14: every document created-  and closed left SOLIDWORKS about 83 MB and 125 handles heavier, for the life of the-  session.-  - Make scratch and test geometry hidden with `swx_new_document {"type":"part",-    "visible":false}`, or `{"op":"new_part","visible":false}` in a script. The document-    has no window, and every object-model verb works on it. SOLIDWORKS cannot show a-    hidden-created document later, so save it and reopen it to see it.-  - A user's own script stays visible by default.-  - For a batch, run each script with `"reuseDocument": "<path of a part the bridge-    made>"`. Its leading `new_part` becomes `clear_part`, which deletes every feature-    after the origin, and only in a part the bridge created. N checks then cost one-    document.-  - Whether hiding or reuse stops the growth is UNMEASURED. See the-    **solidworks-memory** skill.+- **Batches reuse one document (measured, 2026-10-14).**+  - Every NEW part costs SOLIDWORKS about 85 MB and 125 handles, kept until it restarts.+    Twelve builds in ONE reused part left it flat.+  - Make one visible part with `swx_new_document`, then run each script with+    `"reuseDocument": "<its path>"`. Its leading `new_part` becomes `clear_part`, which+    deletes every feature after the origin, and only in a part the bridge created.+  - A hidden document (`visible:false`) CANNOT be modelled. SOLIDWORKS selects only in the+    active document, and a hidden one cannot be activated, so scripts refuse it+    (`document_hidden`). Everything is visible by default. - `swx_run_script` takes a JSON **array of steps**, not a string of code —   SOLIDWORKS has no in-process scripting host the way Fusion has Python. Every   dimension is in **millimetres**. A rejected script returns the full op list
skills/solidworks-memory/SKILL.md+48−39
@@ -38,21 +38,26 @@ Parent skill: **solidworks-bridge**   **520 MB, 913 after part 1, 1191 after part 2, then a straight line of +83 MB per part   to 2035 MB after 12**. Its handle count went **2845 to 4539, about +125 per part**, also   linear. `GetDocumentCount` was **0** after every close: no hidden documents linger.-- [measured] So every scratch document created and closed costs about **83 MB and 125-  handles for good**, for the life of the session.-- [unmeasured] The leading explanation: each new VISIBLE document gets a window and its-  own graphics context and driver resources, which SOLIDWORKS does not give back on close.-  SOLIDWORKS's own help for `IModelDoc2::Visible` says hiding a document does not release-  its user-interface resources, and points to opening documents invisibly with-  `ISldWorks::DocumentVisible` instead.-- [unmeasured] The other candidate is a COM reference the bridge itself keeps. A code-  review found none. `swx_com_respawn {"reason": ...}` settles it: it restarts only the-  bridge's own COM host, releasing every reference the bridge holds, and never calls-  SOLIDWORKS. If SOLIDWORKS's handle count drops after a respawn (allow COM's roughly-  six-minute ping rundown if the old host had to be killed), the bridge was pinning-  something.-- [unmeasured] Whether hidden documents or a reused document stop the growth is not yet-  measured. Bridge 0.10.6 makes both possible; the next live run answers it.+  Re-measured with 0.10.6: 538 to 1697 MB over 8 new parts (+85 MB and +125 handles each).+- [measured] **Reusing ONE document is the fix.** Bridge 0.10.6: one visible part made+  with `swx_new_document`, then 12 builds with `swx_run_script {"reuseDocument": <its path>}`+  (each build's `new_part` becomes `clear_part`). Private bytes went **3260 to 3246 MB**,+  handles **4373 to 4398**, over all 12 builds: no growth at all.+- [measured] **Hidden documents cannot be modelled.** A part created with+  `visible:false` (`ISldWorks::DocumentVisible`) cannot be activated: modelling it failed+  "could not activate 'Part1'". SOLIDWORKS selects only in the active document, and every+  modelling step selects something (a plane, a sketch, an edge, a face), so hiding is not+  a memory fix for modelling. From 0.10.7 the bridge refuses that up front+  (`document_hidden`) and makes everything visible by default.+- [measured] **The bridge does not pin the documents.** `swx_com_respawn` (it restarts+  only the bridge's COM host, releasing every reference it holds, and never calls+  SOLIDWORKS) freed about 210 MB of private bytes (1697 to 1487 MB). The handle count did+  not drop (4022 to 4056). So the per-document cost is SOLIDWORKS's own, kept until+  SOLIDWORKS restarts.+- [measured, unexplained] Creating the reuse part once raised SOLIDWORKS's private bytes+  from 1487 to 3437 MB (+1.95 GB) while it was open. After it was closed, SOLIDWORKS held+  1772 MB, 285 MB more than before it was created. This happened once and has no+  explanation yet; record it if you see it again.  ## The rules @@ -60,12 +65,17 @@ Parent skill: **solidworks-bridge**    `memory` block (commit used, limit and available; SOLIDWORKS's own committed bytes and    handles; the largest holders). [measured: SOLIDWORKS's private bytes and handle count,    read through the 12-part run on 2026-10-14] [unmeasured: the pressure verdict and the-   largest-holders list] If commit available is low before you start, do not start: tell-   the user what holds the memory.-2. **Recycle a session the bridge started, never one the user is working in.** When-   SOLIDWORKS's own commit passes a budget (6 GB is a sensible default on a 32 GB PC) or-   commit available drops under about 10 % of the limit, and the session was started by-   the bridge and has **zero documents open**: `swx_stop {"userAuthorized": true, "reason": ...}`+   largest-holders list] If commit is CRITICAL before you start, do not start: tell the+   user what holds the memory. HIGH pressure held by other applications is not a reason to+   stop a run that reuses one document: it keeps SOLIDWORKS flat (measured).+2. **Recycle a session the bridge started, never one the user is working in.**+   [measured] On the 3DEXPERIENCE edition the launcher chain re-parents, so 0.10.6 never+   recognised its own launch (`attributedSolidworks` was null). [unmeasured] From 0.10.7+   the bridge claims the one sldworks.exe that appeared in the launch window+   (`swx_status.launch.attributedSolidworks.how`). When SOLIDWORKS's OWN commit passes a+   budget (6 GB is a sensible default on a 32 GB PC), or commit is critical and SOLIDWORKS+   holds a real share of it, and the session was started by the bridge and has **zero+   documents open**: `swx_stop {"userAuthorized": true, "reason": ...}`    then `swx_start` (1-3 minutes on a 3DEXPERIENCE edition). `swx_stop` refuses on its own    when documents are open or the user is active; never work around that refusal. 3. **Close what you made as you go.** `swx_reset_workspace` after each scratch document, or@@ -78,28 +88,27 @@ Parent skill: **solidworks-bridge**    (The bridge's hidden creation flips one session setting, `DocumentVisible`, for one    `NewDocument` call and puts it back straight away, even when the creation fails. It    never leaves it changed, and it ledgers both the flip and the restore.)-6. **Split long runs.** A battery of more than about 30 new parts should recycle the-   bridge's session between batches. [unmeasured] `swx_selftest {"level":"write"}` does-   this itself from 0.10.5: before each check it reads memory, recycles a session the-   bridge started with nothing open (its reply lists `recycled`), and otherwise stops with-   `stoppedFor:"memory"` rather than end a session that is not its own.+6. **Batch work reuses one document (flat); never one new part per item.** New documents+   cost about 85 MB and 125 handles each until SOLIDWORKS restarts; builds in one reused+   document cost nothing (measured). Make ONE visible part with `swx_new_document`, then+   run each script with `"reuseDocument": "<its path>"`. Its leading `new_part` becomes+   `clear_part`, which deletes every feature after the origin, and only in a part the+   bridge created. `swx_selftest {"level":"write"}` does this itself (it reports+   `documentsCreated`), and it ends by respawning the bridge's COM host+   (`comRespawn.freedMb`). [unmeasured] Between checks, the self-test recycles the+   bridge's own empty session only when SOLIDWORKS ITSELF is over its budget (the+   recycled list says when). It stops only when commit is critical, or when SOLIDWORKS is+   over budget in a session it may not end. High commit held by other applications is+   reported (`memoryPressureSeen`) and does not stop it. 7. **A refusal is not an obstacle to route around.** [unmeasured] While commit is    critical, verbs that create or load geometry answer `low_commit_memory` and change    nothing; the reply names the holders and the safe remedy. `ignoreMemoryPressure:true`    with a reason runs the verb anyway and is ledgered: use it only when the user says so.-8. **Create scratch and test geometry hidden.** [unmeasured effect]-   `swx_new_document {"type":"part","visible":false}`, or `{"op":"new_part","visible":false}`-   in a script, creates the document with no window. Inspection, measurement, export and-   scripts all work on it. SOLIDWORKS documents that a document created hidden cannot be-   shown later: to see it, save it and reopen it. A user's own script stays visible (that-   is the default), because the user wants to see their part. `swx_selftest` and the quit-   ladder's scratch part are hidden from 0.10.6.-9. **Reuse one document for a batch.** [unmeasured effect]-   `swx_run_script {"reuseDocument": "<path of a part the bridge made>", "script": [...]}`-   runs the script in that part, and its leading `new_part` becomes `clear_part`. That-   step deletes every feature after the origin, and only in a part the bridge created. N-   checks then cost one document. `swx_selftest` does this for every check that does not-   set a material or colour or name a feature by number, and reports `documentsCreated`.+8. **Hidden documents can't be modelled.** [measured] `visible:false` creates a document+   SOLIDWORKS cannot activate, and every modelling step needs the active document, so+   `swx_run_script` refuses to model one (`document_hidden`). Everything is visible by+   default, including the self-test's parts. Use `visible:false` only for a document that+   is created to be saved or exported, never modelled.  ## Diagnosing by hand (read-only) 
src/comhost/com.rs+25−4
@@ -5413,8 +5413,11 @@ impl ComBackend {         use crate::script::Op;         let script = args.get("script").cloned().unwrap_or(Value::Null);         let ops = crate::script::parse(&script).map_err(HostError::msg)?;-        let has_doc = self.resolve(&json!({})).is_ok();-        crate::script::validate(&ops, has_doc).map_err(HostError::msg)?;+        // A HIDDEN part cannot be modelled (measured 2026-10-14): refused before+        // anything runs, never as a failure halfway through.+        if let Some(m) = crate::script::hidden_conflict(&ops) {+            return Err(HostError::coded("document_hidden", m));+        }          // PIN the target. Without this every step re-resolved "the active         // document", so a dialog, a rebuild that stole focus, or the user simply@@ -5430,14 +5433,32 @@ impl ComBackend {             .filter(|s| !s.is_empty())             .or_else(|| args.get("targetDocument").and_then(|v| v.as_str()).filter(|s| !s.is_empty()))             .map(String::from);+        let mut pinned_doc: Option<Disp> = None;         if let Some(want) = &pinned {             // Prove the pin resolves BEFORE running anything.-            self.resolve(&Self::doc_arg(&pinned)).map_err(|e| {+            pinned_doc = Some(self.resolve(&Self::doc_arg(&pinned)).map_err(|e| {                 HostError::detailed(                     format!("cannot target document '{want}': {}", e.message),                     "swx_docs lists the titles that are open. Nothing was run.".to_string(),                 )-            })?;+            })?);+        }+        // THE PINNED DOCUMENT COUNTS AS "a document" (0.10.7): measured, a+        // reuseDocument naming a part that was open but not ACTIVE failed+        // "needs a document" because only the active one was looked at.+        let has_doc = pinned_doc.is_some() || self.resolve(&json!({})).is_ok();+        crate::script::validate(&ops, has_doc).map_err(HostError::msg)?;+        if let Some(d) = &pinned_doc {+            if ops.iter().any(|o| o.needs_selection()) && d.flag("Visible", &[]) == Some(false) {+                return Err(HostError::coded(+                    "document_hidden",+                    format!(+                        "'{}' was created invisible: {}",+                        d.string("GetTitle", &[]).unwrap_or_default(),+                        crate::script::HIDDEN_DOCUMENT+                    ),+                ));+            }         }          let mut results = Vec::new();
src/comhost/mock.rs+22−2
@@ -2672,6 +2672,12 @@ impl MockBackend {                              the titles that are actually open"                         ))                     })?;+                if self.find(&key).map(|d| !d.visible).unwrap_or(false) {+                    return Err(HostError::msg(format!(+                        "SOLIDWORKS could not activate '{name}' (errors=0) — it was created invisible, and a hidden \+                         document cannot be activated (measured 2026-10-14)"+                    )));+                }                 self.active = Some(key.clone());                 let visibility = if args.get("visible").and_then(|v| v.as_bool()) == Some(true) {                     self.find(&key).map(|d| Self::make_visible_report(d.visible))@@ -3851,6 +3857,9 @@ impl MockBackend {                 // so a script the mock accepts is a script SOLIDWORKS will be                 // asked to run — that is what makes the mock worth having.                 let ops = crate::script::parse(&script).map_err(HostError::msg)?;+                if let Some(m) = crate::script::hidden_conflict(&ops) {+                    return Err(HostError::coded("document_hidden", m));+                }                 crate::script::validate(&ops, !self.docs.is_empty()).map_err(HostError::msg)?;                 // The SAME pinning the real backend does: every step targets the                 // document the server named (or the one a new_part step made),@@ -3867,7 +3876,15 @@ impl MockBackend {                     None => json!({}),                 };                 if pinned.is_some() {-                    self.resolve(&pin_arg(&pinned))?;+                    let key = self.resolve(&pin_arg(&pinned))?;+                    let hidden = self.find(&key).map(|d| !d.visible).unwrap_or(false);+                    if hidden && ops.iter().any(|o| o.needs_selection()) {+                        let title = self.find(&key).map(|d| d.title.clone()).unwrap_or_default();+                        return Err(HostError::coded(+                            "document_hidden",+                            format!("'{title}' was created invisible: {}", crate::script::HIDDEN_DOCUMENT),+                        ));+                    }                 }                 // ACTIVATE THE PINNED DOCUMENT (0.10.2, review #8): selection works                 // only in SOLIDWORKS' active document. The user's active document@@ -3895,7 +3912,10 @@ impl MockBackend {                                 // not made active by NewDocument, so model that here.                                 if !fault("mock-no-activation") {                                     if let Ok(p) = self.resolve(&pin_arg(&pinned)) {-                                        self.active = Some(p);+                                        // A HIDDEN document cannot be activated (measured).+                                        if self.find(&p).map(|d| d.visible).unwrap_or(true) {+                                            self.active = Some(p);+                                        }                                     }                                 }                                 run = fresh_run();
src/compat.rs+10−4
@@ -105,8 +105,14 @@ pub const LIVE_2026_10_14: Measurement = Measurement { year: 2026, major: 34, ed /// tools/dsl-live-check.py (T33-T39, T42), all passing on the same install. pub const LIVE_2026_10_14_B: Measurement = Measurement { year: 2026, major: 34, edition: "connected", date: "2026-10-14", bridge: "0.10.4" }; +/// The same day, bridge 0.10.6: twelve builds in ONE reused part+/// (`reuseDocument`, its new_part becoming clear_part) — memory flat, and+/// clear_part re-reads the tree and fails if anything is left.+pub const LIVE_2026_10_14_C: Measurement = Measurement { year: 2026, major: 34, edition: "connected", date: "2026-10-14", bridge: "0.10.6" };+ const M2026: &[Measurement] = &[LIVE_2026_10_14]; const M2026B: &[Measurement] = &[LIVE_2026_10_14_B];+const M2026C: &[Measurement] = &[LIVE_2026_10_14_C]; const UNMEASURED: &[Measurement] = &[];  /// One variant of one DSL op, and where it was measured live.@@ -122,9 +128,9 @@ pub struct OpFact { pub const OP_FACTS: &[OpFact] = &[     OpFact { id: "new_part", what: "a new part document", checks: "every check", measured: M2026 },     OpFact { id: "new_assembly", what: "a new assembly document", checks: "not in the live battery", measured: UNMEASURED },-    OpFact { id: "new_part.invisible", what: "a new part created HIDDEN (DocumentVisible false around NewDocument)", checks: "not yet run live (0.10.6)", measured: UNMEASURED },-    OpFact { id: "new_assembly.invisible", what: "a new assembly created HIDDEN", checks: "not yet run live (0.10.6)", measured: UNMEASURED },-    OpFact { id: "clear_part", what: "every feature after the origin deleted from the bridge's own part (DeleteSelection2)", checks: "not yet run live (0.10.6)", measured: UNMEASURED },+    OpFact { id: "new_part.invisible", what: "a new part created HIDDEN (DocumentVisible false around NewDocument) — it CANNOT be modelled: a hidden document cannot be activated, and every modelling op selects (measured 2026-10-14)", checks: "creation worked live (0.10.6); its modelling failed, so scripts now refuse it (document_hidden)", measured: UNMEASURED },+    OpFact { id: "new_assembly.invisible", what: "a new assembly created HIDDEN (cannot be modelled, as new_part.invisible)", checks: "not run live", measured: UNMEASURED },+    OpFact { id: "clear_part", what: "every feature after the origin deleted from the bridge's own part (DeleteSelection2), so one document serves a whole battery", checks: "12 reused builds (reuseDocument), 2026-10-14", measured: M2026C },     OpFact { id: "sketch.plane.front", what: "a sketch on Front Plane", checks: "T01 T03 T10 T14 T27", measured: M2026 },     OpFact { id: "sketch.plane.top", what: "a sketch on Top Plane", checks: "T02 T13 T24 T30", measured: M2026 },     OpFact { id: "sketch.plane.right", what: "a sketch on Right Plane (sketch +x = model −Z, +y = model +Y; a boss goes +X)", checks: "T35", measured: M2026B },@@ -521,7 +527,7 @@ mod tests {         assert_eq!(             unmeasured,             [-                "chamfer.allOfFeature", "clear_part", "extrude.both.throughAll", "fillet.createDefinition", "new_assembly",+                "chamfer.allOfFeature", "extrude.both.throughAll", "fillet.createDefinition", "new_assembly",                 "new_assembly.invisible", "new_part.invisible", "rebuild",                 "save_as", "select.entitySelect4", "select.selectByIdAtPoint", "select.selectByRay", "set_appearance.body",                 "sketch.face.front.positive", "sketch.face.right.negative", "sketch.offset.front.negative",
src/launch.rs+201−1
@@ -839,6 +839,11 @@ pub struct Attempt {     /// can never authorise acting on a DIFFERENT process than the one it was     /// made about.     pub attributed_sldworks: Option<(u32, u64)>,+    /// HOW `attributed_sldworks` was established (0.10.7): `ancestry` (a parent+    /// walk back to our own chain), `newProcessInLaunchWindow` (the one+    /// sldworks.exe that was not in the baseline and was created between the+    /// attempt's start and the attach), or `reattributedAfterSignIn`.+    pub attributed_how: Option<String>,     /// Did we ever see the attributed root, or anything descending from it?     /// An attributed route whose chain was NEVER confirmed cannot be cleaned     /// up, and that is a failure to account for what we started — not a clean@@ -918,7 +923,7 @@ impl Attempt {             // terminated on either reading.             "possiblyOurs": self.possibly_ours,             "attributedSolidworks": self.attributed_sldworks-                .map(|(p, c)| json!({"pid": p, "createdAt": c})),+                .map(|(p, c)| json!({"pid": p, "createdAt": c, "how": self.attributed_how})),             "chainLastSeen": self.chain_last_seen                 .iter()                 .map(|(p, c, t)| json!([p, c, t]))@@ -1071,6 +1076,7 @@ pub fn load() -> LaunchStore {                 v.get("createdAt")?.as_u64()?,             ))         }),+        attributed_how: a.pointer("/attributedSolidworks/how").and_then(|v| v.as_str()).map(String::from),         chain_last_seen: a             .get("chainLastSeen")             .and_then(|x| x.as_array())@@ -1432,6 +1438,7 @@ pub fn begin(             possibly_ours: Vec::new(),             chain_last_seen: Vec::new(),             attributed_sldworks: None,+            attributed_how: None,             root_confirmed: false,             unattributed_log: false,             foreign_chain_seen: false,@@ -1501,6 +1508,7 @@ fn unfinished_placeholder(shortcut: &str) -> Attempt {         possibly_ours: Vec::new(),         chain_last_seen: Vec::new(),         attributed_sldworks: None,+        attributed_how: None,         root_confirmed: false,         unattributed_log: false,         foreign_chain_seen: false,@@ -3202,6 +3210,7 @@ pub fn reattribute_after_attach(os: &dyn LaunchOs, attached: bool) -> Option<Str             // ladder that builds its Identity from the COM pid — can now check             // that it is talking to the same process the claim was made about.             at.attributed_sldworks = Some((sw_pid, sw_created));+            at.attributed_how = Some("reattributedAfterSignIn".into());             at.last_route_before_attach = Some(route.clone());             if at.attribution.is_none() {                 if let (Some(pid), Some(c)) = (root, Some(created)) {@@ -3244,6 +3253,131 @@ pub fn reattribute_after_attach(os: &dyn LaunchOs, attached: bool) -> Option<Str     )) } +/// WHICH SOLIDWORKS DID THIS LAUNCH START? Decided at attach (0.10.7).+///+/// MEASURED 2026-10-14 on the Connected edition: the launch recorded its root+/// (pid 53900, `startProcessPassThru`) and attached, but `attributedSolidworks`+/// stayed null — the sldworks.exe that came up (pid 70644, created 4 s after+/// the root) has ppid 40520, because the launcher chain RE-PARENTS. Only a+/// sign-in re-attribution ever set it, so a session swx_start had launched four+/// seconds earlier read `bridgeStarted:false`, and a long run could never+/// recycle it.+///+/// So, at attach: first ANCESTRY (a parent walk back to our own chain, the+/// strongest proof); failing that, THE LAUNCH WINDOW — the one sldworks.exe+/// that (a) was not in the baseline census taken before our invoke and (b) was+/// created between the attempt's start and the attach. NEVER when two could be+/// ours, never without a baseline, and never a process other than the one COM+/// is attached to (when that is known).+pub fn solidworks_attribution(+    a: &Attempt,+    census: &[ProcInfo],+    attached_pid: Option<u32>,+    ready_at: u64,+) -> Result<(u32, u64, &'static str), String> {+    let sw: Vec<&ProcInfo> = census.iter().filter(|p| p.is("SLDWORKS.exe")).collect();+    if sw.is_empty() {+        return Err("no sldworks.exe in the census".into());+    }+    let must_be_attached = |p: &ProcInfo| -> Result<(), String> {+        match attached_pid {+            Some(want) if want != p.pid => Err(format!(+                "the SOLIDWORKS this launch started (pid {}) is not the one COM is attached to (pid {want})",+                p.pid+            )),+            _ => Ok(()),+        }+    };+    // 1. ANCESTRY. The walk records its refusal reasons in the slot the sign-in+    //    re-attribution reports; this probe must not overwrite them.+    let saved = reattribution_refused_because();+    let by_lineage: Vec<&ProcInfo> =+        sw.iter().copied().filter(|p| descends_from_our_chain(census, p.pid, a).is_some()).collect();+    *REATTR_REASON.lock().unwrap_or_else(|e| e.into_inner()) = saved;+    match by_lineage.as_slice() {+        [one] => {+            must_be_attached(one)?;+            let created = one.created_at.ok_or("its creation time is unknown")?;+            return Ok((one.pid, created, "ancestry"));+        }+        [] => {}+        many => {+            return Err(format!(+                "{} SOLIDWORKS processes descend from this launch's chain — never attributed when more than one could be ours",+                many.len()+            ))+        }+    }+    // 2. THE LAUNCH WINDOW.+    if !a.baseline_known {+        return Err("the census before the invoke could not be taken, so no process can be called new".into());+    }+    let new: Vec<&ProcInfo> = sw+        .iter()+        .copied()+        .filter(|p| !a.baseline_pids.contains(&p.pid))+        .filter(|p| p.created_at.map(|c| c >= a.started_at && c <= ready_at).unwrap_or(false))+        .collect();+    match new.as_slice() {+        [one] => {+            must_be_attached(one)?;+            Ok((one.pid, one.created_at.unwrap_or(0), "newProcessInLaunchWindow"))+        }+        [] => Err(format!(+            "no sldworks.exe that was not already running was created between the launch's start ({}) and the attach ({ready_at})",+            a.started_at+        )),+        many => Err(format!(+            "{} sldworks.exe processes were created in the launch window — never attributed when more than one could be ours",+            many.len()+        )),+    }+}++/// Apply [`solidworks_attribution`] to the current attempt — once, when it is+/// attached and makes no claim yet. Ledgered either way. Returns the claim.+pub fn attribute_on_attach(os: &dyn LaunchOs, attached_pid: Option<u32>) -> Option<(u32, u64)> {+    let a = attempt()?;+    if a.attributed_sldworks.is_some() {+        return a.attributed_sldworks;+    }+    if !(a.stage == Stage::Attached || a.outcome.as_deref() == Some("attached")) {+        return None;+    }+    let census = os.processes().ok()?;+    // THE WINDOW CLOSES AT THE ATTACH, not now: a SOLIDWORKS the user starts+    // tomorrow is "new" to a record from today, and must never inherit it.+    let ready_at = a.stage_at;+    match solidworks_attribution(&a, &census, attached_pid, ready_at) {+        Ok((pid, created, how)) => {+            mutate_store(|s| {+                if let Some(at) = s.attempt.as_mut() {+                    if at.id == a.id && at.attributed_sldworks.is_none() {+                        at.attributed_sldworks = Some((pid, created));+                        at.attributed_how = Some(how.into());+                    }+                }+            });+            crate::ledger::record(+                "solidworks_attributed",+                json!({"pid": pid, "createdAt": created, "how": how, "attachedPid": attached_pid,+                       "launchStartedAt": a.started_at, "readyAt": ready_at,+                       "launchRoot": a.attribution.as_ref().map(|r| r.pid)}),+            );+            Some((pid, created))+        }+        Err(why) => {+            static LAST: Mutex<Option<u64>> = Mutex::new(None);+            let mut last = LAST.lock().unwrap_or_else(|e| e.into_inner());+            if *last != Some(a.id) {+                *last = Some(a.id);+                crate::ledger::record("solidworks_attribution_refused", json!({"why": why, "attachedPid": attached_pid}));+            }+            None+        }+    }+}+ pub fn report(os: &dyn LaunchOs) -> Value {     report_with_attach(os, false) }@@ -4005,4 +4139,70 @@ mod tests {         assert_eq!(ROUTES[1], "powershell");         assert!(ROUTES.iter().all(|r| is_route(r)));     }++    /// THE MEASURED CONNECTED-EDITION LAUNCH (2026-10-14): the invoke reported+    /// its root (pid 53900, created 1792014529, `startProcessPassThru`);+    /// sldworks.exe came up as pid 70644, created 1792014533, with ppid 40520 —+    /// the launcher chain re-parents, so no parent walk reaches the root.+    fn measured_launch() -> (Attempt, Vec<ProcInfo>) {+        let mut a = unfinished_placeholder("SOLIDWORKS.lnk");+        a.started_at = 1792014520;+        a.invoked_at = 1792014528;+        a.stage = Stage::Attached;+        a.stage_at = 1792014611;+        a.baseline_known = true;+        a.baseline_pids = vec![6076, 90552];+        a.attribution = Some(Attribution { pid: 53900, created_at: Some(1792014529), source: "startProcessPassThru".into() });+        a.chain = vec![p(53900, "CATSTART.exe", Some(1792014529), None)];+        a.chain_last_seen = vec![(53900, 1792014529, 1792014540)];+        let census = vec![+            p(6076, "3DEXPERIENCELauncher.exe", Some(1791900000), None),+            p(40520, "SWXDesktopHelper.exe", Some(1792014531), None),+            p(70644, "SLDWORKS.exe", Some(1792014533), Some(40520)),+        ];+        (a, census)+    }++    #[test]+    fn the_launched_solidworks_is_attributed_by_the_launch_window_when_ancestry_cannot_reach_it() {+        let (a, census) = measured_launch();+        assert_eq!(+            solidworks_attribution(&a, &census, Some(70644), a.stage_at),+            Ok((70644, 1792014533, "newProcessInLaunchWindow")),+            "the one new sldworks.exe of the window, and the one COM is attached to"+        );+        // COM is attached to some OTHER process: no claim.+        assert!(solidworks_attribution(&a, &census, Some(99999), a.stage_at).unwrap_err().contains("not the one COM is attached to"));+        // TWO new SOLIDWORKS in the window: never a guess.+        let mut two = census.clone();+        two.push(p(70700, "SLDWORKS.exe", Some(1792014560), Some(40520)));+        assert!(solidworks_attribution(&a, &two, None, a.stage_at).unwrap_err().contains("2 sldworks.exe"));+        // One that was ALREADY running (in the baseline) is never new.+        let mut pre = a.clone();+        pre.baseline_pids.push(70644);+        assert!(solidworks_attribution(&pre, &census, Some(70644), pre.stage_at).is_err());+        // Created AFTER the attach (the user's own, later): outside the window.+        let mut late = census.clone();+        late[2] = p(70644, "SLDWORKS.exe", Some(1792014700), Some(40520));+        assert!(solidworks_attribution(&a, &late, Some(70644), a.stage_at).is_err());+        // Created BEFORE the attempt started: outside the window.+        late[2] = p(70644, "SLDWORKS.exe", Some(1792014000), Some(40520));+        assert!(solidworks_attribution(&a, &late, Some(70644), a.stage_at).is_err());+        // No baseline: nothing can be called new.+        let mut blind = a.clone();+        blind.baseline_known = false;+        assert!(solidworks_attribution(&blind, &census, Some(70644), blind.stage_at).unwrap_err().contains("could not be taken"));+    }++    #[test]+    fn ancestry_is_tried_first() {+        let (a, mut census) = measured_launch();+        // sldworks.exe whose parent IS our recorded root, alive with the same identity.+        census.push(p(53900, "CATSTART.exe", Some(1792014529), None));+        census[2] = p(70644, "SLDWORKS.exe", Some(1792014533), Some(53900));+        assert_eq!(solidworks_attribution(&a, &census, Some(70644), a.stage_at), Ok((70644, 1792014533, "ancestry")));+        // Two descend from our chain: refused, even though the window would pick one.+        census.push(p(70645, "SLDWORKS.exe", Some(1792014534), Some(53900)));+        assert!(solidworks_attribution(&a, &census, None, a.stage_at).unwrap_err().contains("descend from this launch's chain"));+    } }
src/memory.rs+85−36
@@ -492,6 +492,11 @@ pub fn session_facts(ctx: &crate::verbs::Ctx, os: &dyn LaunchOs) -> SessionFacts     }     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) } }@@ -581,45 +586,71 @@ pub fn guard(ctx: &crate::verbs::Ctx, command: &str, args: &Value) -> Result<Opt #[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 at all? (Cheap; the session facts are-/// read only when it does.)+/// 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.pressing() || sw_private_mb.map(|m| m > budget_mb).unwrap_or(false)+    p == Pressure::Critical || sw_private_mb.map(|m| m > budget_mb).unwrap_or(false) } -/// RECYCLE ONLY A SESSION THE BRIDGE STARTED, WITH NOTHING OPEN. Anything else —-/// the user's session, documents open, or a session nobody can identify —-/// STOPS the run instead.+/// 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 {-    if !needs_attention(p, sw_private_mb, budget_mb) {-        return Between::Continue;-    }-    let mut why: Vec<String> = Vec::new();-    if let Some(m) = sw_private_mb.filter(|m| *m > budget_mb) {-        why.push(format!("SOLIDWORKS holds {} (budget {})", gb(m), gb(budget_mb)));-    }-    if p.pressing() {-        why.push(format!("memory pressure is {}", p.name()));+    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) };     }-    let why = why.join("; ");-    let worth = sw_private_mb.map(|m| m > budget_mb || m >= RECYCLE_WORTH_MB).unwrap_or(false);-    match (bridge_started, open_docs) {-        (Some(true), Some(0)) if worth => Between::Recycle(why),-        (Some(true), Some(0)) => 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())-        )),-        (Some(true), Some(n)) => Between::Stop(format!(-            "{why}; the session is the bridge's but {n} document(s) are open, so it cannot be recycled safely"+    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)         )),-        (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")),+        _ => Between::Continue,     } } @@ -742,23 +773,41 @@ mod tests {     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(_)));-        assert!(matches!(between_checks(Pressure::High, Some(3_000), 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(_)));-        // Pressure that SOLIDWORKS is not causing: a restart would not relieve it.+        // ...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:?}"),             }         }-        for (bs, d) in [(Some(true), Some(1)), (Some(true), None), (Some(false), Some(0)), (None, Some(0)), (None, None)] {-            match between_checks(Pressure::High, Some(b + 1), b, bs, d) {-                Between::Stop(why) => assert!(why.contains("SOLIDWORKS holds") && why.contains("memory pressure is high"), "{why}"),-                other => panic!("{bs:?} {d:?}: {other:?} — only the bridge's own empty session is recycled"),+        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!(matches!(between_checks(Pressure::Unknown, Some(10), b, None, None), Between::Continue), "nothing to act on");+        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]
src/script.rs+47
@@ -232,6 +232,27 @@ impl Op {         }     } +    /// Does this op SELECT something in the document? SOLIDWORKS selects only in+    /// its ACTIVE document — a plane, a sketch, a face, an edge, a feature — and+    /// a document created invisible cannot be activated (MEASURED 2026-10-14:+    /// `new_part {visible:false}` then `sketch_rect` on Top Plane failed "could+    /// not activate 'Part1'"). So these ops cannot run in a hidden document.+    pub fn needs_selection(&self) -> bool {+        matches!(+            self,+            Op::Sketch { .. }+                | Op::Extrude(_)+                | Op::Cut(_)+                | Op::Fillet { .. }+                | Op::Chamfer { .. }+                | Op::Shell { .. }+                | Op::LinearPattern { .. }+                | Op::CircularPattern { .. }+                | Op::ClearPart+                | Op::SetAppearance { target: AppearanceTarget::FaceAt(_), .. }+        )+    }+     /// Does this op make a FEATURE in the tree (the one "last" refers to)?     pub fn makes_feature(&self) -> bool {         matches!(@@ -816,6 +837,32 @@ fn parse_step(step: &Value) -> Result<Op, String> {     }) } +/// The message for a modelling op aimed at a HIDDEN document.+pub const HIDDEN_DOCUMENT: &str = "SOLIDWORKS can select only in the active document, and a document \+     created invisible cannot be activated (measured 2026-10-14) — so it cannot be modelled";++/// A script that makes a HIDDEN part and then models it: the step that would+/// fail mid-script, named up front (0.10.7).+pub fn hidden_conflict(ops: &[Op]) -> Option<String> {+    let mut hidden = false;+    for (i, op) in ops.iter().enumerate() {+        match op {+            Op::NewPart { visible } | Op::NewAssembly { visible } => hidden = !*visible,+            _ if hidden && op.needs_selection() => {+                return Some(format!(+                    "step {} ({}) would model a part created with visible:false: {HIDDEN_DOCUMENT}. Leave \+                     visible at its default (true) — to keep a battery's memory flat, reuse one document \+                     (reuseDocument) instead",+                    i + 1,+                    op.name()+                ))+            }+            _ => {}+        }+    }+    None+}+ /// A sketch op must come before the feature that consumes it, a feature needs /// a document, and "last" needs a feature before it. Checked BEFORE the first /// COM call so a script that cannot work never half-runs on the user's live
src/selftest.rs+68−21
@@ -179,12 +179,13 @@ pub fn selftest(ctx: &Ctx, args: &Value) -> Value {         let memory_override = crate::memory::override_reason(args).ok().flatten();         let mut memory_log = MemoryLog::default();         let mut last_check: Option<&'static str> = None;-        // ONE HIDDEN DOCUMENT FOR THE BATTERY (0.10.6). Measured 2026-10-14:-        // each created-and-closed document cost SOLIDWORKS ~83 MB and ~125-        // handles for good. Every check that can runs in ONE hidden part the-        // bridge made, cleared between checks (clear_part); a check that needs-        // a fresh part (a material, a colour, a feature named by its number)-        // gets its own — hidden too.+        // ONE VISIBLE DOCUMENT FOR THE BATTERY (0.10.7). MEASURED 2026-10-14:+        // every NEW part costs SOLIDWORKS ~85 MB and ~125 handles until it+        // restarts, while 12 builds in ONE reused part left it flat (3260 ->+        // 3246 MB). Every check that can runs in one part the bridge made,+        // cleared between checks (clear_part); a check that needs a fresh part+        // (a material, a colour, a feature named by its number) gets its own.+        // VISIBLE: a hidden part cannot be modelled (measured).         let mut shared: Option<String> = None;         for check in battery() {             if !only.is_empty() && !only.iter().any(|o| o == check.id) {@@ -231,7 +232,7 @@ pub fn selftest(ctx: &Ctx, args: &Value) -> Value {         }          // THE VERBS on one scratch part made by swx_new_document.-        let mut new_doc = json!({"type": "part", "visible": false});+        let mut new_doc = json!({"type": "part"});         memory_log.documents_created += 1;         if let Some(r) = &memory_override {             new_doc["ignoreMemoryPressure"] = json!(true);@@ -284,6 +285,10 @@ pub fn selftest(ctx: &Ctx, args: &Value) -> Value {         if let Some(prev) = &active {             run.call(ctx, "re-activate the document that was active before", "swx_activate_document", json!({"name": prev}));         }+        // HYGIENE (0.10.7): release every COM reference the run held. MEASURED+        // 2026-10-14: a respawn freed ~210 MB of SOLIDWORKS's memory and never+        // touched SOLIDWORKS itself.+        memory_log.com_respawn = Some(respawn_com_host(ctx));         return finish_with(level, &sw, ready, true, started, run, None, memory_log.to_json(memory_override.as_deref()));     } @@ -295,6 +300,10 @@ pub fn selftest(ctx: &Ctx, args: &Value) -> Value { struct MemoryLog {     /// Documents the run created (the shared part counts once).     documents_created: usize,+    /// Checks that ran under HIGH commit pressure with SOLIDWORKS within budget.+    pressure_seen: Vec<Value>,+    /// The COM host respawned at the end of the run, and what it freed.+    com_respawn: Option<Value>,     recycled: Vec<Value>,     /// Checks run on under `ignoreMemoryPressure` where the run would have stopped.     overridden: Vec<Value>,@@ -306,6 +315,8 @@ impl MemoryLog {         let mut v = json!({             "recycled": self.recycled,             "documentsCreated": self.documents_created,+            "memoryPressureSeen": self.pressure_seen,+            "comRespawn": self.com_respawn,             "complete": self.stopped.is_none(),             "memory": crate::memory::report(crate::launchos::os().as_ref()),         });@@ -333,8 +344,15 @@ fn memory_between(     use crate::memory::Between;     let os = crate::launchos::os();     let s = crate::memory::sample(os.as_ref());-    let (p, _) = s.pressure();+    let (p, pressure_why) = s.pressure();     if !crate::memory::needs_attention(p, s.sw_private_mb, s.budget_mb) {+        // HIGH, with SOLIDWORKS within its budget: carry on, and report it+        // (0.10.7 — 0.10.6 stopped a run for memory other applications held).+        if let crate::memory::Between::Note(why) = crate::memory::between_checks(p, s.sw_private_mb, s.budget_mb, None, None) {+            if log.pressure_seen.len() < 50 {+                log.pressure_seen.push(json!({"beforeCheck": next, "pressure": p.name(), "why": why, "readings": pressure_why}));+            }+        }         return Ok(());     }     // The run's own shared part is closed first: it is the bridge's, and a@@ -343,6 +361,10 @@ fn memory_between(     let facts = crate::memory::session_facts(ctx, os.as_ref());     match crate::memory::between_checks(p, s.sw_private_mb, s.budget_mb, facts.bridge_started, facts.open_docs) {         Between::Continue => Ok(()),+        Between::Note(why) => {+            log.pressure_seen.push(json!({"beforeCheck": next, "pressure": p.name(), "why": why}));+            Ok(())+        }         Between::Recycle(why) => {             let r = recycle(ctx, last, next, &s, &why)?;             log.recycled.push(r);@@ -367,7 +389,7 @@ fn memory_between( /// Can this check run in the SHARED, cleared document? Not if it sets a /// material, colour or property (they outlive clear_part), names a feature by /// its number (numbering continues in a reused part), or checks the feature-/// list — those get a fresh, hidden part of their own.+/// list — those get a fresh part of their own. fn reusable(c: &Check) -> bool {     let Some(steps) = c.script.as_array() else { return false };     if steps.first().and_then(|s| s.get("op")).and_then(|o| o.as_str()) != Some("new_part") || c.expect.features.is_some() {@@ -383,9 +405,10 @@ fn reusable(c: &Check) -> bool {     }) } -/// The battery's shared part: hidden, the bridge's own.+/// The battery's shared part: VISIBLE (a hidden one cannot be modelled), the+/// bridge's own. fn make_shared_document(ctx: &Ctx, over: Option<&str>) -> Option<String> {-    let mut a = json!({"type": "part", "visible": false});+    let mut a = json!({"type": "part"});     if let Some(r) = over {         a["ignoreMemoryPressure"] = json!(true);         a["reason"] = json!(r);@@ -406,6 +429,29 @@ fn close_shared(ctx: &Ctx, shared: &mut Option<String>) {     } } +/// swx_com_respawn at the end of a write run, with SOLIDWORKS's memory read+/// either side of it.+fn respawn_com_host(ctx: &Ctx) -> Value {+    let before = crate::memory::sample(crate::launchos::os().as_ref());+    let r = crate::verbs::dispatch(+        ctx,+        "swx_com_respawn",+        &json!({"reason": "swx_selftest: release the COM references the run held (measured: about 210 MB)"}),+    );+    let after = crate::memory::sample(crate::launchos::os().as_ref());+    json!({+        "respawned": r.get("respawned").cloned().unwrap_or(json!(false)),+        "gracefulExit": r.get("gracefulExit").cloned().unwrap_or(Value::Null),+        "solidworksPrivateMbBefore": before.sw_private_mb,+        "solidworksPrivateMbAfter": after.sw_private_mb,+        "freedMb": match (before.sw_private_mb, after.sw_private_mb) {+            (Some(b), Some(a)) => json!(b as i64 - a as i64),+            _ => Value::Null,+        },+        "error": r.get("error").cloned().unwrap_or(Value::Null),+    })+}+ fn compact(v: &Value) -> Value {     json!({         "success": v.get("success"),@@ -483,6 +529,15 @@ fn finish(level: &str, sw: &Value, ready: bool, running: bool, started: Instant, #[allow(clippy::too_many_arguments)] fn finish_with(level: &str, sw: &Value, ready: bool, running: bool, started: Instant, run: Run, lead: Option<String>, extra: Value) -> Value {     let recycled = extra.get("recycled").and_then(|r| r.as_array()).map(|a| a.len()).unwrap_or(0);+    let pressured = extra.get("memoryPressureSeen").and_then(|r| r.as_array()).map(|a| a.len()).unwrap_or(0);+    let lead = match pressured {+        0 => lead,+        n => Some(format!(+            "MEMORY: committed memory was HIGH before {n} check(s), held by other applications — SOLIDWORKS stayed \+             within its budget, so the run carried on (see `memoryPressureSeen` and swx_status.memory). {}",+            lead.unwrap_or_default()+        )),+    };     let lead = match recycled {         0 => lead,         n => Some(format!(@@ -789,16 +844,8 @@ fn geometry_check(ctx: &Ctx, run: &mut Run, c: &Check, memory_override: Option<&     let mut sargs = match reuse {         // In the shared part: its new_part becomes clear_part.         Some(doc) => json!({"script": c.script, "reuseDocument": doc}),-        // A part of its own, HIDDEN: the self-test makes it for itself.-        None => {-            let mut script = c.script.clone();-            if let Some(first) = script.as_array_mut().and_then(|a| a.first_mut()) {-                if first.get("op").and_then(|o| o.as_str()) == Some("new_part") {-                    first["visible"] = json!(false);-                }-            }-            json!({"script": script})-        }+        // A part of its own (visible: a hidden part cannot be modelled).+        None => json!({"script": c.script}),     };     if let Some(r) = memory_override {         sargs["ignoreMemoryPressure"] = json!(true);
src/verbs.rs+14−3
@@ -596,6 +596,11 @@ fn swx_status(ctx: &Ctx, _args: &Value) -> Value {     // sldworks.exe on the machine (a zombie from a crash, or the user's own     // session beside ours) the one a caller MEANS is the one COM answers for.     crate::launch::note_attached_solidworks(com_pid);+    // WHICH SOLIDWORKS THE LAST LAUNCH STARTED (0.10.7), decided once it is+    // attached — the claim a recycle and the quit gate rely on.+    if com_pid.is_some() {+        crate::launch::attribute_on_attach(os.as_ref(), com_pid);+    }     let health = crate::launch::host_app_health(os.as_ref(), open_docs, com_timed_out, com_pid);     let mut solidworks = detection.to_json();     if let (Value::Object(map), Value::Object(h)) = (&mut solidworks, &health) {@@ -765,6 +770,8 @@ fn swx_start(ctx: &Ctx, args: &Value) -> Value {         // clicking their Start Menu while we watched — the attempt is over and         // the route that was in flight is the one that worked.         crate::launch::mark_attached();+        let pid = r.get("processId").and_then(|v| v.as_u64()).map(|p| p as u32);+        crate::launch::attribute_on_attach(crate::launchos::os().as_ref(), pid);         return ok_merge(             r,             json!({@@ -966,6 +973,12 @@ fn start_via_shortcut(     if attached {         crate::ledger::record_time("swx_start", started.elapsed().as_secs_f64());         crate::launch::mark_attached();+        let pid = ctx+            .call("app_state", json!({}), 20)+            .ok()+            .and_then(|r| r.get("processId").and_then(|v| v.as_u64()))+            .map(|p| p as u32);+        crate::launch::attribute_on_attach(os.as_ref(), pid);     }     let version = if attached {         ctx.call("start", json!({"visible": arg_bool(args, "visible", true), "launch": false}), 30)@@ -2002,9 +2015,7 @@ fn prime_session(         Ok(_) => return Err("a dialog is up".into()),         Err(e) => return Err(format!("the window census could not be read: {e}")),     }-    // HIDDEN (0.10.6): a document the bridge makes purely for itself gets no-    // window — each created-and-closed one cost SOLIDWORKS ~83 MB (measured).-    let made = crate::verbs3::new_document(ctx, &json!({"type": "part", "visible": false}));+    let made = crate::verbs3::new_document(ctx, &json!({"type": "part"}));     if made.get("success").and_then(|v| v.as_bool()) != Some(true) {         return Err(format!("the scratch part could not be created: {}", made.get("error").cloned().unwrap_or(Value::Null)));     }
src/verbs3.rs+35−11
@@ -3924,15 +3924,14 @@ pub fn new_document(ctx: &Ctx, args: &Value) -> Value {                 "windowEtiquette": "background",                 "progress": job.finished(),                 "_hint": if !visible {-                    "A NEW HIDDEN document: it has no window, so it costs SOLIDWORKS no graphics \-                     context (the leading suspect for the ~83 MB each created-and-closed document \-                     kept, measured 2026-10-14). Every object-model verb works on it — \-                     swx_run_script, swx_inspect_bodies, swx_mass_properties, the exports, \-                     swx_save_as. SOLIDWORKS documents that a document created hidden cannot be \-                     shown later; to see it, save it and reopen it (swx_save_as, then swx_open). \-                     The hidden-creation setting was put back for the next document \-                     (`visibility.documentVisibleRestored`). UNMEASURED live: whether a hidden \-                     document avoids that cost."+                    "A NEW HIDDEN document, with no window. It CANNOT BE MODELLED: SOLIDWORKS \+                     selects only in the active document and a document created invisible cannot \+                     be activated (measured 2026-10-14), so swx_run_script refuses modelling steps \+                     on it (document_hidden). Saving and exporting work. SOLIDWORKS documents that \+                     it cannot be shown later either; save it and reopen it to see it. Hiding is \+                     NOT a memory fix: to keep a battery flat, reuse one VISIBLE document \+                     (swx_run_script reuseDocument). The hidden-creation setting was put back \+                     (`visibility.documentVisibleRestored`)."                 } else if titled_only {                     "A NEW document is open and active, with a TITLE and NO PATH — so it can only \                      be named by that title, and SOLIDWORKS reuses Part1/Part2 every session. If a \@@ -4636,6 +4635,15 @@ pub fn run_script(ctx: &Ctx, args: &Value) -> Value {         }         let _ = r;     }+    if let Some(m) = crate::script::hidden_conflict(&ops) {+        return err_code(+            "document_hidden",+            m,+            "Nothing was run. A hidden document (visible:false) can be created, saved and exported, but \+             not modelled. Scripts default to a visible part; for a battery that must not grow \+             SOLIDWORKS's memory, create ONE part and pass its path as reuseDocument.",+        );+    }     let makes_its_own_doc =         matches!(ops.first(), Some(crate::script::Op::NewPart { .. } | crate::script::Op::NewAssembly { .. }));     let has_doc = ctx@@ -4670,7 +4678,18 @@ pub fn run_script(ctx: &Ctx, args: &Value) -> Value {         };         let target = match resolve_document(ctx, &target_args, "swx_run_script") {             Ok(t) => t,-            Err(e) => return e,+            Err(mut e) => {+                // SAY WHICH ARGUMENT NAMED IT (measured 2026-10-14: a reused part+                // that could not be found read "needs a document").+                if let (Some(r), Value::Object(m)) = (&reuse, &mut e) {+                    let inner = m.get("error").and_then(|v| v.as_str()).unwrap_or("").to_string();+                    m.insert(+                        "error".into(),+                        json!(format!("the document named by reuseDocument ('{r}') is not open, or not visible: {inner}")),+                    );+                }+                return e;+            }         };         if reuse.is_some() && !target.owned {             return err_code(@@ -4729,7 +4748,12 @@ pub fn run_script(ctx: &Ctx, args: &Value) -> Value {                 crate::ledger::record("document_created", json!({"title": t, "via": "run_script (before a failed step)"}));             }             let created = e.created.clone();+            let hidden_reuse = reuse.is_some() && e.code.as_deref() == Some("document_hidden");             let mut out = sidecar_error(e);+            if let (true, Some(r), Value::Object(m)) = (hidden_reuse, &reuse, &mut out) {+                let inner = m.get("error").and_then(|v| v.as_str()).unwrap_or("").to_string();+                m.insert("error".into(), json!(format!("the document named by reuseDocument ('{r}') is hidden: {inner}")));+            }             if let (Value::Object(m), Some(c)) = (&mut out, created) {                 m.insert("createdBeforeFailing".into(), c);             }@@ -4859,7 +4883,7 @@ fn script_help(message: impl Into<String>) -> Value {         map.insert(             "opArgs".into(),             json!({-                "new_part": {"visible": "true (default) | false — created HIDDEN, with no window (ISldWorks::DocumentVisible); every object-model op works on it, and SOLIDWORKS cannot show it later (save and reopen to see it)"},+                "new_part": {"visible": "true (default) | false — created HIDDEN (ISldWorks::DocumentVisible). A hidden part CANNOT BE MODELLED (SOLIDWORKS selects only in the active document, and a hidden one cannot be activated — measured): a script that models it is refused document_hidden. Use reuseDocument, not hiding, to keep memory flat"},                 "new_assembly": {"visible": "as new_part"},                 "clear_part": {"(none)": "deletes EVERY feature after the origin of a part the BRIDGE created — never anyone else's; N checks then cost one document"},                 "sketch": {"plane": "Front Plane|Top Plane|Right Plane|<any plane name> | {offsetFrom, offsetMm}", "face": "{atPointMm:[x,y,z]} instead of plane", "entities": "[{rect:{widthMm, heightMm, xMm, yMm, anchor:corner|center, centerMm:[x,y]}}, {circle:{radiusMm|diameterMm, xMm, yMm | centerMm}}] — ALL in one sketch: concentric circles make a ring, a rect with circles inside makes a plate with holes"},
tests/memory.rs+90
@@ -341,3 +341,93 @@ fn a_recycle_that_cannot_quit_stops_the_run_and_says_where() {     );     check!(b, "...and nothing was terminated", m.nothing_terminated(), "terminated.jsonl exists"); }++/// ATTRIBUTION ON THE CONNECTED EDITION (0.10.7), with the measured numbers:+/// the launch's root was pid 53900 (created 1792014529, startProcessPassThru),+/// sldworks.exe came up as pid 70644 (created 1792014533) with ppid 40520 — the+/// chain re-parents, so no parent walk reaches the root, and 0.10.6 left+/// `attributedSolidworks` null and `bridgeStarted:false` for a session it had+/// launched four seconds earlier. The ONE new sldworks.exe of the launch window+/// is now the bridge's.+#[test]+fn a_launch_whose_chain_reparents_still_owns_the_solidworks_it_started() {+    let m = Machine::new("mem-attrib", incident(4300), 10854);+    std::fs::write(m.dir.join("sldworks-created"), "1792014533").unwrap();+    std::fs::write(m.dir.join("sldworks-parent"), "40520").unwrap();+    let scratch = TempDir::new("mem-attrib-scratch");+    std::fs::write(scratch.join("mock-solidworks-session"), "70644").unwrap();+    std::fs::write(+        scratch.join("launch-state.json"),+        json!({+            "attempt": {+                "id": 7, "startedAt": 1792014520u64, "shortcut": "SOLIDWORKS.lnk", "route": "startProcess",+                "invokedAt": 1792014528u64, "stage": "attached", "stageAt": 1792014611u64, "finished": true,+                "outcome": "attached", "runBeginSeen": true, "baselineKnown": true, "baselinePids": [6076, 90552],+                "attribution": {"pid": 53900, "createdAt": 1792014529u64, "source": "startProcessPassThru"},+            },+            "preferredRoute": "startProcess", "consecutiveFailures": 0,+        })+        .to_string(),+    )+    .unwrap();+    let b = Bridge::start_in(scratch, &m.env());+    b.call("swx_start", json!({}));+    let st = b.call("swx_status", json!({}));+    let claim = st.pointer("/launch/attributedSolidworks").cloned().unwrap_or(Value::Null);+    check!(+        b,+        "swx_status: the launch now names its SOLIDWORKS — the one new sldworks.exe of the launch window",+        claim == json!({"pid": 70644, "createdAt": 1792014533u64, "how": "newProcessInLaunchWindow"}),+        s(&st["launch"])+    );+    check!(+        b,+        "...ledgered with how it was decided",+        ledger(&b).iter().any(|e| e["action"] == "solidworks_attributed" && e["detail"]["how"] == "newProcessInLaunchWindow"),+        s(&json!(ledger(&b)))+    );+    let r = b.call("swx_new_document", json!({"type": "part"}));+    check!(+        b,+        "so the memory guard knows the session is the bridge's (recyclable when empty)",+        r["errorCode"] == "low_commit_memory" && r["solidworksSession"]["bridgeStarted"] == true+            && r["remedy"].as_str().unwrap_or("").contains("swx_stop"),+        s(&r)+    );+}++/// THE WINDOW CLOSES AT THE ATTACH: a SOLIDWORKS created AFTER the launch+/// attached (the user's own, started later) is never the launch's, however new+/// it is to the record.+#[test]+fn a_solidworks_started_after_the_attach_is_never_claimed() {+    let m = Machine::new("mem-attrib-late", incident(40000), 900);+    std::fs::write(m.dir.join("sldworks-created"), "1792014700").unwrap();+    std::fs::write(m.dir.join("sldworks-parent"), "40520").unwrap();+    let scratch = TempDir::new("mem-attrib-late-scratch");+    std::fs::write(scratch.join("mock-solidworks-session"), "70644").unwrap();+    std::fs::write(+        scratch.join("launch-state.json"),+        json!({+            "attempt": {+                "id": 8, "startedAt": 1792014520u64, "shortcut": "SOLIDWORKS.lnk", "route": "startProcess",+                "invokedAt": 1792014528u64, "stage": "attached", "stageAt": 1792014611u64, "finished": true,+                "outcome": "attached", "runBeginSeen": true, "baselineKnown": true, "baselinePids": [6076],+                "attribution": {"pid": 53900, "createdAt": 1792014529u64, "source": "startProcessPassThru"},+            },+            "preferredRoute": "startProcess", "consecutiveFailures": 0,+        })+        .to_string(),+    )+    .unwrap();+    let b = Bridge::start_in(scratch, &m.env());+    b.call("swx_start", json!({}));+    let st = b.call("swx_status", json!({}));+    check!(+        b,+        "swx_status: a sldworks.exe created after the attach is NOT claimed, and the refusal is ledgered",+        st.pointer("/launch/attributedSolidworks").map(|v| v.is_null()).unwrap_or(true)+            && ledger(&b).iter().any(|e| e["action"] == "solidworks_attribution_refused"),+        s(&st["launch"])+    );+}
tests/quit.rs+8
@@ -618,6 +618,14 @@ fn a_never_had_a_document_session_is_primed_and_then_quits() {         s(&r)     );     check!(b, "swx_stop: the prime is in the ledger", ledger_has(&b, "quit_prime"), String::new());+    // 0.10.7: VISIBLE — a hidden document is not what was measured to prime a+    // session, and 0.10.6's hidden prime was reverted with every other default.+    check!(+        b,+        "swx_stop: the scratch part that primed it was created VISIBLE (no DocumentVisible call)",+        !b.scratch.join("document-visible.jsonl").exists(),+        String::new()+    ); }  /// Priming needs the user's authorization: under the write opt-in alone, a
tests/visibility.rs+226−163
@@ -6,13 +6,13 @@ //! handles 2845 -> 4539, +~125 per part; GetDocumentCount back at 0 every time. //! So every created-and-closed document cost ~83 MB and ~125 handles for good. //!-//! This release: documents the bridge makes for itself are created HIDDEN-//! (ISldWorks::DocumentVisible bracketed around NewDocument — a SESSION-//! setting, put back on every path); a battery reuses ONE part (clear_part);-//! and `swx_com_respawn` settles whether the bridge itself pins anything.-//!-//! All in the SIMULATOR: whether hiding or reuse actually stops the growth is-//! UNMEASURED until it runs live.+//! 0.10.6 tried HIDDEN documents (ISldWorks::DocumentVisible around+//! NewDocument) and ONE reused part (clear_part). MEASURED LIVE (0.10.7's+//! brief): a hidden document cannot be modelled — SOLIDWORKS selects only in+//! the active document and a hidden one cannot be activated — while twelve+//! builds in one reused, VISIBLE part left SOLIDWORKS flat. So 0.10.7 makes+//! everything visible by default, refuses to model a hidden document up front+//! (`document_hidden`), and runs batteries in one reused part.  mod common; @@ -55,67 +55,6 @@ fn box_script(first: Value) -> Value {            {"op": "extrude", "depthMm": 10}]) } -#[test]-fn a_hidden_document_is_created_with_the_session_setting_bracketed_and_put_back() {-    let (b, _m) = boot("vis-hidden", &[]);-    b.call("swx_start", json!({}));-    let r = b.call("swx_new_document", json!({"type": "part", "visible": false}));-    check!(-        b,-        "swx_new_document visible:false: created hidden, DocumentVisible put back, a scratch file of its own",-        r["success"] == true && r["visibility"]["requested"] == false && r["visibility"]["documentVisibleRestored"] == true-            && r["visibility"]["visibleReadBack"] == false && r["visibility"]["scratchSaved"] == true-            && r["documentIdentity"]["path"].as_str().unwrap_or("").ends_with(".SLDPRT"),-        s(&r)-    );-    let calls = visible_calls(&b);-    check!(-        b,-        "DocumentVisible(false), then DocumentVisible(true) — in that order, for parts",-        calls.len() == 2 && calls[0]["visible"] == false && calls[1]["visible"] == true && calls[0]["type"] == "part",-        s(&json!(calls))-    );-    check!(-        b,-        "the toggle and the hidden creation are ledgered",-        ledger(&b).iter().any(|e| e["action"] == "document_visible_toggled")-            && ledger(&b).iter().any(|e| e["action"] == "document_created" && e["detail"]["visible"] == false),-        s(&json!(ledger(&b)))-    );-    // EVERY OBJECT-MODEL VERB WORKS ON IT.-    let path = r["documentIdentity"]["path"].as_str().unwrap_or("").to_string();-    let built = b.call("swx_run_script", json!({"path": path, "script": [-        {"op": "sketch_rect", "plane": "Top Plane", "widthMm": 20, "heightMm": 20, "anchor": "center"},-        {"op": "extrude", "depthMm": 10}]}));-    let ib = b.call("swx_inspect_bodies", json!({"path": path}));-    let mp = b.call("swx_mass_properties", json!({"path": path}));-    let out = b.scratch.join("hidden.step");-    let ex = b.call("swx_export_step", json!({"path": path, "outputPath": out.to_string_lossy()}));-    check!(-        b,-        "a script, swx_inspect_bodies, swx_mass_properties and swx_export_step all work on the hidden part",-        built["success"] == true && ib["bodyCount"] == 1 && ib["bodies"][0]["volumeMm3"].as_f64().map(|v| (v - 4000.0).abs() < 1e-6) == Some(true)-            && mp["success"] == true && ex["success"] == true,-        format!("{}\n{}\n{}\n{}", s(&built), s(&ib), s(&mp), s(&ex))-    );-    let shown = b.call("swx_show_part", json!({"path": path}));-    check!(-        b,-        "swx_show_part on it: attempted, read back, and SAID to stay hidden (as SOLIDWORKS documents it)",-        shown["visibility"]["wasVisible"] == false && shown["visibility"]["madeVisible"] == false-            && shown["visibility"]["note"].as_str().unwrap_or("").contains("cannot be made visible"),-        s(&shown)-    );-    // THE DEFAULT IS UNCHANGED: a user's document is visible, and nothing toggles.-    let v = b.call("swx_new_document", json!({"type": "part"}));-    check!(-        b,-        "swx_new_document with no visible: a visible document, and no DocumentVisible call at all",-        v["success"] == true && v.get("visibility").is_none() && visible_calls(&b).len() == 2,-        s(&v)-    );-}- #[test] fn a_hidden_creation_that_fails_still_puts_the_setting_back() {     let (b, _m) = boot("vis-fail", &[]);@@ -168,70 +107,6 @@ fn a_setting_the_host_could_not_put_back_is_retried_and_otherwise_shouted() {     ); } -#[test]-fn a_users_script_stays_visible_by_default_and_new_part_can_hide() {-    let (b, _m) = boot("vis-script", &[]);-    b.call("swx_start", json!({}));-    let r = b.call("swx_run_script", json!({"script": box_script(json!({"op": "new_part"}))}));-    check!(-        b,-        "new_part with no visible: visible, and no DocumentVisible call (Michael wants to see his part)",-        r["success"] == true && r["steps"][0]["result"].get("visibility").is_none() && visible_calls(&b).is_empty(),-        s(&r)-    );-    let r = b.call("swx_run_script", json!({"script": box_script(json!({"op": "new_part", "visible": false}))}));-    check!(-        b,-        "new_part visible:false: hidden, the setting put back, and its variant is UNMEASURED live",-        r["success"] == true && r["steps"][0]["result"]["visibility"]["documentVisibleRestored"] == true-            && r["steps"][0]["result"]["proven"]["variants"] == json!(["new_part.invisible"])-            && r["steps"][0]["result"]["proven"]["live"] == false,-        s(&r)-    );-    let bad = b.call("swx_run_script", json!({"script": box_script(json!({"op": "new_part", "visible": "no"}))}));-    check!(b, "visible must be a boolean", bad["errorCode"] == "bad_script", s(&bad));-}--#[test]-fn a_battery_reuses_one_part_and_clear_part_never_touches_anyone_elses() {-    let (b, _m) = boot("vis-reuse", &[("SWX_ALLOW_USER_WRITES", "1")]);-    b.call("swx_start", json!({}));-    let made = b.call("swx_new_document", json!({"type": "part", "visible": false}));-    let p = made["documentIdentity"]["path"].as_str().unwrap_or("").to_string();-    let first = b.call("swx_run_script", json!({"reuseDocument": p, "script": box_script(json!({"op": "new_part"}))}));-    let docs = doc_count(&b);-    let second = b.call("swx_run_script", json!({"reuseDocument": p, "script": [-        {"op": "new_part"},-        {"op": "sketch_circle", "plane": "Top Plane", "radiusMm": 5},-        {"op": "extrude", "depthMm": 10}]}));-    let ib = b.call("swx_inspect_bodies", json!({"path": p}));-    check!(-        b,-        "reuseDocument: the leading new_part became clear_part; the box is gone, only the cylinder is left",-        first["success"] == true && second["success"] == true && second["steps"][0]["op"] == "clear_part"-            && second["steps"][0]["result"]["featuresDeleted"] == 1-            && ib["bodyCount"] == 1-            && ib["bodies"][0]["volumeMm3"].as_f64().map(|v| (v - std::f64::consts::PI * 250.0).abs() < 1e-3) == Some(true),-        format!("{}\n{}", s(&second), s(&ib))-    );-    check!(b, "...and no document was created by the reused runs", doc_count(&b) == docs, format!("{docs} -> {}", doc_count(&b)));-    // A USER'S part.-    let user = "C:\\mock\\plate.sldprt";-    b.call("swx_open", json!({"path": user}));-    let r = b.call("swx_run_script", json!({"path": user, "script": [{"op": "clear_part"}],-        "allowUserWrites": true, "confirm": true, "reason": "the user asked to clear it"}));-    check!(-        b,-        "clear_part on a part the bridge did not create: refused by the server, write grant or not",-        r["errorCode"] == "clear_part_not_owned" && r["error"].as_str().unwrap_or("").contains("is not a part this bridge created"),-        s(&r)-    );-    let r = b.call("swx_run_script", json!({"reuseDocument": user, "script": box_script(json!({"op": "new_part"}))}));-    check!(b, "reuseDocument naming the user's part: refused", r["errorCode"] == "reuse_not_owned", s(&r));-    let r = b.call("swx_run_script", json!({"reuseDocument": p, "script": [{"op": "new_part"}, {"op": "new_part"}]}));-    check!(b, "with reuseDocument, new_part only as the first step", r["errorCode"] == "bad_script", s(&r));-}- #[test] fn com_respawn_replaces_only_the_bridges_host_and_never_calls_solidworks() {     let (b, _m) = boot("vis-respawn", &[("SWX_MOCK_SLOW_MS", "2500")]);@@ -277,37 +152,6 @@ fn com_respawn_replaces_only_the_bridges_host_and_never_calls_solidworks() {     ); } -#[test]-fn the_selftest_builds_hidden_and_reuses_one_document() {-    let (b, _m) = boot("vis-selftest", &[]);-    b.call("swx_start", json!({}));-    let r = b.call("swx_selftest", json!({"level": "write", "only": ["T01", "T03", "T15", "T17"]}));-    let rows = r["results"].as_array().cloned().unwrap_or_default();-    let row = |id: &str| rows.iter().find(|x| x["name"].as_str().unwrap_or("").starts_with(id)).cloned().unwrap_or(Value::Null);-    check!(-        b,-        "every check passes; T01 and T03 ran in the shared part, T15 (a colour) and T17 (a feature by name) in their own",-        r["summary"]["failed"] == 0 && row("T01")["pass"] == true && row("T03")["pass"] == true-            && row("T01")["reusedDocument"] == true && row("T03")["reusedDocument"] == true-            && row("T15")["reusedDocument"] == false && row("T17")["reusedDocument"] == false,-        s(&r)-    );-    check!(-        b,-        "documentsCreated: one shared part, two of their own, one scratch part for the verbs — 4, not 5",-        r["documentsCreated"] == 4,-        s(&r["documentsCreated"])-    );-    let calls = visible_calls(&b);-    let hidden = calls.iter().filter(|c| c["visible"] == false).count();-    check!(-        b,-        "every one of them was created HIDDEN, and the setting was put back after each",-        hidden == 4 && calls.len() == 8 && calls.chunks(2).all(|p| p[0]["visible"] == false && p[1]["visible"] == true),-        s(&json!(calls))-    );-}- /// THE MEASURED GROWTH, SIMULATED: every created document leaves SOLIDWORKS /// 83 MB heavier (`growthPerDocumentMB`). A reused check creates nothing, so /// it costs nothing; and when the budget is reached mid-battery, the run@@ -372,3 +216,222 @@ fn reuse_keeps_solidworks_flat_and_the_shared_part_is_closed_before_a_recycle()     );     check!(b, "nothing was terminated", !machine.join("terminated.jsonl").exists(), "terminated.jsonl"); }++#[test]+fn a_hidden_document_is_bracketed_but_cannot_be_modelled() {+    let (b, _m) = boot("vis-hidden", &[]);+    b.call("swx_start", json!({}));+    let r = b.call("swx_new_document", json!({"type": "part", "visible": false}));+    check!(+        b,+        "swx_new_document visible:false: created hidden, DocumentVisible put back, a scratch file of its own",+        r["success"] == true && r["visibility"]["documentVisibleRestored"] == true+            && r["visibility"]["visibleReadBack"] == false && r["visibility"]["scratchSaved"] == true+            && r["_hint"].as_str().unwrap_or("").contains("CANNOT BE MODELLED"),+        s(&r)+    );+    let calls = visible_calls(&b);+    check!(+        b,+        "DocumentVisible(false), then DocumentVisible(true), and ledgered",+        calls.len() == 2 && calls[0]["visible"] == false && calls[1]["visible"] == true+            && ledger(&b).iter().any(|e| e["action"] == "document_visible_toggled"),+        s(&json!(calls))+    );+    let path = r["documentIdentity"]["path"].as_str().unwrap_or("").to_string();+    let built = b.call("swx_run_script", json!({"path": path, "script": [+        {"op": "sketch_rect", "plane": "Top Plane", "widthMm": 20, "heightMm": 20, "anchor": "center"},+        {"op": "extrude", "depthMm": 10}]}));+    let ib = b.call("swx_inspect_bodies", json!({"path": path}));+    check!(+        b,+        "modelling it is refused UP FRONT (document_hidden), naming why — nothing was built",+        built["errorCode"] == "document_hidden"+            && built["error"].as_str().unwrap_or("").contains("cannot be activated")+            && ib["bodyCount"] == 0,+        format!("{}\n{}", s(&built), s(&ib))+    );+    let prop = b.call("swx_run_script", json!({"path": path, "script": [{"op": "set_prop", "name": "Kind", "value": "hidden"}]}));+    let out = b.scratch.join("hidden.step");+    let ex = b.call("swx_export_step", json!({"path": path, "outputPath": out.to_string_lossy()}));+    check!(b, "what selects nothing still works on it: a property, an export", prop["success"] == true && ex["success"] == true, format!("{}\n{}", s(&prop), s(&ex)));+    let shown = b.call("swx_show_part", json!({"path": path}));+    check!(+        b,+        "swx_show_part on it: attempted, read back, and SAID to stay hidden",+        shown["visibility"]["madeVisible"] == false && shown["visibility"]["note"].as_str().unwrap_or("").contains("cannot be made visible"),+        s(&shown)+    );+    let v = b.call("swx_new_document", json!({"type": "part"}));+    check!(+        b,+        "swx_new_document with no visible: a visible document, and no DocumentVisible call at all",+        v["success"] == true && v.get("visibility").is_none() && visible_calls(&b).len() == 2,+        s(&v)+    );+}++#[test]+fn a_script_is_visible_by_default_and_a_hidden_part_is_never_modelled() {+    let (b, _m) = boot("vis-script", &[]);+    b.call("swx_start", json!({}));+    let r = b.call("swx_run_script", json!({"script": box_script(json!({"op": "new_part"}))}));+    check!(+        b,+        "new_part with no visible: visible, and no DocumentVisible call (Michael wants to see his part)",+        r["success"] == true && r["steps"][0]["result"].get("visibility").is_none() && visible_calls(&b).is_empty(),+        s(&r)+    );+    let docs = doc_count(&b);+    let r = b.call("swx_run_script", json!({"script": box_script(json!({"op": "new_part", "visible": false}))}));+    check!(+        b,+        "new_part visible:false then a sketch: refused document_hidden BEFORE anything ran — no part made, no setting touched",+        r["errorCode"] == "document_hidden" && r["error"].as_str().unwrap_or("").contains("step 2 (sketch_rect)")+            && r["_hint"].as_str().unwrap_or("").starts_with("Nothing was run")+            && doc_count(&b) == docs && visible_calls(&b).is_empty(),+        s(&r)+    );+    let r = b.call("swx_run_script", json!({"script": [{"op": "new_part", "visible": false}, {"op": "set_prop", "name": "Kind", "value": "hidden"}]}));+    check!(+        b,+        "a hidden part that is only given a property: allowed, and its variant is unmeasured live",+        r["success"] == true && r["steps"][0]["result"]["proven"]["variants"] == json!(["new_part.invisible"])+            && r["steps"][0]["result"]["proven"]["live"] == false,+        s(&r)+    );+    let bad = b.call("swx_run_script", json!({"script": box_script(json!({"op": "new_part", "visible": "no"}))}));+    check!(b, "visible must be a boolean", bad["errorCode"] == "bad_script", s(&bad));+}++#[test]+fn a_battery_reuses_one_visible_part_and_clear_part_never_touches_anyone_elses() {+    let (b, _m) = boot("vis-reuse", &[("SWX_ALLOW_USER_WRITES", "1")]);+    // The simulated release is the one clear_part was measured on.+    std::fs::write(b.scratch.join("mock-revision"), "34.3.0").unwrap();+    b.call("swx_start", json!({}));+    let made = b.call("swx_new_document", json!({"type": "part"}));+    let p = made["documentIdentity"]["path"].as_str().unwrap_or("").to_string();+    let first = b.call("swx_run_script", json!({"reuseDocument": p, "script": box_script(json!({"op": "new_part"}))}));+    let docs = doc_count(&b);+    let second = b.call("swx_run_script", json!({"reuseDocument": p, "script": [+        {"op": "new_part"},+        {"op": "sketch_circle", "plane": "Top Plane", "radiusMm": 5},+        {"op": "extrude", "depthMm": 10}]}));+    let ib = b.call("swx_inspect_bodies", json!({"path": p}));+    check!(+        b,+        "reuseDocument: the leading new_part became clear_part; the box is gone, only the cylinder is left",+        first["success"] == true && second["success"] == true && second["steps"][0]["op"] == "clear_part"+            && second["steps"][0]["result"]["featuresDeleted"] == 1+            && second["steps"][0]["result"]["proven"]["live"] == true+            && ib["bodyCount"] == 1+            && ib["bodies"][0]["volumeMm3"].as_f64().map(|v| (v - std::f64::consts::PI * 250.0).abs() < 1e-3) == Some(true),+        format!("{}\n{}", s(&second), s(&ib))+    );+    check!(b, "...and no document was created by the reused runs", doc_count(&b) == docs, format!("{docs} -> {}", doc_count(&b)));+    // A USER'S part.+    let user = "C:\\mock\\plate.sldprt";+    b.call("swx_open", json!({"path": user}));+    let r = b.call("swx_run_script", json!({"path": user, "script": [{"op": "clear_part"}],+        "allowUserWrites": true, "confirm": true, "reason": "the user asked to clear it"}));+    check!(+        b,+        "clear_part on a part the bridge did not create: refused by the server, write grant or not",+        r["errorCode"] == "clear_part_not_owned" && r["error"].as_str().unwrap_or("").contains("is not a part this bridge created"),+        s(&r)+    );+    let r = b.call("swx_run_script", json!({"reuseDocument": user, "script": box_script(json!({"op": "new_part"}))}));+    check!(b, "reuseDocument naming the user's part: refused", r["errorCode"] == "reuse_not_owned", s(&r));+    let r = b.call("swx_run_script", json!({"reuseDocument": p, "script": [{"op": "new_part"}, {"op": "new_part"}]}));+    check!(b, "with reuseDocument, new_part only as the first step", r["errorCode"] == "bad_script", s(&r));+    // MEASURED (D): a reuseDocument that did not resolve read "needs a document".+    let gone = b.call("swx_run_script", json!({"reuseDocument": "C:\\nowhere\\gone.SLDPRT", "script": box_script(json!({"op": "new_part"}))}));+    check!(+        b,+        "a reuseDocument that is not open: the error names reuseDocument",+        gone["success"] == false && gone["error"].as_str().unwrap_or("").contains("the document named by reuseDocument"),+        s(&gone)+    );+    let hidden = b.call("swx_new_document", json!({"type": "part", "visible": false}));+    let hp = hidden["documentIdentity"]["path"].as_str().unwrap_or("").to_string();+    let r = b.call("swx_run_script", json!({"reuseDocument": hp, "script": box_script(json!({"op": "new_part"}))}));+    check!(+        b,+        "a reuseDocument that is HIDDEN: document_hidden, and the error names reuseDocument",+        r["errorCode"] == "document_hidden" && r["error"].as_str().unwrap_or("").contains("the document named by reuseDocument")+            && r["error"].as_str().unwrap_or("").contains("is hidden"),+        s(&r)+    );+}++#[test]+fn the_selftest_reuses_one_visible_document_and_releases_the_com_host_at_the_end() {+    let (b, _m) = boot("vis-selftest", &[]);+    b.call("swx_start", json!({}));+    let r = b.call("swx_selftest", json!({"level": "write", "only": ["T01", "T03", "T15", "T17"]}));+    let rows = r["results"].as_array().cloned().unwrap_or_default();+    let row = |id: &str| rows.iter().find(|x| x["name"].as_str().unwrap_or("").starts_with(id)).cloned().unwrap_or(Value::Null);+    check!(+        b,+        "every check passes; T01 and T03 in the shared part, T15 (a colour) and T17 (a feature by name) in their own",+        r["summary"]["failed"] == 0 && row("T01")["pass"] == true && row("T03")["pass"] == true+            && row("T01")["reusedDocument"] == true && row("T03")["reusedDocument"] == true+            && row("T15")["reusedDocument"] == false && row("T17")["reusedDocument"] == false,+        s(&r)+    );+    check!(+        b,+        "documentsCreated: one shared part, two of their own, one scratch part for the verbs — 4, not 5",+        r["documentsCreated"] == 4,+        s(&r["documentsCreated"])+    );+    check!(+        b,+        "everything VISIBLE: not one DocumentVisible call (a hidden part cannot be modelled)",+        visible_calls(&b).is_empty(),+        s(&json!(visible_calls(&b)))+    );+    check!(+        b,+        "the run ends by respawning the COM host, and reports it",+        r["comRespawn"]["respawned"] == true && r["comRespawn"]["gracefulExit"] == true+            && ledger(&b).iter().any(|e| e["action"] == "com_host_respawn"),+        s(&r["comRespawn"])+    );+}++/// MEASURED 2026-10-14: at 86.9 % committed (9.9 GB available; Fusion 20.4 GB,+/// Chrome 9.7 GB, SOLIDWORKS only 1.5 GB), 0.10.6's self-test STOPPED before+/// T01. High pressure that SOLIDWORKS is not causing must not throttle the run.+#[test]+fn high_pressure_held_by_other_applications_does_not_stop_the_selftest() {+    let machine = TempDir::new("vis-high");+    std::fs::write(machine.join("windows.json"), "[]").unwrap();+    std::fs::write(+        machine.join("sldworks.json"),+        json!({"responding": true, "hwnd": 6622700, "privateMB": 1536, "handles": 4000}).to_string(),+    )+    .unwrap();+    std::fs::write(+        machine.join("memory.json"),+        json!({"commitLimitMB": 78438, "commitAvailMB": 10138, "physTotalMB": 32153, "physAvailMB": 2000,+               "processes": [{"name": "Fusion360.exe", "pid": 41000, "privateMB": 20890},+                             {"name": "chrome.exe", "pid": 42000, "privateMB": 9933}]}).to_string(),+    )+    .unwrap();+    let b = Bridge::start_with(&[("SWX_LAUNCH_FAKE_DIR", Box::leak(machine.path().into_boxed_str()))]);+    b.call("swx_start", json!({}));+    let r = b.call("swx_selftest", json!({"level": "write", "only": ["T01", "T03"]}));+    check!(+        b,+        "the run COMPLETES at high pressure: no stop, no recycle, and the pressure is reported per check",+        r["complete"] == true && r.get("stoppedFor").is_none() && r["recycled"] == json!([])+            && r["memoryPressureSeen"].as_array().map(|a| a.len() >= 2).unwrap_or(false)+            && r["memoryPressureSeen"][0]["why"].as_str().unwrap_or("").contains("carries on")+            && r["summary"]["failed"] == 0+            && r["_hint"].as_str().unwrap_or("").contains("held by other applications"),+        s(&r)+    );+    check!(b, "nothing was quit or terminated", !machine.join("quit.jsonl").exists() && !machine.join("terminated.jsonl").exists(), "quit / terminated");+}
uninstall.sh