← Commit history
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.10.3+0.10.4
Cargo.lock+1−1
@@ -122,7 +122,7 @@ dependencies = [  [[package]] name = "swx-bridge"-version = "0.10.3"+version = "0.10.4" dependencies = [  "miniz_oxide",  "serde",
Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "swx-bridge"-version = "0.10.3"+version = "0.10.4" edition = "2021" rust-version = "1.80" description = "Adom SOLIDWORKS bridge — swx_* verbs over the SOLIDWORKS COM API, as a single native binary"
README.md+16−2
@@ -14,7 +14,7 @@ about ten milliseconds. | Area | Verbs | |---|---| | **Create & open** | `swx_new_document` (part/assembly/drawing from the stock template), `swx_open`, `swx_import` (STEP/IGES/Parasolid/STL), `swx_save`, `swx_save_as`, `swx_close_doc`, `swx_find_files`, `swx_docs` |-| **Model** | `swx_run_script` — a typed modelling DSL: `new_part`, `sketch` (several rectangles/circles in ONE sketch — rings, walls, hole patterns), `sketch_rect`, `sketch_circle` (on a named plane, an offset plane, or a face), `extrude` / `cut` (reverse, both directions, mid-plane, through all), `fillet`, `chamfer`, `shell`, `linear_pattern`, `circular_pattern`, `set_appearance`, `set_material`, `set_prop`, `save_as`, `rebuild`, `zoom_fit`. Everything in millimetres; a worked LEGO brick is in [`examples/lego-2x4.json`](examples/lego-2x4.json). |+| **Model** | `swx_run_script` — a typed modelling DSL: `new_part`, `sketch` (several rectangles/circles in ONE sketch — rings, walls, hole patterns), `sketch_rect`, `sketch_circle` (on a named plane, an offset plane, or a face), `extrude` / `cut` (reverse, both directions, mid-plane, through all), `fillet`, `chamfer`, `shell`, `linear_pattern`, `circular_pattern`, `set_appearance`, `set_material`, `set_prop`, `save_as`, `rebuild`, `zoom_fit`. Everything in millimetres; a worked LEGO brick is in [`examples/lego-2x4.json`](examples/lego-2x4.json). Measured live on SOLIDWORKS 2026 (30 of 30 checks): [`docs/LIVE-RESULTS.md`](docs/LIVE-RESULTS.md). | | **Macros** | `swx_run_macro` — `ISldWorks::RunMacro2` on a `.swp`/`.swb`/`.dll`. Arbitrary code, so it needs the write opt-in every time. Scaffolding: not yet run live. | | **Assemble** | `swx_add_component` — insert a part or sub-assembly into the active assembly, positioned and rotated. | | **Close & quit safely** | `swx_close_all_safely` (saves every open document to a recovery folder, verifies each file, then closes), `swx_shutdown` (that sweep, then a graceful quit), `swx_recover_list` (what was preserved and where), `swx_close_import_temps`, `swx_user_presence` (is someone working in this session? the bridge refuses to end a session a person is using). |@@ -492,7 +492,21 @@ not.  ## Roadmap -- v0.10.3 (this): **selection after a geometric pick, fixed on SOLIDWORKS+- v0.10.4 (this): **proven live on SOLIDWORKS 2026.** 0.10.3 passed 30 of 30+  checks in `tools/dsl-live-check.py`, and `swx_selftest {"level":"write"}`+  passed 43 with none failed ([`docs/LIVE-RESULTS.md`](docs/LIVE-RESULTS.md)).+  Every live selection used the first rung, the typed `MultiSelect2`, and it+  stays first. Each step's `proven` is now+  `{"live": …, "measuredOn": [{"solidworks": 2026, "major": 34, …}]}` in place+  of `false`. `live` is true only when every variant the step ran was measured+  on the release it ran on. The variants come from one table in `compat.rs`,+  which `swx_describe` and `swx_selftest` also report as `liveMeasured`.+  Variants no live check has built say so under `unmeasured`: a reversed boss,+  through all on a boss, Right Plane and side-face sketches, patterns along+  other axes, the later selection rungs and the CreateDefinition fillet. The+  shell result now reports its `route` like fillet and chamfer. Nothing else+  changed. 596 tests.+- v0.10.3: **selection after a geometric pick, fixed on SOLIDWORKS   2026.** The first live run of `tools/dsl-live-check.py` (2026, major 34)   passed 20 of 30 checks. All 10 failures were one bug: `MultiSelect2`, given a   SAFEARRAY of VARIANTs, selected nothing. This broke face sketches, fillet,
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "solidworks",   "displayName": "SOLIDWORKS",-  "version": "0.10.3",+  "version": "0.10.4",   "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/COMPATIBILITY.md+29−4
@@ -16,8 +16,20 @@ only ever **run** against **3DEXPERIENCE SOLIDWORKS 2026** (Makers, revision   looked up on help.solidworks.com. The newest is **SOLIDWORKS 2017 (revision   25)**: `IFeatureManager::FeatureCut4` and `ISldWorks::GetCurrentLicenseType`.   Nothing is newer than 2021, so nothing needs a fallback on 2021.-- **[measured] Tested on SOLIDWORKS 2026 only.** Michael's reports show the-  bridge installing and working on 2021. That is evidence, not a test.+- **[measured] Tested on SOLIDWORKS 2026, live.** On 2026-10-14 the modelling+  ops of issues #1 and #2 ran on SOLIDWORKS 2026 (major 34, 3DEXPERIENCE+  "Connected") with bridge 0.10.3. `tools/dsl-live-check.py` passed 30 of 30+  checks. `swx_selftest {"level":"write"}` passed 43, failed 0 and skipped 1.+  The LEGO brick matched Michael's 2021 numbers, face count included. See+  [LIVE-RESULTS.md](LIVE-RESULTS.md).+- **[unmeasured] 2021 is still waiting for a `swx_selftest` run.** Michael's+  reports show the bridge installing and working on 2021. That is evidence, not+  a test.+- Each `swx_run_script` step reports `proven: {live, measuredOn}`. `live` is+  true only when every variant the step ran was measured on the release it ran+  on, so on 2021 it is `false` for now. The variants and their measurements are+  one table, `compat::OP_FACTS`. `swx_describe` and `swx_selftest` report the+  same table as `liveMeasured`. - `bridge.json` declares `"solidworks": ">=2021"`. It said `>=2024`, which was   untrue in both directions: it never stopped Michael installing, and nothing in   the API needs 2024.@@ -300,8 +312,21 @@ from the gate, so a check held at the gate is reported, not hidden.   - the pinned document not activated, now activated;   - a negative offset plane's and an axis's orientation, now read back. -  They are still asserted only in the simulator and marked `proven:false` in-  step results.+  On 2026 they are now MEASURED ([LIVE-RESULTS.md](LIVE-RESULTS.md)). On 2021+  they are not, and each step's `proven.live` says `false` there.++  Some variants have not been measured on any release yet. Steps that use them+  report `proven.live:false` and name them under `unmeasured`:+  - a boss with its direction reversed;+  - through all on a boss;+  - a sketch on Right Plane, on a named plane, on a plane offset from Front or+    Right, or on a side face;+  - patterns along another axis, or in two directions;+  - a chamfer on all of a feature's edges;+  - an outward shell;+  - a colour on one body of a multi-body part;+  - the later rungs of the selection ladder;+  - the CreateDefinition fillet fallback. - SELECTION after a geometric pick (0.10.3). On SOLIDWORKS 2026 `MultiSelect2`   with a VARIANT array selected nothing. This was measured, and it broke face   sketches, fillet, chamfer and shell. The bridge now climbs a verified ladder:
docs/LIVE-RESULTS.mdadded+63
@@ -0,0 +1,63 @@+# Live results: the modelling ops on real SOLIDWORKS++Measured 2026-10-14 with bridge **0.10.3** on **SOLIDWORKS 2026** (major version 34, 3DEXPERIENCE edition, Windows 11).+These are the features requested in wiki issue #1 and the bugs reported in #2.++## Summary++- `tools/dsl-live-check.py`: **30 of 30 checks pass.** Each builds a part on SOLIDWORKS through `swx_run_script`, reads it back with `swx_inspect_bodies` / `swx_mass_properties`, and compares it with geometry computed independently in the script (closed-form volumes, bounding boxes from the documented conventions). Tolerances: volume 0.2 %, bounding box 0.01 mm.+- `swx_selftest {"level":"write"}`: **43 passed, 0 failed, 1 skipped** (the active-document reads, skipped because the test leaves no document open). 538 s.+- Selections after a geometric pick used the first rung of the ladder, `MultiSelect2` with a typed object array, every time (11 of 11).+- Every scratch part was closed afterwards: SOLIDWORKS was left with no open documents.++## The LEGO 2x4 brick (`examples/lego-2x4.json`)++Built in 4 features, measured live:++| Measured | Value | Issue #1/#2 reference (SOLIDWORKS 2021, 22-feature build) |+|---|---|---|+| Volume | 2073.362 mm^3 | 2073.362 mm^3 |+| Bounding box | [-15.9, 0.0, -7.9] to [15.9, 11.3, 7.9] mm | 31.8 x 11.3 x 15.8 mm |+| Faces | 39 | 39 |+| Colour | [200, 0, 0] (set before the material, kept after it) | red requested |+| Density / mass | 1020.0 kg/m^3 / 0.0021148 kg | ABS 1020 / 0.0021148 |++## Every check++| Id | Check | Issue | Expected volume (mm^3) | Measured (mm^3) | Result |+|---|---|---|---|---|---|+| T01 | rectangle anchor: xMm/yMm is the CORNER by default | #1.5 | 1000.0 | 1000.0 | PASS |+| T02 | Top Plane: sketch +Y is model -Z | #1.5 | 1000.0 | 1000.0 | PASS |+| T03 | anchor center | #1.5 | 1000.0 | 1000.0 | PASS |+| T04 | sketch on a plane offset +10 mm from Top Plane | #1.1 | 4392.699 | 4392.699 | PASS |+| T05 | sketch on a NEGATIVE offset plane (-5 mm) | #1.1 | 6000.0 | 6000.0 | PASS |+| T06 | sketch on a FACE picked by a point (the block's top face) | #1.1 | 4113.097 | 4113.097 | PASS |+| T07 | Michael's case: a default cut from Top Plane with the body on +Y must FAIL LOUDLY, never succeed silently | #1.2 |  |  | PASS |+| T08 | cut reverse:true removes material on +Y | #1.2 | 3500.0 | 3500.0 | PASS |+| T09 | cut throughAll + reverse | #1.2 | 3000.0 | 3000.0 | PASS |+| T10 | extrude direction both (5 mm one way, 3 mm the other) | #1.2 | 800.0 | 800.0 | PASS |+| T11 | extrude midPlane | #1.2 | 1000.0 | 1000.0 | PASS |+| T12 | cut midPlane through the block | #1.2 | 3904.0 | 3904.0 | PASS |+| T13 | two concentric circles in ONE sketch extrude as a RING | #1.3 | 502.655 | 502.655 | PASS |+| T14 | two disjoint rectangles in ONE sketch: one feature, two bodies | #1.3 | 250.0 | 250.0 | PASS |+| T15 | set_appearance on the part | #1.4 | 4000.0 | 4000.0 | PASS |+| T16 | set_appearance on one face picked by a point | #1.4 | 4000.0 | 4000.0 | PASS |+| T17 | fillet every edge of the block, r = 1 | #1.6 | 3958.419 | 3958.419 | PASS |+| T18 | chamfer ONE edge picked by a point, 1 mm | #1.6 | 3990.0 | 3990.0 | PASS |+| T19 | shell 1 mm, top face removed | #1.6 | 1084 | 1084.0 | PASS |+| T20 | linear pattern: one boss -> 3 along X, 6 mm apart | #1.6 | 4188.496 | 4188.496 | PASS |+| T21 | circular pattern: one boss -> 6 around Y | #1.6 | 4169.646 | 4169.646 | PASS |+| T22 | issue #2: body mass uses the material's density (ABS 1020 kg/m^3) | #2.a | 4000.0 | 4000.0 | PASS |+| T25 | audit #1/#5: sketch on the BOTTOM face (hidden in an iso view, normal -Y) extrudes OUTWARD | #1.1 | 4084.823 | 4084.823 | PASS |+| T26 | audit unverified #1: a NEGATIVE offset plane keeps the base plane's axes (asymmetric sketch, separate body) | #1.1 | 4048.0 | 4048.0 | PASS |+| T27 | audit #6: an ASYMMETRIC two-way cut: depthMm goes along the cut's own direction (-normal), depth2Mm the other way | #1.2 | 968 | 968.0 | PASS |+| T28 | audit unverified #2: linear pattern with NEGATIVE spacing goes -X (instances stay on the block) | #1.6 | 4188.496 | 4188.496 | PASS |+| T29 | audit #1: shell removing the BOTTOM face (hidden in an iso view) removes that face, not a side | #1.6 | 1084 | 1084.0 | PASS |+| T30 | audit #3: fillet ONE feature's edges by allOfFeature on a cylinder (2 circular edges) | #1.6 | 772.517 | 772.517 | PASS |+| T24 | the LEGO 2x4 acceptance brick (examples/lego-2x4.json) | #1 acceptance | 2073.362 | 2073.362 | PASS |+| T23 | issue #2: run_script on the bridge's own new part (addressed by path) says bridgeCreatedDocument:true | #2.b |  |  | PASS |++## 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.+- `throughAll` with `direction:"both"` on a boss, the `IEntity::Select4` and `SelectByRay` selection rungs (never needed live: the first rung worked), and the `CreateDefinition` fillet fallback (never needed: `FeatureFillet3` worked).
install.sh

  
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "solidworks-bridge",-  "version": "0.10.3",+  "version": "0.10.4",   "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+8−2
@@ -45,8 +45,14 @@ adom-bridge --ai-thread "<thread>" swx_save_as '{"outputPath":"C:\\parts\\bracke   dimension is in **millimetres**. A rejected script returns the full op list   with every op's arguments (`opArgs`), the coordinate `conventions` and a   worked example, so read the error.-- **The ops (0.10.0).** Michael asked for these in issue #1; the API calls are-  audited but UNMEASURED live, and each reports `proven:false`.+- **The ops (0.10.0).** Michael asked for these in issue #1. They were+  MEASURED live on SOLIDWORKS 2026 on 2026-10-14: `tools/dsl-live-check.py`+  passed 30 of 30 checks. See `docs/LIVE-RESULTS.md` in the bridge repo.+  SOLIDWORKS 2021 is still waiting for a `swx_selftest {"level":"write"}` run.+  Each step reports `proven: {live, measuredOn}`. `live` is true only when+  every variant the step ran was measured on the release it ran on. When it is+  false, `unmeasured` names the variant, so check that step's geometry with+  `swx_inspect_bodies`. `swx_describe` lists every variant as `liveMeasured`.   - **Sketching.**     - `sketch` holds SEVERAL profiles in ONE sketch:       `{"op":"sketch","plane":"Top Plane","entities":[{"rect":{...}},{"circle":{...}}]}`.
src/comhost/com.rs+10−7
@@ -5384,10 +5384,14 @@ impl ComBackend {                 let _ = sw.call("ActivateDoc3", &[&Var::bstr(&prev), &Var::boolean(false), &Var::i4(1), &Var::byref_i4(&mut errors as *mut i32)]);             }         }+        // The release the steps ran on: `proven` (compat::step_proof) is LIVE+        // only for variants measured on this very release.+        let revision = self.app(false, true).ok().and_then(|sw| sw.string("RevisionNumber", &[]));         Ok(json!({             "steps": results,             "stepCount": results.len(),             "created": created,+            "solidworksRevision": revision,             "targetDocument": doc.as_ref().and_then(|d| d.string("GetTitle", &[])),             "documentName": doc.as_ref().and_then(|d| d.string("GetTitle", &[])),             "doc": doc.as_ref().map(Self::doc_info).unwrap_or(Value::Null),@@ -5710,7 +5714,6 @@ impl ComBackend {                     "reverse": ext.reverse,                     "dir": {"value": dir, "flippedToFollowTheReference": flipped, "sketchNormal": pending.normal, "reference": pending.reference},                     "endCondition": t1,-                    "proven": !cut && ext.direction == Direction::One && !ext.through_all && pending.reference.is_none(),                 }))             } @@ -5805,7 +5808,7 @@ impl ComBackend {                         fallback_error.map(|e| format!("; the CreateFeature route failed too: {e}")).unwrap_or_default()                     ))                 })?;-                Ok(json!({"feature": feature.string("Name", &[]), "edgesSelected": want, "edges": how, "sizeMm": radius * 1000.0, "route": route, "proven": false}))+                Ok(json!({"feature": feature.string("Name", &[]), "edgesSelected": want, "edges": how, "sizeMm": radius * 1000.0, "route": route}))             }              Op::Shell { thickness, faces, outward } => {@@ -5837,8 +5840,8 @@ impl ComBackend {                 if after.is_none() || after == before {                     return Err(HostError::msg("SOLIDWORKS made no shell (no new feature appeared) — the thickness may be too large for the part"));                 }-                Ok(json!({"feature": after, "thicknessMm": thickness * 1000.0, "facesRemoved": want, "outward": outward, "proven": false,-                          "selectedVia": r.report(), "viewDependent": r.view_dependent()}))+                Ok(json!({"feature": after, "thicknessMm": thickness * 1000.0, "facesRemoved": want, "outward": outward,+                          "route": "InsertFeatureShell", "selectedVia": r.report(), "viewDependent": r.view_dependent()}))             }              Op::LinearPattern { feature, dirs } => {@@ -5906,7 +5909,7 @@ impl ComBackend {                 let _ = Self::clear_selection(&doc);                 let made = made.ok_or_else(|| HostError::msg(format!("SOLIDWORKS made no linear pattern of '{seed}' (the feature came back null)")))?;                 Ok(json!({"feature": made.string("Name", &[]), "seed": seed, "axes": axes.iter().map(|(n, s)| json!({"axis": n, "pointsPositive": *s > 0.0})).collect::<Vec<_>>(),-                          "instances": n1 * n2, "flip": [flip1, flip2], "proven": false}))+                          "instances": n1 * n2, "flip": [flip1, flip2]}))             }              Op::CircularPattern { feature, axis, count, angle } => {@@ -5936,7 +5939,7 @@ impl ComBackend {                 let made = f.as_dispatch().map(Disp);                 let _ = Self::clear_selection(&doc);                 let made = made.ok_or_else(|| HostError::msg(format!("SOLIDWORKS made no circular pattern of '{seed}' (the feature came back null)")))?;-                Ok(json!({"feature": made.string("Name", &[]), "seed": seed, "axis": ax, "instances": count, "angleDeg": angle.to_degrees(), "proven": false}))+                Ok(json!({"feature": made.string("Name", &[]), "seed": seed, "axis": ax, "instances": count, "angleDeg": angle.to_degrees()}))             }              Op::SetMaterial { name, database } => {@@ -6473,7 +6476,7 @@ impl ComBackend {             )));         }         let _ = doc.call("GraphicsRedraw2", &[]);-        let mut out = json!({"rgb": to255(rgb), "target": route, "route": route, "readBack": to255(&back), "proven": false});+        let mut out = json!({"rgb": to255(rgb), "target": route, "route": route, "readBack": to255(&back)});         if let Some((v, d)) = via {             out["selectedVia"] = v;             out["viewDependent"] = json!(d);
src/comhost/mock.rs+1
@@ -3824,6 +3824,7 @@ impl MockBackend {                     "steps": steps,                     "stepCount": steps.len(),                     "created": created,+                    "solidworksRevision": mock_version(),                     "targetDocument": doc.as_ref().and_then(|d| d.get("title").cloned()),                     "documentName": doc.as_ref().and_then(|d| d.get("title").cloned()),                     "doc": doc.unwrap_or(Value::Null),
src/comhost/mock_model.rs+2
@@ -873,6 +873,7 @@ pub fn apply(model: &mut Model, run: &mut Run, op: &Op, active: bool) -> Result<             how["viewDependent"] = via["viewDependent"].clone();             let mut r = model.edge_feature(false, *distance, *angle, &sel)?;             r["edges"] = how;+            r["route"] = json!("InsertFeatureChamfer");             r         }         Op::Shell { thickness, faces, outward } => {@@ -880,6 +881,7 @@ pub fn apply(model: &mut Model, run: &mut Run, op: &Op, active: bool) -> Result<             let picked: Vec<FaceRef> = faces.iter().map(|p| model.pick_face(*p)).collect::<Result<_, _>>()?;             let via = select_n(run, modeling::dedupe(&picked).len(), "faces", "no shell was made")?;             let mut r = model.shell(*thickness, faces, *outward)?;+            r["route"] = json!("InsertFeatureShell");             r["selectedVia"] = via["selectedVia"].clone();             r["viewDependent"] = via["viewDependent"].clone();             r
src/compat.rs+455
@@ -66,6 +66,324 @@ pub fn describe(revision: Option<&str>) -> serde_json::Value {     }) } +// ---------------------------------------------------------------------------+// WHAT HAS BEEN MEASURED LIVE (0.10.4) — the ONE table step results+// (`proven`), swx_selftest and swx_describe all report from.+//+// A variant is MEASURED when a live check built it on a real SOLIDWORKS and+// the geometry read back matched (docs/LIVE-RESULTS.md). Anything not in this+// table, or in it with no measurement, is UNMEASURED and says so. The+// grain is the SOLIDWORKS call shape, not the op name: a boss whose direction+// flag is reversed is a different call from the default one, and no live+// boss has run that way yet.+// ---------------------------------------------------------------------------++/// One live run of the modelling battery.+#[derive(Clone, Copy, Debug, PartialEq, Eq)]+pub struct Measurement {+    pub year: u32,+    pub major: u32,+    pub edition: &'static str,+    pub date: &'static str,+    pub bridge: &'static str,+}++impl Measurement {+    pub fn to_json(&self) -> serde_json::Value {+        serde_json::json!({"solidworks": self.year, "major": self.major, "edition": self.edition, "date": self.date, "bridge": self.bridge})+    }+}++/// 2026-10-14, bridge 0.10.3, SOLIDWORKS 2026 (major 34, 3DEXPERIENCE+/// "Connected"): tools/dsl-live-check.py 30/30 and swx_selftest+/// {level:"write"} 43 passed / 0 failed / 1 skipped (docs/LIVE-RESULTS.md).+pub const LIVE_2026_10_14: Measurement = Measurement { year: 2026, major: 34, edition: "connected", date: "2026-10-14", bridge: "0.10.3" };++const M2026: &[Measurement] = &[LIVE_2026_10_14];+const UNMEASURED: &[Measurement] = &[];++/// One variant of one DSL op, and where it was measured live.+pub struct OpFact {+    pub id: &'static str,+    pub what: &'static str,+    /// The live checks that measured it (T-ids of tools/dsl-live-check.py and+    /// swx_selftest {level:"write"}), or why it has none.+    pub checks: &'static str,+    pub measured: &'static [Measurement],+}++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: "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 (its axes are the documented convention, not a measurement)", checks: "no live check sketches on Right Plane", measured: UNMEASURED },+    OpFact { id: "sketch.plane.named", what: "a sketch on a named plane other than Front/Top/Right", checks: "not in the live battery", measured: UNMEASURED },+    OpFact { id: "sketch.offset.top.positive", what: "a sketch on a plane offset + from Top Plane", checks: "T04 T24", measured: M2026 },+    OpFact { id: "sketch.offset.top.negative", what: "a sketch on a plane offset − from Top Plane (keeps Top Plane's axes)", checks: "T05 T26", measured: M2026 },+    OpFact { id: "sketch.offset.other", what: "a sketch on a plane offset from Front Plane, Right Plane or a named plane", checks: "not in the live battery", measured: UNMEASURED },+    OpFact { id: "sketch.face.top", what: "a sketch on a face parallel to Top Plane, picked by a point (the top face, and the bottom face hidden in an iso view)", checks: "T06 T20 T21 T25 T28 T32", measured: M2026 },+    OpFact { id: "sketch.face.side", what: "a sketch on a face parallel to Front or Right Plane", checks: "not in the live battery", measured: UNMEASURED },+    OpFact { id: "sketch.rect", what: "a rectangle (corner or center anchor)", checks: "T01 T02 T03", measured: M2026 },+    OpFact { id: "sketch.circle", what: "a circle", checks: "T04 T06 T30", measured: M2026 },+    OpFact { id: "sketch.multiProfile", what: "several profiles in one sketch (a ring, disjoint rectangles, the LEGO tubes)", checks: "T13 T14 T24", measured: M2026 },+    OpFact { id: "extrude.one", what: "a boss one way (the plane's normal, or outward from a face)", checks: "T01 T06 T25 and every base block", measured: M2026 },+    OpFact { id: "extrude.one.reversed", what: "a boss whose direction flag is reversed (reverse:true)", checks: "no live boss has run reversed: every one went the default way", measured: UNMEASURED },+    OpFact { id: "extrude.both", what: "a boss both ways (depthMm and depth2Mm)", checks: "T10", measured: M2026 },+    OpFact { id: "extrude.midPlane", what: "a boss symmetric about the sketch plane", checks: "T11", measured: M2026 },+    OpFact { id: "extrude.one.throughAll", what: "a boss through all, one way", checks: "not in the live battery", measured: UNMEASURED },+    OpFact { id: "extrude.both.throughAll", what: "a boss through all, both ways", checks: "not in the live battery", measured: UNMEASURED },+    OpFact { id: "cut.one", what: "a cut one way, the default direction (the plane's −normal, into a face)", checks: "T07 (nothing on that side: the step failed, as it must) and T27's first direction", measured: M2026 },+    OpFact { id: "cut.one.reversed", what: "a cut one way, reversed (reverse:true)", checks: "T08 T24", measured: M2026 },+    OpFact { id: "cut.one.throughAll.reversed", what: "a cut through all, reversed", checks: "T09", measured: M2026 },+    OpFact { id: "cut.both", what: "a cut both ways (depthMm along the cut's own direction, depth2Mm the other)", checks: "T27", measured: M2026 },+    OpFact { id: "cut.midPlane", what: "a cut symmetric about the sketch plane", checks: "T12", measured: M2026 },+    OpFact { id: "select.multiSelectTyped", what: "the picked entities selected by MultiSelect2 with a typed object array", checks: "11 of 11 live selections (T06 T16-T21 T25 T28-T30)", measured: M2026 },+    OpFact { id: "select.entitySelect4", what: "a picked entity selected by IEntity::Select4 (the ladder's 2nd rung)", checks: "never needed live: the first rung worked", measured: UNMEASURED },+    OpFact { id: "select.selectByRay", what: "a picked entity selected by SelectByRay (the ladder's 3rd rung)", checks: "never needed live: the first rung worked", measured: UNMEASURED },+    OpFact { id: "select.selectByIdAtPoint", what: "a picked entity selected by SelectByID2 at the point, through the view (the last rung)", checks: "never needed live: the first rung worked", measured: UNMEASURED },+    OpFact { id: "fillet.allOfFeature", what: "a fillet on every edge of a feature (a box's 12, a cylinder's 2)", checks: "T17 T30", measured: M2026 },+    OpFact { id: "fillet.atPoints", what: "a fillet on edges picked by points", checks: "T32 (swx_selftest)", measured: M2026 },+    OpFact { id: "fillet.FeatureFillet3", what: "the fillet made by IFeatureManager::FeatureFillet3", checks: "T17 T30 T32", measured: M2026 },+    OpFact { id: "fillet.createDefinition", what: "the fillet made by the CreateDefinition(swFmFillet) fallback", checks: "never needed live: FeatureFillet3 worked", measured: UNMEASURED },+    OpFact { id: "chamfer.atPoints", what: "a chamfer on edges picked by points", checks: "T18", measured: M2026 },+    OpFact { id: "chamfer.allOfFeature", what: "a chamfer on every edge of a feature", checks: "not in the live battery", measured: UNMEASURED },+    OpFact { id: "shell.inward", what: "a shell inward, removing faces picked by points (the top face, and the bottom face hidden in an iso view)", checks: "T19 T29", measured: M2026 },+    OpFact { id: "shell.outward", what: "a shell outward", checks: "not in the live battery", measured: UNMEASURED },+    OpFact { id: "linear_pattern.x", what: "a linear pattern along X, one direction (positive or negative spacing)", checks: "T20 T28", measured: M2026 },+    OpFact { id: "circular_pattern.y", what: "a circular pattern about Y", checks: "T21", measured: M2026 },+    OpFact { id: "set_appearance.part", what: "a colour on the part (or on the one body of a single-body part)", checks: "T15 T24", measured: M2026 },+    OpFact { id: "set_appearance.face", what: "a colour on one face picked by a point", checks: "T16", measured: M2026 },+    OpFact { id: "set_appearance.body", what: "a colour on one body of a multi-body part", checks: "not in the live battery", measured: UNMEASURED },+    OpFact { id: "set_material", what: "a material from the SOLIDWORKS library (the body's density follows it)", checks: "T22 T24", measured: M2026 },+    OpFact { id: "set_material.reappliesAppearance", what: "a material after a colour: the bridge re-applies the colour", checks: "T31 (swx_selftest)", measured: M2026 },+    OpFact { id: "set_prop", what: "a custom property", checks: "T24", measured: M2026 },+    OpFact { id: "save_as", what: "save_as as a script step", checks: "not in the live battery (swx_save_as, the verb, is)", measured: UNMEASURED },+    OpFact { id: "rebuild", what: "rebuild as a script step", checks: "not in the live battery", measured: UNMEASURED },+    OpFact { id: "zoom_fit", what: "zoom_fit as a script step", checks: "not in the live battery", measured: UNMEASURED },+];++pub fn op_fact(id: &str) -> Option<&'static OpFact> {+    OP_FACTS.iter().find(|f| f.id == id)+}++/// The labels the selection ladder reports (`modeling::Rung::label`), to fact ids.+fn rung_fact(label: &str) -> &'static str {+    use crate::modeling::Rung;+    for (r, id) in [+        (Rung::MultiSelectTyped, "select.multiSelectTyped"),+        (Rung::EntitySelect4, "select.entitySelect4"),+        (Rung::SelectByRay, "select.selectByRay"),+        (Rung::SelectByIdAtPoint, "select.selectByIdAtPoint"),+    ] {+        if label == r.label() {+            return id;+        }+    }+    "select.unknown"+}++fn axis_name(a: crate::script::Axis) -> &'static str {+    match a {+        crate::script::Axis::X => "x",+        crate::script::Axis::Y => "y",+        crate::script::Axis::Z => "z",+    }+}++fn plane_key(name: &str) -> &'static str {+    match crate::modeling::default_index(name) {+        Some(0) => "front",+        Some(1) => "top",+        Some(2) => "right",+        _ => "named",+    }+}++/// THE VARIANTS ONE STEP EXERCISED — from the op as written and from what+/// SOLIDWORKS was actually asked (the result's direction flag, route and+/// selection rung).+pub fn op_variants(op: &crate::script::Op, result: &serde_json::Value) -> Vec<String> {+    use crate::script::{AppearanceTarget, Direction, EdgeSel, Op, PlaneRef};+    let mut v: Vec<String> = Vec::new();+    match op {+        Op::NewPart => v.push("new_part".into()),+        Op::NewAssembly => v.push("new_assembly".into()),+        Op::Sketch { plane, entities, .. } => {+            v.push(match plane {+                PlaneRef::Named(n) => format!("sketch.plane.{}", plane_key(n)),+                PlaneRef::Offset { from, offset } => match plane_key(from) {+                    "top" => format!("sketch.offset.top.{}", if *offset < 0.0 { "negative" } else { "positive" }),+                    _ => "sketch.offset.other".into(),+                },+                PlaneRef::Face { .. } => {+                    let from = result.pointer("/resolvedPlane/offsetFrom").and_then(|f| f.as_str()).unwrap_or("");+                    if plane_key(from) == "top" { "sketch.face.top".into() } else { "sketch.face.side".into() }+                }+            });+            if entities.iter().any(|e| matches!(e, crate::script::Entity::Rect { .. })) {+                v.push("sketch.rect".into());+            }+            if entities.iter().any(|e| matches!(e, crate::script::Entity::Circle { .. })) {+                v.push("sketch.circle".into());+            }+            if entities.len() > 1 {+                v.push("sketch.multiProfile".into());+            }+        }+        Op::Extrude(e) | Op::Cut(e) => {+            let kind = if matches!(op, Op::Cut(_)) { "cut" } else { "extrude" };+            let dir = match e.direction {+                Direction::One => "one",+                Direction::Both => "both",+                Direction::MidPlane => "midPlane",+            };+            // The flag SOLIDWORKS was given (the COM host reports it); the+            // caller's `reverse` where a backend does not.+            let reversed = result.pointer("/dir/value").and_then(|d| d.as_bool()).unwrap_or(e.reverse);+            let mut id = format!("{kind}.{dir}");+            if e.through_all {+                id.push_str(".throughAll");+            }+            if reversed {+                id.push_str(".reversed");+            }+            v.push(id);+        }+        Op::Fillet { edges, .. } | Op::Chamfer { edges, .. } => {+            let kind = if matches!(op, Op::Fillet { .. }) { "fillet" } else { "chamfer" };+            v.push(format!("{kind}.{}", if matches!(edges, EdgeSel::AtPoints(_)) { "atPoints" } else { "allOfFeature" }));+            if kind == "fillet" {+                match result.get("route").and_then(|r| r.as_str()) {+                    Some("FeatureFillet3") | None => v.push("fillet.FeatureFillet3".into()),+                    Some(_) => v.push("fillet.createDefinition".into()),+                }+            }+        }+        Op::Shell { outward, .. } => v.push(if *outward { "shell.outward".into() } else { "shell.inward".into() }),+        Op::LinearPattern { dirs, .. } => {+            let axes: Vec<&str> = dirs.iter().map(|d| axis_name(d.0)).collect();+            v.push(format!("linear_pattern.{}", axes.join("+")));+        }+        Op::CircularPattern { axis, .. } => v.push(format!("circular_pattern.{}", axis_name(*axis))),+        Op::SetAppearance { target, .. } => v.push(match target {+            AppearanceTarget::Part => "set_appearance.part".into(),+            AppearanceTarget::FaceAt(_) => "set_appearance.face".into(),+            // A single-body part is coloured at the part level (the route says so).+            AppearanceTarget::Body(_) => {+                if result.get("route").and_then(|r| r.as_str()).map(|r| r.starts_with("part")).unwrap_or(false) {+                    "set_appearance.part".into()+                } else {+                    "set_appearance.body".into()+                }+            }+        }),+        Op::SetMaterial { .. } => {+            v.push("set_material".into());+            if result.get("appearanceReapplied").and_then(|a| a.as_array()).map(|a| !a.is_empty()).unwrap_or(false) {+                v.push("set_material.reappliesAppearance".into());+            }+        }+        Op::SetProp { .. } => v.push("set_prop".into()),+        Op::SaveAs { .. } => v.push("save_as".into()),+        Op::Rebuild => v.push("rebuild".into()),+        Op::ZoomFit => v.push("zoom_fit".into()),+    }+    // The selection rung(s), wherever the step reports them.+    for at in ["/selectedVia", "/edges/selectedVia", "/resolvedPlane/selectedVia"] {+        match result.pointer(at) {+            Some(serde_json::Value::String(s)) => v.push(rung_fact(s).into()),+            Some(serde_json::Value::Array(a)) => {+                for s in a.iter().filter_map(|s| s.as_str()) {+                    let id = rung_fact(s).to_string();+                    if !v.contains(&id) {+                        v.push(id);+                    }+                }+            }+            _ => {}+        }+    }+    v+}++/// `proven` for a step that exercised `variants`, on the SOLIDWORKS whose+/// major version is `major`: LIVE only when every variant was measured on+/// that very release.+pub fn proof(variants: &[String], major: Option<u32>) -> serde_json::Value {+    let mut unmeasured: Vec<serde_json::Value> = Vec::new();+    let mut common: Option<Vec<Measurement>> = None;+    for id in variants {+        let (what, measured): (String, Vec<Measurement>) = match op_fact(id) {+            Some(f) => (f.what.to_string(), f.measured.to_vec()),+            None => (format!("{id} (no live check has built this variant)"), Vec::new()),+        };+        if measured.is_empty() {+            unmeasured.push(serde_json::json!({"variant": id, "what": what}));+        }+        common = Some(match common {+            None => measured,+            Some(c) => c.into_iter().filter(|m| measured.contains(m)).collect(),+        });+    }+    let on = common.unwrap_or_default();+    let live = major.map(|m| on.iter().any(|x| x.major == m)).unwrap_or(false);+    let mut out = serde_json::json!({+        "live": live,+        "measuredOn": on.iter().map(Measurement::to_json).collect::<Vec<_>>(),+        "variants": variants,+    });+    if !live {+        let years: Vec<String> = on.iter().map(|m| m.year.to_string()).collect();+        out["why"] = serde_json::json!(if !unmeasured.is_empty() {+            "not measured live: see `unmeasured` — verify this step's geometry with swx_inspect_bodies".to_string()+        } else if on.is_empty() {+            "nothing was measured".to_string()+        } else {+            match major {+                Some(m) => format!("measured on SOLIDWORKS {} only; this is SOLIDWORKS {}", years.join(", "), year_of(m)),+                None => format!("measured on SOLIDWORKS {} only; this SOLIDWORKS's version could not be read", years.join(", ")),+            }+        });+        if !unmeasured.is_empty() {+            out["unmeasured"] = serde_json::Value::Array(unmeasured);+        }+    }+    out+}++/// `proven` for one step of a script.+pub fn step_proof(op: &crate::script::Op, result: &serde_json::Value, major: Option<u32>) -> serde_json::Value {+    proof(&op_variants(op, result), major)+}++/// The whole table, as swx_describe and swx_selftest report it; with the+/// install's major version, each row also says whether it is live HERE.+pub fn live_table(major: Option<u32>) -> serde_json::Value {+    let rows: Vec<serde_json::Value> = OP_FACTS+        .iter()+        .map(|f| {+            let mut r = serde_json::json!({+                "variant": f.id,+                "what": f.what,+                "checks": f.checks,+                "measuredOn": f.measured.iter().map(Measurement::to_json).collect::<Vec<_>>(),+            });+            if let Some(m) = major {+                r["liveOnThisInstall"] = serde_json::json!(f.measured.iter().any(|x| x.major == m));+            }+            r+        })+        .collect();+    serde_json::json!({+        "source": "docs/LIVE-RESULTS.md",+        "measuredVariants": OP_FACTS.iter().filter(|f| !f.measured.is_empty()).count(),+        "unmeasuredVariants": OP_FACTS.iter().filter(|f| f.measured.is_empty()).count(),+        "note": "A step's `proven.live` is true only when every variant it exercised was measured on this SOLIDWORKS release. A variant not listed has never been built live.",+        "variants": rows,+    })+}+ #[cfg(test)] mod tests {     use super::*;@@ -87,4 +405,141 @@ mod tests {         assert_eq!(d["tested"], false);         assert_eq!(d["year"], 2021);     }++    fn ops(script: serde_json::Value) -> Vec<crate::script::Op> {+        crate::script::parse(&script).expect("the test script parses")+    }++    fn ids(op: &crate::script::Op, result: serde_json::Value) -> Vec<String> {+        op_variants(op, &result)+    }++    #[test]+    fn the_table_is_well_formed() {+        for (i, f) in OP_FACTS.iter().enumerate() {+            assert!(OP_FACTS.iter().skip(i + 1).all(|g| g.id != f.id), "duplicate fact {}", f.id);+            assert!(!f.checks.is_empty() && !f.what.is_empty(), "{} says what and where", f.id);+        }+        // The live run of 2026-10-14, exactly as the coordinator recorded it.+        assert_eq!(+            LIVE_2026_10_14.to_json(),+            serde_json::json!({"solidworks": 2026, "major": 34, "edition": "connected", "date": "2026-10-14", "bridge": "0.10.3"})+        );+        // What docs/LIVE-RESULTS.md lists as measured...+        for id in [+            "new_part", "sketch.plane.front", "sketch.plane.top", "sketch.offset.top.positive", "sketch.offset.top.negative",+            "sketch.face.top", "sketch.rect", "sketch.circle", "sketch.multiProfile", "extrude.one", "extrude.both",+            "extrude.midPlane", "cut.one", "cut.one.reversed", "cut.one.throughAll.reversed", "cut.both", "cut.midPlane",+            "set_appearance.part", "set_appearance.face", "set_material", "set_material.reappliesAppearance",+            "fillet.allOfFeature", "fillet.FeatureFillet3", "chamfer.atPoints", "shell.inward", "linear_pattern.x",+            "circular_pattern.y", "set_prop", "select.multiSelectTyped",+        ] {+            assert_eq!(op_fact(id).map(|f| f.measured), Some(M2026), "{id} was measured on 2026");+        }+        // ...and EXACTLY what is not: its "not yet measured" list (throughAll+both+        // on a boss, the ladder's later rungs, the CreateDefinition fillet) plus+        // the variants no live check built — a reversed boss among them.+        let mut unmeasured: Vec<&str> = OP_FACTS.iter().filter(|f| f.measured.is_empty()).map(|f| f.id).collect();+        unmeasured.sort_unstable();+        assert_eq!(+            unmeasured,+            [+                "chamfer.allOfFeature", "extrude.both.throughAll", "extrude.one.reversed", "extrude.one.throughAll",+                "fillet.createDefinition", "new_assembly", "rebuild", "save_as", "select.entitySelect4",+                "select.selectByIdAtPoint", "select.selectByRay", "set_appearance.body", "shell.outward",+                "sketch.face.side", "sketch.offset.other", "sketch.plane.named", "sketch.plane.right", "zoom_fit",+            ]+        );+    }++    #[test]+    fn a_step_is_classified_by_what_solidworks_was_asked() {+        let o = ops(serde_json::json!([+            {"op": "new_part"},+            {"op": "sketch_rect", "plane": "Front Plane", "widthMm": 10, "heightMm": 10},+            {"op": "extrude", "depthMm": 5},+            {"op": "sketch", "plane": "Right Plane", "entities": [{"circle": {"radiusMm": 4}}, {"circle": {"radiusMm": 2}}]},+            {"op": "extrude", "depthMm": 5, "reverse": true},+            {"op": "sketch_circle", "plane": {"offsetFrom": "Top Plane", "offsetMm": -5}, "radiusMm": 2},+            {"op": "cut", "throughAll": true, "reverse": true},+            {"op": "sketch_circle", "plane": {"offsetFrom": "Front Plane", "offsetMm": 5}, "radiusMm": 2},+            {"op": "extrude", "throughAll": true, "direction": "both"},+            {"op": "sketch_circle", "face": {"atPointMm": [0, 10, 0]}, "radiusMm": 2},+            {"op": "fillet", "radiusMm": 1, "edges": {"allOfFeature": "last"}},+            {"op": "chamfer", "distanceMm": 1, "edges": {"atPointsMm": [[0, 0, 10]]}},+            {"op": "shell", "thicknessMm": 1, "removeFacesAtPointsMm": [[0, 10, 0]], "outward": true},+            {"op": "linear_pattern", "feature": "last", "directions": [{"axis": "x", "count": 2, "spacingMm": 5}, {"axis": "z", "count": 2, "spacingMm": 5}]},+            {"op": "circular_pattern", "feature": "last", "axis": "y", "count": 4},+            {"op": "set_appearance", "rgb": [1, 2, 3], "target": {"bodyName": "Boss-Extrude1"}},+            {"op": "set_material", "name": "ABS"}+        ]));+        assert_eq!(ids(&o[0], serde_json::json!({})), ["new_part"]);+        assert_eq!(ids(&o[1], serde_json::json!({})), ["sketch.plane.front", "sketch.rect"]);+        assert_eq!(ids(&o[2], serde_json::json!({"dir": {"value": false}})), ["extrude.one"]);+        assert_eq!(ids(&o[3], serde_json::json!({})), ["sketch.plane.right", "sketch.circle", "sketch.multiProfile"]);+        // The flag SOLIDWORKS was given decides — reported by the COM host...+        assert_eq!(ids(&o[4], serde_json::json!({"dir": {"value": true}})), ["extrude.one.reversed"]);+        // ...and the caller's `reverse` where a backend does not report it.+        assert_eq!(ids(&o[4], serde_json::json!({})), ["extrude.one.reversed"]);+        assert_eq!(ids(&o[2], serde_json::json!({"dir": {"value": true}})), ["extrude.one.reversed"], "a face's flip is the reversed call too");+        assert_eq!(ids(&o[5], serde_json::json!({})), ["sketch.offset.top.negative", "sketch.circle"]);+        assert_eq!(ids(&o[6], serde_json::json!({"dir": {"value": true}})), ["cut.one.throughAll.reversed"]);+        assert_eq!(ids(&o[7], serde_json::json!({})), ["sketch.offset.other", "sketch.circle"]);+        assert_eq!(ids(&o[8], serde_json::json!({})), ["extrude.both.throughAll"]);+        assert_eq!(+            ids(&o[9], serde_json::json!({"resolvedPlane": {"offsetFrom": "Top Plane", "selectedVia": "MultiSelect2(typed array)"}})),+            ["sketch.face.top", "sketch.circle", "select.multiSelectTyped"]+        );+        assert_eq!(ids(&o[9], serde_json::json!({"resolvedPlane": {"offsetFrom": "Front Plane"}}))[0], "sketch.face.side");+        assert_eq!(+            ids(&o[10], serde_json::json!({"route": "FeatureFillet3", "edges": {"selectedVia": "MultiSelect2(typed array)"}})),+            ["fillet.allOfFeature", "fillet.FeatureFillet3", "select.multiSelectTyped"]+        );+        assert_eq!(ids(&o[10], serde_json::json!({"route": "CreateFeature(ISimpleFilletFeatureData2)"})), ["fillet.allOfFeature", "fillet.createDefinition"]);+        assert_eq!(+            ids(&o[11], serde_json::json!({"edges": {"selectedVia": ["IEntity.Select4", "SelectByRay"]}})),+            ["chamfer.atPoints", "select.entitySelect4", "select.selectByRay"]+        );+        assert_eq!(ids(&o[12], serde_json::json!({"selectedVia": "SelectByID2 at the point (view-dependent)"})), ["shell.outward", "select.selectByIdAtPoint"]);+        assert_eq!(ids(&o[13], serde_json::json!({})), ["linear_pattern.x+z"]);+        assert_eq!(ids(&o[14], serde_json::json!({})), ["circular_pattern.y"]);+        assert_eq!(ids(&o[15], serde_json::json!({"route": "part (single-body part)"})), ["set_appearance.part"]);+        assert_eq!(ids(&o[15], serde_json::json!({"route": "body Boss-Extrude1"})), ["set_appearance.body"]);+        assert_eq!(ids(&o[16], serde_json::json!({"appearanceReapplied": []})), ["set_material"]);+        assert_eq!(ids(&o[16], serde_json::json!({"appearanceReapplied": [{"target": "Part"}]})), ["set_material", "set_material.reappliesAppearance"]);+    }++    #[test]+    fn proven_is_live_only_where_every_variant_was_measured_on_this_release() {+        let v = |ids: &[&str]| ids.iter().map(|s| s.to_string()).collect::<Vec<_>>();+        // The coordinator's shape, on SOLIDWORKS 2026.+        let p = proof(&v(&["sketch.face.top", "sketch.circle", "select.multiSelectTyped"]), Some(34));+        assert_eq!(p["live"], true);+        assert_eq!(p["measuredOn"], serde_json::json!([LIVE_2026_10_14.to_json()]));+        assert!(p.get("why").is_none() && p.get("unmeasured").is_none());+        // The same step on SOLIDWORKS 2021: measured elsewhere, not live here.+        let p = proof(&v(&["sketch.face.top", "sketch.circle"]), Some(29));+        assert_eq!(p["live"], false);+        assert_eq!(p["measuredOn"].as_array().map(|a| a.len()), Some(1));+        assert!(p["why"].as_str().unwrap_or("").contains("this is SOLIDWORKS 2021"), "{p}");+        // A version that could not be read is never live.+        assert_eq!(proof(&v(&["new_part"]), None)["live"], false);+        // ONE unmeasured variant makes the step unmeasured, and says which.+        let p = proof(&v(&["fillet.allOfFeature", "fillet.createDefinition"]), Some(34));+        assert_eq!(p["live"], false);+        assert_eq!(p["measuredOn"], serde_json::json!([]));+        assert_eq!(p["unmeasured"][0]["variant"], "fillet.createDefinition");+        // A variant the table does not know has never been built live.+        let p = proof(&v(&["linear_pattern.x+z"]), Some(34));+        assert_eq!(p["live"], false);+        assert!(p["unmeasured"][0]["what"].as_str().unwrap_or("").contains("no live check"));+        // The table, as describe and selftest report it.+        let t = live_table(Some(29));+        assert_eq!(t["variants"].as_array().map(|a| a.len()), Some(OP_FACTS.len()));+        assert!(t["variants"].as_array().unwrap().iter().all(|r| r["liveOnThisInstall"] == false), "nothing is live on 2021 yet");+        let t = live_table(Some(34));+        let live = t["variants"].as_array().unwrap().iter().filter(|r| r["liveOnThisInstall"] == true).count();+        assert_eq!(live, t["measuredVariants"].as_u64().unwrap() as usize);+        assert!(live_table(None)["variants"][0].get("liveOnThisInstall").is_none());+    } }
src/selftest.rs+4
@@ -240,9 +240,13 @@ fn finish(level: &str, sw: &Value, ready: bool, running: bool, started: Instant,         Some(first) => format!("{first} {send}"),         None => send.to_string(),     };+    // The SAME table step results' `proven` comes from (compat::OP_FACTS),+    // each row saying whether it is live on THIS install's release.+    let major = sw.pointer("/versionSupport/majorVersion").and_then(|m| m.as_u64()).map(|m| m as u32);     crate::verbs::ok(json!({         "level": level,         "bridgeVersion": crate::BRIDGE_VERSION,+        "liveMeasured": crate::compat::live_table(major),         "solidworks": {             "versionSupport": sw.get("versionSupport"),             "edition": sw.get("edition"),
src/verbs.rs+3
@@ -3153,6 +3153,9 @@ fn swx_describe(_ctx: &Ctx, _args: &Value) -> Value {         "mock": crate::is_mock(),         "verbs": verbs,         "scriptOps": crate::script::OPS,+        // What each modelling op variant has been MEASURED on, live — the one+        // table step results' `proven` and swx_selftest report too.+        "liveMeasured": crate::compat::live_table(None),         "exportFormats": crate::verbs3::export_format_note(),         "userWrites": crate::writes::posture(),         "writePolicy": "DENY BY DEFAULT. This bridge writes only to documents it created itself \
src/verbs3.rs+25−1
@@ -4605,8 +4605,12 @@ pub fn run_script(ctx: &Ctx, args: &Value) -> Value {             }             out         }-        Ok(r) => {+        Ok(mut r) => {             crate::ledger::record_time("swx_run_script", started.elapsed().as_secs_f64());+            // WHAT IS MEASURED (0.10.4): each step says whether the variant it ran+            // was measured live on THIS SOLIDWORKS release — one table,+            // compat::OP_FACTS (docs/LIVE-RESULTS.md).+            attach_proofs(&mut r, &ops);             if let Some(t) = r                 .get("created")                 .and_then(|c| c.get("created").and_then(|x| x.get("title")).or_else(|| c.get("title")))@@ -4654,6 +4658,26 @@ pub fn run_script(ctx: &Ctx, args: &Value) -> Value {     } } +/// `proven` on every step of a run_script reply, from compat::step_proof and+/// the release the host reports (`solidworksRevision`).+fn attach_proofs(r: &mut Value, ops: &[crate::script::Op]) {+    let major = r.get("solidworksRevision").and_then(|v| v.as_str()).and_then(crate::compat::major_of);+    if let Some(steps) = r.get_mut("steps").and_then(|s| s.as_array_mut()) {+        for st in steps.iter_mut() {+            let Some(op) = st.get("step").and_then(|n| n.as_u64()).and_then(|n| (n as usize).checked_sub(1)).and_then(|i| ops.get(i)) else {+                continue;+            };+            let proof = crate::compat::step_proof(op, st.get("result").unwrap_or(&Value::Null), major);+            match st.get_mut("result") {+                Some(Value::Object(m)) => {+                    m.insert("proven".into(), proof);+                }+                _ => st["result"] = json!({"proven": proof}),+            }+        }+    }+}+ /// One place for the DSL's documentation, so a rejected script teaches the /// caller the grammar instead of just saying no. fn script_help(message: impl Into<String>) -> Value {
tests/issue1.rs+69
@@ -359,3 +359,72 @@ fn selections_after_a_pick_climb_the_ladder_and_say_which_rung_worked() {         s(&r)     ); }++/// 0.10.4 — PROVEN LIVE. tools/dsl-live-check.py ran 30/30 on SOLIDWORKS 2026+/// (docs/LIVE-RESULTS.md). A step now says `proven: {live, measuredOn}` from+/// ONE table (compat::OP_FACTS) — live only for a variant measured on the+/// release it ran on; swx_describe and swx_selftest report the same table.+#[test]+fn steps_say_what_was_measured_live_on_this_release() {+    let (b, _m) = boot("issue1-proven");+    let script = json!([+        {"op": "new_part"},+        {"op": "sketch_rect", "plane": "Top Plane", "widthMm": 20, "heightMm": 20, "anchor": "center"},+        {"op": "extrude", "depthMm": 10},+        {"op": "sketch_circle", "face": {"atPointMm": [0, 10, 0]}, "radiusMm": 3},+        {"op": "extrude", "depthMm": 2},+        {"op": "shell", "thicknessMm": 1, "removeFacesAtPointsMm": [[5, 0, 5]]},+        {"op": "sketch_rect", "plane": "Front Plane", "widthMm": 4, "heightMm": 4, "anchor": "center", "yMm": 5},+        {"op": "extrude", "depthMm": 3, "reverse": true}+    ]);+    // The simulated install is SOLIDWORKS 2026, the release that was measured.+    std::fs::write(b.scratch.join("mock-revision"), "34.3.0").unwrap();+    let r = b.call("swx_run_script", json!({"script": script}));+    check!(b, "the script runs", r["success"] == true, s(&r));+    let st = r["steps"].as_array().cloned().unwrap_or_default();+    let measured = json!([{"solidworks": 2026, "major": 34, "edition": "connected", "date": "2026-10-14", "bridge": "0.10.3"}]);+    check!(+        b,+        "measured variants are LIVE on 2026, with the measurement: new part, Top Plane rect, boss, face sketch, shell",+        [0usize, 1, 2, 3, 4, 5].iter().all(|i| st[*i]["result"]["proven"]["live"] == true && st[*i]["result"]["proven"]["measuredOn"] == measured),+        s(&r)+    );+    check!(+        b,+        "the shell reports its route, like fillet and chamfer",+        st[5]["result"]["route"] == "InsertFeatureShell",+        s(&st[5])+    );+    check!(+        b,+        "a REVERSED boss was never built live: live:false, and the step says which variant is unmeasured",+        st[7]["result"]["proven"]["live"] == false+            && st[7]["result"]["proven"]["unmeasured"][0]["variant"] == "extrude.one.reversed",+        s(&st[7])+    );+    // The same script on SOLIDWORKS 2021: nothing is live there yet.+    std::fs::write(b.scratch.join("mock-revision"), "29.5.1").unwrap();+    let r = b.call("swx_run_script", json!({"script": script}));+    let st = r["steps"].as_array().cloned().unwrap_or_default();+    check!(+        b,+        "on 2021 every step is live:false, measured on 2026 only — and says so",+        r["success"] == true+            && st.iter().all(|x| x["result"]["proven"]["live"] == false)+            && st[2]["result"]["proven"]["measuredOn"] == measured+            && st[2]["result"]["proven"]["why"].as_str().unwrap_or("").contains("this is SOLIDWORKS 2021"),+        s(&r)+    );+    // One table: describe and selftest report what the steps report.+    let d = b.call("swx_describe", json!({}));+    let t = b.call("swx_selftest", json!({"level": "read"}));+    let rows = |v: &Value| v["liveMeasured"]["variants"].as_array().map(|a| a.len()).unwrap_or(0);+    check!(+        b,+        "swx_describe and swx_selftest carry the same table; the selftest's rows say what is live on THIS install (2021: nothing)",+        rows(&d) > 20 && rows(&d) == rows(&t)+            && d["liveMeasured"]["variants"][0]["variant"] == t["liveMeasured"]["variants"][0]["variant"]+            && t["liveMeasured"]["variants"].as_array().map(|a| a.iter().all(|r| r["liveOnThisInstall"] == false)).unwrap_or(false),+        format!("{}\n{}", s(&d["liveMeasured"]), s(&t["liveMeasured"]))+    );+}
uninstall.sh