app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
//! SOLIDWORKS VERSION FACTS (0.10.0) — what the bridge needs from each
//! release, and how it reads which release it is talking to.
//!
//! Issue #1/#2's reporter (Michael) runs SOLIDWORKS 2021 (COM RevisionNumber
//! 29.5.1). Every COM member this bridge calls was checked against its
//! help.solidworks.com "Availability" line (docs/COMPATIBILITY.md); the
//! newest is SOLIDWORKS 2017 (Revision 25), so 2021 is SUPPORTED. TESTED: 3DEXPERIENCE 2026 (ours) and,
//! since 2026-10-15, SOLIDWORKS 2021 (a community swx_selftest run, 0.10.9).
/// The oldest SOLIDWORKS this bridge supports, by the API audit.
pub const SUPPORTED_MIN_MAJOR: u32 = 29; // SOLIDWORKS 2021
/// The release the bridge has actually run against.
/// SOLIDWORKS 2026 (3DEXPERIENCE, ours) and 2021 (classic desktop: a community
/// run of swx_selftest {level:"write"}, 2026-10-15, bridge 0.10.9).
pub const TESTED_MAJOR: &[u32] = &[34, 29];
/// `RevisionNumber` "29.5.1" -> 29.
pub fn major_of(revision: &str) -> Option<u32> {
// The first run of digits ("29.5.1", or the mock's "mock 33.0.0 (...)").
let start = revision.find(|c: char| c.is_ascii_digit())?;
let digits: String = revision[start..].chars().take_while(|c| c.is_ascii_digit()).collect();
digits.parse().ok().filter(|m: &u32| *m >= 5 && *m < 200)
}
/// The marketing year of a major version: 29 -> 2021 (SOLIDWORKS 2001Plus was
/// revision 10, 2021 is 29).
pub fn year_of(major: u32) -> u32 {
major + 1992
}
/// The major version of a marketing year: 2021 -> 29.
pub fn major_of_year(year: u32) -> u32 {
year.saturating_sub(1992)
}
/// The members whose availability is newer than the oldest SOLIDWORKS the
/// bridge would otherwise run on, with the major version each needs. Kept as
/// data so `swx_selftest` and `swx_status` can say what a given install lacks.
pub const GATED: &[(&str, u32)] = &[
("IFeatureManager::FeatureCut4", 25),
("ISldWorks::GetCurrentLicenseType", 25),
// The selection ladder's third rung (0.10.3) — skipped below 2017.
("IModelDocExtension::SelectByRay", 25),
("IFeatureManager::FeatureFillet3", 23),
("IFeatureManager::FeatureLinearPattern4", 23),
("ISldWorks::StartupProcessCompleted", 23),
("IInterferenceDetectionMgr::UseTransform", 23),
("IFeatureManager::FeatureExtrusion3", 22),
("ICustomPropertyManager::Add3", 22),
("IFeatureManager::FeatureCircularPattern4", 21),
];
/// What a given major version lacks (empty for anything >= 2017).
pub fn missing_on(major: u32) -> Vec<&'static str> {
GATED.iter().filter(|(_, need)| major < *need).map(|(m, _)| *m).collect()
}
/// The `solidworks` block's version facts.
pub fn describe(revision: Option<&str>) -> serde_json::Value {
let major = revision.and_then(major_of);
serde_json::json!({
"revision": revision,
"majorVersion": major,
"year": major.map(year_of),
"supported": major.map(|m| m >= SUPPORTED_MIN_MAJOR),
"tested": major.map(|m| TESTED_MAJOR.contains(&m)),
"supportedFrom": year_of(SUPPORTED_MIN_MAJOR),
"testedOn": TESTED_MAJOR.iter().map(|m| year_of(*m)).collect::<Vec<_>>(),
"lacks": major.map(missing_on),
})
}
// ---------------------------------------------------------------------------
// 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,
/// Who ran it: "adom" (our own machine) or "community" (a user's install,
/// their reply sent back).
pub by: &'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, "by": self.by})
}
}
/// 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", by: "adom" };
/// The same day, bridge 0.10.4: eight more checks added to
/// 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", by: "adom" };
/// 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", by: "adom" };
/// 2026-10-15, bridge 0.10.9, SOLIDWORKS 2021 (major 29, classic desktop,
/// COM 29.5.1, Windows 11): a COMMUNITY run — Michael's swx_selftest
/// {level:"write"} reply (issue #1). Every modelling check passed (T01-T22,
/// T24-T32), the LEGO brick to the same numbers as 2026; the 6 failures were
/// one bug (titles and HideFileExt, fixed in 0.10.10), in verbs, not ops.
pub const LIVE_2021_10_15: Measurement = Measurement { year: 2021, major: 29, edition: "desktop", date: "2026-10-15", bridge: "0.10.9", by: "community" };
/// 2026-10-15 22:38Z, bridge 0.11.2, SOLIDWORKS 2026, Drew's laptop with
/// Windows' "Hide extensions for known file types" ON (HideFileExt=1,
/// SOLIDWORKS restarted) — Michael's 2021 condition, reproduced: the 0.10.10
/// title fix, proven.
pub const LIVE_2026_10_15_HIDEEXT: Measurement = Measurement { year: 2026, major: 34, edition: "connected", date: "2026-10-15", bridge: "0.11.2", by: "adom" };
/// 2026-10-15 23:04Z, bridge 0.11.2, SOLIDWORKS 2026 (34.3.0, "Connected"),
/// Drew's laptop: THE FIRST LIVE CSWA RUN — tools/cswa-check.py, 8 of 8 parts
/// within tolerance, every one within 0.0015 % of its published key
/// (tools/cswa/reports/cswa-2026-10-15-sw2026-0.11.2.json; docs/LIVE-RESULTS.md).
/// Every variant those eight scripts used is measured by it — the lines and
/// arcs of the Tool Block's profile, the face-offset plane, the plain hole,
/// the density, the sketch on a +Z face among them. The same install, bridge
/// and day as the HideFileExt run (equal as values; `checks` says which run).
pub const LIVE_2026_10_15_CSWA: Measurement = Measurement { year: 2026, major: 34, edition: "connected", date: "2026-10-15", bridge: "0.11.2", by: "adom" };
/// The first canary (0.11.4-rc1): 14 CSWA parts, 12 within 0.1 % (cswa-00..10), two caught the draft flag
/// and the up-to-surface id (cswa-11, cswa-12). The variants listed on it were exercised by PASSING parts.
pub const LIVE_2026_10_16_RC1: Measurement = Measurement { year: 2026, major: 34, edition: "connected", date: "2026-10-16", bridge: "0.11.4-rc1", by: "adom" };
const M2026_2021: &[Measurement] = &[LIVE_2026_10_14, LIVE_2021_10_15];
const M2026_2021_CSWA: &[Measurement] = &[LIVE_2026_10_14, LIVE_2021_10_15, LIVE_2026_10_15_CSWA];
const M2026_HIDEEXT: &[Measurement] = &[LIVE_2026_10_15_HIDEEXT];
const M2026C_2021_CSWA: &[Measurement] = &[LIVE_2026_10_14_C, LIVE_2021_10_15, LIVE_2026_10_15_CSWA];
const M2026_CSWA: &[Measurement] = &[LIVE_2026_10_15_CSWA];
const M2026_RC1: &[Measurement] = &[LIVE_2026_10_16_RC1];
const M2026B: &[Measurement] = &[LIVE_2026_10_14_B];
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_2021 },
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) — 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; the 8 CSWA parts in one part, tools/cswa-check.py 2026-10-15", measured: M2026C_2021_CSWA },
OpFact { id: "sketch.plane.front", what: "a sketch on Front Plane", checks: "T01 T03 T10 T14 T27; 7 CSWA parts, tools/cswa-check.py 2026-10-15", measured: M2026_2021_CSWA },
OpFact { id: "sketch.plane.top", what: "a sketch on Top Plane", checks: "T02 T13 T24 T30; the LEGO brick, tools/cswa-check.py 2026-10-15", measured: M2026_2021_CSWA },
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 },
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; the LEGO brick, tools/cswa-check.py 2026-10-15", measured: M2026_2021_CSWA },
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_2021 },
OpFact { id: "sketch.offset.front.positive", what: "a sketch on a plane offset + from Front Plane", checks: "T38", measured: M2026B },
OpFact { id: "sketch.offset.front.negative", what: "a sketch on a plane offset − from Front Plane", checks: "not in the live battery", measured: UNMEASURED },
OpFact { id: "sketch.offset.right.positive", what: "a sketch on a plane offset + from Right Plane", checks: "not in the live battery", measured: UNMEASURED },
OpFact { id: "sketch.offset.right.negative", what: "a sketch on a plane offset − from Right Plane", checks: "not in the live battery", measured: UNMEASURED },
OpFact { id: "sketch.offset.named", what: "a sketch on a plane offset from a named plane", checks: "not in the live battery", measured: UNMEASURED },
OpFact { id: "sketch.face.top.positive", what: "a sketch on a face facing +Y (a top face), picked by a point", checks: "T06 T20 T21 T28 T32 T39", measured: M2026_2021 },
OpFact { id: "sketch.face.top.negative", what: "a sketch on a face facing −Y (a bottom face, hidden in an iso view)", checks: "T25", measured: M2026_2021 },
OpFact { id: "sketch.face.right.positive", what: "a sketch on a face facing +X (keeps Right Plane's axes, the origin projected)", checks: "T36", measured: M2026B },
OpFact { id: "sketch.face.right.negative", what: "a sketch on a face facing −X", checks: "not in the live battery", measured: UNMEASURED },
OpFact { id: "sketch.face.front.negative", what: "a sketch on a face facing −Z (keeps Front Plane's axes, the origin projected)", checks: "T37", measured: M2026B },
OpFact { id: "sketch.face.front.positive", what: "a sketch on a face facing +Z", checks: "the Tool Block step 3's two cuts from the front face, tools/cswa-check.py 2026-10-15", measured: M2026_CSWA },
OpFact { id: "sketch.rect", what: "a rectangle (corner or center anchor)", checks: "T01 T02 T03; 7 CSWA parts, tools/cswa-check.py 2026-10-15", measured: M2026_2021_CSWA },
OpFact { id: "sketch.circle", what: "a circle", checks: "T04 T06 T30; the CSWA holes and slot, tools/cswa-check.py 2026-10-15", measured: M2026_2021_CSWA },
OpFact { id: "sketch.multiProfile", what: "several profiles in one sketch (a ring, disjoint rectangles, the LEGO tubes)", checks: "T13 T14 T24; the LEGO brick and the Tool Block (its Ø14 in the profile), tools/cswa-check.py 2026-10-15", measured: M2026_2021_CSWA },
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; every CSWA part, tools/cswa-check.py 2026-10-15", measured: M2026_2021_CSWA },
OpFact { id: "extrude.one.reversed", what: "a boss whose direction flag is reversed (reverse:true: from Top Plane it goes −Y)", checks: "T33", measured: M2026B },
OpFact { id: "extrude.both", what: "a boss both ways (depthMm and depth2Mm)", checks: "T10", measured: M2026_2021 },
OpFact { id: "extrude.midPlane", what: "a boss symmetric about the sketch plane", checks: "T11", measured: M2026_2021 },
OpFact { id: "extrude.one.throughAll", what: "a boss through all, one way", checks: "T34", measured: M2026B },
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; the Tool Block step 3's cuts, tools/cswa-check.py 2026-10-15", measured: M2026_2021_CSWA },
OpFact { id: "cut.one.reversed", what: "a cut one way, reversed (reverse:true)", checks: "T08 T24; the LEGO brick, tools/cswa-check.py 2026-10-15", measured: M2026_2021_CSWA },
OpFact { id: "cut.one.throughAll.reversed", what: "a cut through all, reversed", checks: "T09", measured: M2026_2021 },
OpFact { id: "cut.both", what: "a cut both ways (depthMm along the cut's own direction, depth2Mm the other)", checks: "T27", measured: M2026_2021 },
OpFact { id: "cut.midPlane", what: "a cut symmetric about the sketch plane", checks: "T12", measured: M2026_2021 },
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); the Tool Block step 3's face picks, tools/cswa-check.py 2026-10-15", measured: M2026_2021_CSWA },
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_2021 },
OpFact { id: "fillet.atPoints", what: "a fillet on edges picked by points", checks: "T32 (swx_selftest)", measured: M2026_2021 },
OpFact { id: "fillet.FeatureFillet3", what: "the fillet made by IFeatureManager::FeatureFillet3", checks: "T17 T30 T32", measured: M2026_2021 },
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_2021 },
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_2021 },
OpFact { id: "shell.outward", what: "a shell outward (the original block becomes the cavity)", checks: "T42", measured: M2026B },
OpFact { id: "linear_pattern.x", what: "a linear pattern along X, one direction (positive or negative spacing)", checks: "T20 T28", measured: M2026_2021 },
OpFact { id: "linear_pattern.x+z", what: "a linear pattern in two directions, X and Z (positive and negative spacing)", checks: "T39", measured: M2026B },
OpFact { id: "circular_pattern.y", what: "a circular pattern about Y", checks: "T21", measured: M2026_2021 },
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_2021 },
OpFact { id: "set_appearance.face", what: "a colour on one face picked by a point", checks: "T16", measured: M2026_2021 },
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; every CSWA part (ABS, 6061 Alloy, AISI 1020), tools/cswa-check.py 2026-10-15", measured: M2026_2021_CSWA },
OpFact { id: "set_material.reappliesAppearance", what: "a material after a colour: the bridge re-applies the colour", checks: "T31 (swx_selftest)", measured: M2026_2021 },
OpFact { id: "set_prop", what: "a custom property", checks: "T24", measured: M2026_2021 },
// ── 0.11.0, CSWA phase 1: built and simulated, NOT YET RUN LIVE ──
OpFact { id: "sketch.line", what: "lines of a closed contour (line, polyline, polygon, slot, rounded rect): ISketchManager::CreateLine", checks: "the Tool Block steps 1-3 and the Q9 L-bracket's polyline, tools/cswa-check.py 2026-10-15", measured: M2026_CSWA },
OpFact { id: "sketch.arc", what: "arcs of a closed contour (arc, arc3, tangentArc, slot, rounded rect): ISketchManager::CreateArc", checks: "the Tool Block steps 1-3 (tangentArc R19, arc3 R29 and R5), tools/cswa-check.py 2026-10-15", measured: M2026_CSWA },
OpFact { id: "sketch.faceOffset", what: "a sketch on a plane offset from a picked face (along its outward normal)", checks: "the Tool Block step 3's R41 slot, 12 inside the back face, tools/cswa-check.py 2026-10-15", measured: M2026_CSWA },
OpFact { id: "mirror.features.faceOffset", what: "features mirrored about a plane offset from a face", checks: "not run live (0.11.0)", measured: UNMEASURED },
OpFact { id: "mirror.bodies.faceOffset", what: "every body mirrored about a plane offset from a face", checks: "not run live (0.11.0)", measured: UNMEASURED },
OpFact { id: "sketch.construction", what: "construction geometry (CreateCenterLine; arcs with ConstructionGeometry)", checks: "tools/cswa-check.py 2026-10-16 (canary 0.11.4-rc1): cswa-09 within 0.1 % of its key", measured: M2026_RC1 },
OpFact { id: "extrude.upToNext", what: "a boss up to the next face (swEndCondUpToNext 11)", checks: "not run live (0.11.0)", measured: UNMEASURED },
OpFact { id: "extrude.upToSurface", what: "a boss up to a picked face (swEndCondUpToSurface 4, the face at mark 1)", checks: "CAUGHT 2026-10-16 (canary 0.11.4-rc1, cswa-12): with swEndCondUpToSelection 10 the boss stopped at the NEXT face (3392.9 mm3 short); now 4, the macro recorder's id, with a read-back of the stored end condition; not yet run live", measured: UNMEASURED },
OpFact { id: "extrude.offsetFromSurface", what: "a boss to a distance from a picked face (swEndCondOffsetFromSurface 5)", checks: "not run live (0.11.0)", measured: UNMEASURED },
OpFact { id: "extrude.draft", what: "a boss with draft (Dchk1/Ddir1/Dang1; Ddir1 TRUE = outward, measured)", checks: "CAUGHT 2026-10-16 (canary 0.11.4-rc1, cswa-12): Ddir1 as the help reads it widened the 5 deg boss; the flag is now passed as SOLIDWORKS measured it; not yet run live", measured: UNMEASURED },
OpFact { id: "cut.upToNext", what: "a cut up to the next face", checks: "not run live (0.11.0)", measured: UNMEASURED },
OpFact { id: "cut.upToSurface", what: "a cut up to a picked face (swEndCondUpToSurface 4, the face at mark 1)", checks: "CAUGHT 2026-10-16 (canary 0.11.4-rc1, cswa-12): with 10 the cut went one plate deep instead of to the face (1005.3 mm3 short); now 4; not yet run live", measured: UNMEASURED },
OpFact { id: "cut.offsetFromSurface", what: "a cut to a distance from a picked face", checks: "tools/cswa-check.py 2026-10-16 (canary 0.11.4-rc1): cswa-08 within 0.1 % of its key", measured: M2026_RC1 },
OpFact { id: "cut.draft", what: "a cut with draft (a countersink is one; Ddir1 TRUE = outward, measured)", checks: "CAUGHT 2026-10-16 (canary 0.11.4-rc1, cswa-11): the 45 deg countersink cut widened into the outward frustum, 804.25 mm3 more than the inward one; fixed with the boss; not yet run live", measured: UNMEASURED },
OpFact { id: "revolve.360", what: "a full revolve about a line of the sketch (FeatureRevolve2)", checks: "tools/cswa-check.py 2026-10-16 (canary 0.11.4-rc1): cswa-09 within 0.1 % of its key", measured: M2026_RC1 },
OpFact { id: "revolve.angle", what: "a revolve through less than 360°", checks: "not run live (0.11.0)", measured: UNMEASURED },
OpFact { id: "revolve.midPlane", what: "a revolve symmetric about the sketch plane", checks: "not run live (0.11.0)", measured: UNMEASURED },
OpFact { id: "revolve_cut.360", what: "a full revolve-cut (FeatureRevolve2 with IsCut)", checks: "tools/cswa-check.py 2026-10-16 (canary 0.11.4-rc1): cswa-09 within 0.1 % of its key", measured: M2026_RC1 },
OpFact { id: "revolve_cut.angle", what: "a revolve-cut through less than 360°", checks: "not run live (0.11.0)", measured: UNMEASURED },
OpFact { id: "revolve_cut.midPlane", what: "a revolve-cut symmetric about the sketch plane", checks: "tools/cswa-check.py 2026-10-16 (canary 0.11.4-rc1): cswa-09 within 0.1 % of its key", measured: M2026_RC1 },
OpFact { id: "mirror.features.plane", what: "features mirrored about a named plane (InsertMirrorFeature2, features mark 1, plane mark 2)", checks: "tools/cswa-check.py 2026-10-16 (canary 0.11.4-rc1): cswa-10 within 0.1 % of its key", measured: M2026_RC1 },
OpFact { id: "mirror.features.offset", what: "features mirrored about an offset plane", checks: "tools/cswa-check.py 2026-10-16 (canary 0.11.4-rc1): cswa-10 within 0.1 % of its key", measured: M2026_RC1 },
OpFact { id: "mirror.features.face", what: "features mirrored about a planar face", checks: "not run live (0.11.0)", measured: UNMEASURED },
OpFact { id: "mirror.bodies.plane", what: "every body mirrored about a named plane (BMirrorBody, bodies mark 256)", checks: "tools/cswa-check.py 2026-10-16 (canary 0.11.4-rc1): cswa-10 within 0.1 % of its key", measured: M2026_RC1 },
OpFact { id: "mirror.bodies.offset", what: "every body mirrored about an offset plane", checks: "not run live (0.11.0)", measured: UNMEASURED },
OpFact { id: "mirror.bodies.face", what: "every body mirrored about a planar face", checks: "not run live (0.11.0)", measured: UNMEASURED },
OpFact { id: "hole.simple", what: "a plain hole: a circle on the face's plane, cut blind or through all", checks: "3dengr Q6 and Q8 (Ø20, Ø30 through all) and the Tool Block step 3's Ø11, tools/cswa-check.py 2026-10-15", measured: M2026_CSWA },
OpFact { id: "hole.counterbore", what: "a counterbored hole (two cuts)", checks: "not run live (0.11.0)", measured: UNMEASURED },
OpFact { id: "hole.countersink", what: "a countersunk hole (a drafted cut, then the hole)", checks: "CAUGHT 2026-10-16 (canary 0.11.4-rc1, cswa-11): its cone opened downward (the draft flag); the hole and the counterbore beside it were right; not yet run live", measured: UNMEASURED },
OpFact { id: "set_material.density", what: "a density in kg/m³ (the document's own, or checked against the material's)", checks: "every CSWA part (1020, 2700, 7900 kg/m³ read back), tools/cswa-check.py 2026-10-15", measured: M2026_CSWA },
// ── 0.11.4: Offset Entities, sketch regions, and the through-all cut
// the community plate exercises: built and simulated, NOT YET RUN LIVE ──
OpFact { id: "sketch.offsetEntities.outer", what: "a face's OUTER loop offset into the sketch: its edges selected, IModelDoc2::SketchOffsetEntities2 (side checked, redone the other way if SOLIDWORKS put it outside)", checks: "tools/cswa-check.py 2026-10-16 (canary 0.11.4-rc1): cswa-08 within 0.1 % of its key", measured: M2026_RC1 },
OpFact { id: "sketch.offsetEntities.face", what: "every loop of a face offset into the sketch: the face selected, IModelDoc2::SketchOffsetEntities2", checks: "not run live (0.11.4)", measured: UNMEASURED },
OpFact { id: "extrude.regions", what: "a boss of selected sketch regions (EnableContourSelection, SelectByID2 \"SKETCHREGION\" at mark 4)", checks: "not run live (0.11.4)", measured: UNMEASURED },
OpFact { id: "cut.regions", what: "a cut of selected sketch regions (EnableContourSelection, SelectByID2 \"SKETCHREGION\" at mark 4)", checks: "tools/cswa-check.py 2026-10-16 (canary 0.11.4-rc1): cswa-08 within 0.1 % of its key", measured: M2026_RC1 },
OpFact { id: "cut.one.throughAll", what: "a cut through all, the default direction (into a face)", checks: "not run live (0.11.4)", measured: UNMEASURED },
OpFact { id: "titles.hiddenExtensions", what: "documents addressed by title while Windows hides known extensions: swx_new_document reads back 'Part1-…' (no .SLDPRT); swx_design_info and swx_get_parameters by the reported title, the stem and stem+.SLDPRT; swx_set_parameter, swx_export_step, swx_save and swx_close_doc by the stem; nothing left open", checks: "the focused HideFileExt test, 2026-10-15 22:38-22:41Z", measured: M2026_HIDEEXT },
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",
}
}
/// Which way a picked face faces along its parallel default plane's normal
/// (Front +Z, Top +Y, Right +X): the COM host reports the face's normal, the
/// simulator its `outward` sign.
fn face_side(result: &serde_json::Value, base: &str) -> Option<&'static str> {
let axis = match base {
"front" => 2,
"top" => 1,
"right" => 0,
_ => return None,
};
let component = result
.pointer("/resolvedPlane/faceNormal")
.and_then(|n| n.as_array())
.and_then(|n| n.get(axis))
.and_then(|c| c.as_f64())
.or_else(|| result.pointer("/resolvedPlane/outward").and_then(|o| o.as_f64()))?;
if component > 0.5 {
Some("positive")
} else if component < -0.5 {
Some("negative")
} else {
None
}
}
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 { visible: true } => v.push("new_part".into()),
Op::NewPart { visible: false } => v.push("new_part.invisible".into()),
Op::NewAssembly { visible: true } => v.push("new_assembly".into()),
Op::NewAssembly { visible: false } => v.push("new_assembly.invisible".into()),
Op::ClearPart => v.push("clear_part".into()),
Op::Sketch { plane, entities, offsets, .. } => {
v.push(match plane {
PlaneRef::Named(n) => format!("sketch.plane.{}", plane_key(n)),
PlaneRef::Offset { from, offset } => match plane_key(from) {
"named" => "sketch.offset.named".into(),
base => format!("sketch.offset.{base}.{}", if *offset < 0.0 { "negative" } else { "positive" }),
},
PlaneRef::FaceOffset { .. } => "sketch.faceOffset".into(),
PlaneRef::Face { .. } => {
let from = result.pointer("/resolvedPlane/offsetFrom").and_then(|f| f.as_str()).unwrap_or("");
match (plane_key(from), face_side(result, plane_key(from))) {
("named", _) | (_, None) => "sketch.face.unknown".into(),
(base, Some(side)) => format!("sketch.face.{base}.{side}"),
}
}
});
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() + offsets.len() > 1 {
v.push("sketch.multiProfile".into());
}
if entities.iter().any(|e| matches!(e, crate::script::Entity::Line { construction: false, .. })) {
v.push("sketch.line".into());
}
if entities.iter().any(|e| matches!(e, crate::script::Entity::Arc { construction: false, .. })) {
v.push("sketch.arc".into());
}
if entities.iter().any(|e| e.is_construction()) {
v.push("sketch.construction".into());
}
// Offset Entities (0.11.4): by what was SELECTED for SOLIDWORKS —
// the outer loop's edges, or the face.
for loops in [crate::script::OffsetLoops::Outer, crate::script::OffsetLoops::All] {
if offsets.iter().any(|o| o.loops == loops) {
v.push(if loops == crate::script::OffsetLoops::Outer { "sketch.offsetEntities.outer" } else { "sketch.offsetEntities.face" }.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 = match &e.up_to {
Some(crate::script::UpTo::Next) => format!("{kind}.upToNext"),
Some(crate::script::UpTo::Surface { .. }) => format!("{kind}.upToSurface"),
Some(crate::script::UpTo::OffsetFrom { .. }) => format!("{kind}.offsetFromSurface"),
None => format!("{kind}.{dir}"),
};
if e.through_all {
id.push_str(".throughAll");
}
if reversed {
id.push_str(".reversed");
}
v.push(id);
if e.draft.is_some() {
v.push(format!("{kind}.draft"));
}
if !e.regions.is_empty() {
v.push(format!("{kind}.regions"));
}
}
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, .. } => {
// The two directions in either order are the same pattern.
let mut axes: Vec<&str> = dirs.iter().map(|d| axis_name(d.0)).collect();
axes.sort_unstable();
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::Revolve(r) | Op::RevolveCut(r) => {
let kind = if matches!(op, Op::RevolveCut(_)) { "revolve_cut" } else { "revolve" };
let how = if r.mid_plane { "midPlane" } else if r.angle >= std::f64::consts::TAU - 1e-9 { "360" } else { "angle" };
v.push(format!("{kind}.{how}"));
}
Op::Mirror { bodies, plane, .. } => {
let on = match plane {
PlaneRef::Named(_) => "plane",
PlaneRef::Offset { .. } => "offset",
PlaneRef::Face { .. } => "face",
PlaneRef::FaceOffset { .. } => "faceOffset",
};
v.push(format!("mirror.{}.{on}", if *bodies { "bodies" } else { "features" }));
}
Op::Hole { counterbore, countersink, .. } => v.push(
if counterbore.is_some() { "hole.counterbore" } else if countersink.is_some() { "hole.countersink" } else { "hole.simple" }.into(),
),
Op::SetMaterial { name, density, .. } => {
if !name.is_empty() {
v.push("set_material".into());
}
if density.is_some() {
v.push("set_material.density".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 per_variant: Vec<Vec<Measurement>> = Vec::new();
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}));
}
per_variant.push(measured);
}
// A RELEASE COVERS THE STEP when every variant was measured on it — in the
// same run or in different ones (a face sketch measured by 0.10.4's checks
// and the selection rung by 0.10.3's are both measured on 2026).
let mut majors: Vec<u32> = per_variant.iter().flatten().map(|m| m.major).collect();
majors.sort_unstable();
majors.dedup();
let covered: Vec<u32> = if per_variant.is_empty() {
Vec::new()
} else {
majors.into_iter().filter(|mj| per_variant.iter().all(|ms| ms.iter().any(|m| m.major == *mj))).collect()
};
let mut on: Vec<Measurement> = Vec::new();
for m in per_variant.iter().flatten() {
if covered.contains(&m.major) && !on.contains(m) {
on.push(*m);
}
}
let live = major.map(|m| covered.contains(&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 mut years: Vec<String> = on.iter().map(|m| m.year.to_string()).collect();
years.dedup();
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::*;
#[test]
fn revision_numbers_map_to_years() {
assert_eq!(major_of("29.5.1"), Some(29));
assert_eq!(year_of(29), 2021);
assert_eq!(major_of("34.3.0"), Some(34));
assert_eq!(year_of(34), 2026);
assert_eq!(major_of_year(2021), 29);
assert_eq!(major_of(""), None);
assert_eq!(major_of("garbage"), None);
assert_eq!(major_of("mock 33.0.0 (2025 SP0)"), Some(33));
assert!(missing_on(29).is_empty(), "2021 lacks nothing the bridge calls");
assert!(missing_on(24).contains(&"IFeatureManager::FeatureCut4"));
let d = describe(Some("29.5.1"));
assert_eq!(d["supported"], true);
assert_eq!(d["tested"], true, "2021: a community run (0.10.10)");
assert_eq!(d["year"], 2021);
assert_eq!(describe(Some("30.1.0"))["tested"], false, "2022 has never been run");
}
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", "by": "adom"})
);
// 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.positive", "sketch.face.top.negative", "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",
] {
let m = op_fact(id).map(|f| f.measured).unwrap_or_default();
assert!(m.starts_with(M2026_2021), "{id} was measured on 2026, and on 2021 by the community run: {m:?}");
}
// THE FIRST LIVE CSWA RUN (2026-10-15, bridge 0.11.2): EXACTLY the
// variants its eight scripts used (tools/cswa/reports/cswa-2026-10-15-
// sw2026-0.11.2.json, every step's proven.variants), and nothing else.
assert_eq!(
LIVE_2026_10_15_CSWA.to_json(),
serde_json::json!({"solidworks": 2026, "major": 34, "edition": "connected", "date": "2026-10-15", "bridge": "0.11.2", "by": "adom"})
);
let mut by_cswa: Vec<&str> = OP_FACTS.iter().filter(|f| f.measured.contains(&LIVE_2026_10_15_CSWA) && f.checks.contains("tools/cswa-check.py 2026-10-15")).map(|f| f.id).collect();
by_cswa.sort_unstable();
assert_eq!(
by_cswa,
[
"clear_part", "cut.one", "cut.one.reversed", "extrude.one", "hole.simple", "select.multiSelectTyped", "set_material",
"set_material.density", "sketch.arc", "sketch.circle", "sketch.face.front.positive", "sketch.faceOffset", "sketch.line",
"sketch.multiProfile", "sketch.offset.top.positive", "sketch.plane.front", "sketch.plane.top", "sketch.rect",
]
);
// THE FIRST CANARY (2026-10-16, 0.11.4-rc1): EXACTLY the variants its
// twelve PASSING parts exercised and nothing had measured before (the
// report in scratch, cswa-00..10 pass; 11 and 12 caught two bugs).
let mut by_rc1: Vec<&str> = OP_FACTS.iter().filter(|f| f.measured.contains(&LIVE_2026_10_16_RC1)).map(|f| f.id).collect();
by_rc1.sort_unstable();
assert_eq!(
by_rc1,
[
"cut.offsetFromSurface", "cut.regions", "mirror.bodies.plane", "mirror.features.offset", "mirror.features.plane",
"revolve.360", "revolve_cut.360", "revolve_cut.midPlane", "sketch.construction", "sketch.offsetEntities.outer",
]
);
// ...and the five it caught stay unmeasured, saying so, until a run proves the fix.
for id in ["extrude.upToSurface", "cut.upToSurface", "extrude.draft", "cut.draft", "hole.countersink"] {
let f = op_fact(id).unwrap();
assert!(f.measured.is_empty() && f.checks.starts_with("CAUGHT 2026-10-16"), "{id}: {:?} / {}", f.measured, f.checks);
}
for id in ["sketch.line", "sketch.arc", "sketch.faceOffset", "hole.simple", "set_material.density", "sketch.face.front.positive"] {
assert_eq!(op_fact(id).map(|f| f.measured), Some(M2026_CSWA), "{id}: first measured by the CSWA run");
}
// 2021 (0.10.10): Michael's community run, and EXACTLY the facts its
// selftest checks (T01-T22, T24-T32) measured — plus new_part and the
// shared part's clear_part; nothing from T33+ (not in the self-test).
assert_eq!(
LIVE_2021_10_15.to_json(),
serde_json::json!({"solidworks": 2021, "major": 29, "edition": "desktop", "date": "2026-10-15", "bridge": "0.10.9", "by": "community"})
);
let mut on_2021: Vec<&str> = OP_FACTS.iter().filter(|f| f.measured.iter().any(|m| m.major == 29)).map(|f| f.id).collect();
on_2021.sort_unstable();
assert_eq!(on_2021.len(), 32, "{on_2021:?}");
for id in ["sketch.plane.right", "extrude.one.reversed", "shell.outward", "linear_pattern.x+z"] {
assert!(!on_2021.contains(&id), "{id} is a T33+ check, never run on 2021");
}
assert!(TESTED_MAJOR.contains(&29) && TESTED_MAJOR.contains(&34));
// ...and the eight checks added with 0.10.4 (T33-T39, T42), measured the same day.
for id in [
"extrude.one.reversed", "extrude.one.throughAll", "sketch.plane.right", "sketch.face.right.positive",
"sketch.face.front.negative", "sketch.offset.front.positive", "linear_pattern.x+z", "shell.outward",
] {
assert_eq!(op_fact(id).map(|f| f.measured), Some(M2026B), "{id} was measured on 2026 by 0.10.4's checks");
}
assert_eq!(
LIVE_2026_10_14_B.to_json(),
serde_json::json!({"solidworks": 2026, "major": 34, "edition": "connected", "date": "2026-10-14", "bridge": "0.10.4", "by": "adom"})
);
// ...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() && f.checks != "not run live (0.11.0)" && f.checks != "not run live (0.11.4)")
.map(|f| f.id)
.collect();
unmeasured.sort_unstable();
// 0.11.4's, still unrun after the first canary (it measured cut.regions and the
// outer-loop Offset Entities through cswa-08; the through-all cut ran only in cswa-11).
let mut new_0114: Vec<&str> = OP_FACTS.iter().filter(|f| f.checks == "not run live (0.11.4)").map(|f| f.id).collect();
assert!(OP_FACTS.iter().filter(|f| f.checks == "not run live (0.11.4)").all(|f| f.measured.is_empty()));
new_0114.sort_unstable();
assert_eq!(new_0114, ["cut.one.throughAll", "extrude.regions", "sketch.offsetEntities.face"]);
// 0.11.0's CSWA ops: every one of them NOT YET RUN LIVE, and listed.
let mut new_ops: Vec<&str> = OP_FACTS.iter().filter(|f| f.checks == "not run live (0.11.0)").map(|f| f.id).collect();
assert!(OP_FACTS.iter().filter(|f| f.checks == "not run live (0.11.0)").all(|f| f.measured.is_empty()));
new_ops.sort_unstable();
assert_eq!(
new_ops,
[
// (the 2026-10-16 canary measured eight of 0.11.0's and caught five; see by_rc1 below)
"cut.upToNext", "extrude.offsetFromSurface", "extrude.upToNext", "hole.counterbore", "mirror.bodies.face",
"mirror.bodies.faceOffset", "mirror.bodies.offset", "mirror.features.face", "mirror.features.faceOffset",
"revolve.angle", "revolve.midPlane", "revolve_cut.angle",
]
);
assert_eq!(
unmeasured,
[
"chamfer.allOfFeature", "cut.draft", "cut.upToSurface", "extrude.both.throughAll", "extrude.draft",
"extrude.upToSurface", "fillet.createDefinition", "hole.countersink", "new_assembly",
"new_assembly.invisible", "new_part.invisible", "rebuild",
"save_as", "select.entitySelect4", "select.selectByIdAtPoint", "select.selectByRay", "set_appearance.body",
"sketch.face.right.negative", "sketch.offset.front.negative",
"sketch.offset.named", "sketch.offset.right.negative", "sketch.offset.right.positive", "sketch.plane.named",
"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.front.positive", "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", "faceNormal": [0.0, 1.0, 0.0], "selectedVia": "MultiSelect2(typed array)"}})),
["sketch.face.top.positive", "sketch.circle", "select.multiSelectTyped"]
);
// The face's own normal (COM) or its outward sign (the simulator) says which side.
let face = |from: &str, n: serde_json::Value| ids(&o[9], serde_json::json!({"resolvedPlane": {"offsetFrom": from, "faceNormal": n}}))[0].clone();
assert_eq!(face("Top Plane", serde_json::json!([0.0, -1.0, 0.0])), "sketch.face.top.negative");
assert_eq!(face("Right Plane", serde_json::json!([1.0, -0.0, -0.0])), "sketch.face.right.positive", "T36");
assert_eq!(face("Front Plane", serde_json::json!([-0.0, -0.0, -1.0])), "sketch.face.front.negative", "T37");
assert_eq!(face("Front Plane", serde_json::json!([0.0, 0.0, 1.0])), "sketch.face.front.positive");
assert_eq!(ids(&o[9], serde_json::json!({"resolvedPlane": {"offsetFrom": "Right Plane", "outward": -1}}))[0], "sketch.face.right.negative");
assert_eq!(face("Top Plane", serde_json::json!(null)), "sketch.face.unknown", "no side, no claim");
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"]);
let zx = ops(serde_json::json!([{"op": "linear_pattern", "feature": "last", "directions": [{"axis": "z", "count": 2, "spacingMm": 5}, {"axis": "x", "count": 2, "spacingMm": 5}]}]));
assert_eq!(ids(&zx[0], serde_json::json!({})), ["linear_pattern.x+z"], "either order is the same pattern");
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"]);
}
/// 0.11.4: an offset is classified by what was SELECTED for SOLIDWORKS (the
/// outer loop's edges, or the face); a region pick is its own variant, on
/// top of the end condition; an offset and a circle are two profiles.
#[test]
fn offsets_and_regions_are_their_own_variants() {
let o = ops(serde_json::json!([
{"op": "sketch", "plane": "Front Plane", "entities": [
{"offsetEntities": {"fromFace": {"atPointMm": [0, 0, 10]}, "offsetMm": -1}},
{"circle": {"radiusMm": 4}}]},
{"op": "cut", "regionsAtMm": [[1, 1]], "offsetFromSurface": {"face": {"atPointMm": [0, 0, 0]}, "distanceMm": 2}},
{"op": "sketch", "plane": "Front Plane", "entities": [{"offsetEntities": {"fromFace": {"atPointMm": [0, 0, 10]}, "offsetMm": 1, "loops": "all"}}]},
{"op": "extrude", "depthMm": 2, "regionAtMm": [1, 1]},
{"op": "sketch_rect", "plane": "Front Plane", "widthMm": 2, "heightMm": 2},
{"op": "cut", "throughAll": true}
]));
assert_eq!(ids(&o[0], serde_json::json!({})), ["sketch.plane.front", "sketch.circle", "sketch.multiProfile", "sketch.offsetEntities.outer"]);
assert_eq!(ids(&o[1], serde_json::json!({})), ["cut.offsetFromSurface", "cut.regions"]);
assert_eq!(ids(&o[2], serde_json::json!({})), ["sketch.plane.front", "sketch.offsetEntities.face"], "one offset alone is one profile");
assert_eq!(ids(&o[3], serde_json::json!({})), ["extrude.one", "extrude.regions"]);
assert_eq!(ids(&o[5], serde_json::json!({"dir": {"value": false}})), ["cut.one.throughAll"]);
// The canary of 2026-10-16 measured the offset cut and its regions (cswa-08); a step
// using the unrun ones (a boss of regions) is live:false and names it.
let p = proof(&ids(&o[1], serde_json::json!({})), Some(34));
assert_eq!(p["live"], true, "{p}");
let p = proof(&ids(&o[3], serde_json::json!({})), Some(34));
assert_eq!(p["live"], false);
assert_eq!(p["unmeasured"].as_array().map(|a| a.len()), Some(1), "{p}");
}
#[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.positive", "sketch.circle", "select.multiSelectTyped"]), Some(34));
assert_eq!(p["live"], true);
assert_eq!(p["measuredOn"], serde_json::json!([LIVE_2026_10_14.to_json(), LIVE_2021_10_15.to_json(), LIVE_2026_10_15_CSWA.to_json()]));
assert!(p.get("why").is_none() && p.get("unmeasured").is_none());
// ...and on SOLIDWORKS 2021 too, by the community run (0.10.10).
assert_eq!(proof(&v(&["sketch.face.top.positive", "sketch.circle"]), Some(29))["live"], true);
// A step measured only by 0.10.4's T33+ checks, on 2021: measured
// elsewhere, not live here.
let p = proof(&v(&["sketch.face.right.positive", "sketch.circle"]), Some(29));
assert_eq!(p["live"], false);
assert_eq!(p["measuredOn"], serde_json::json!([LIVE_2026_10_14_B.to_json(), LIVE_2026_10_14.to_json(), LIVE_2026_10_15_CSWA.to_json()]));
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");
// Variants measured in DIFFERENT runs on the same release cover the step,
// and every run is listed (0.10.4's, 0.10.3's, and the CSWA run's).
let p = proof(&v(&["sketch.face.right.positive", "sketch.circle", "select.multiSelectTyped"]), Some(34));
assert_eq!(p["live"], true, "{p}");
assert_eq!(p["measuredOn"], serde_json::json!([LIVE_2026_10_14_B.to_json(), LIVE_2026_10_14.to_json(), LIVE_2026_10_15_CSWA.to_json()]));
// A variant the table does not know has never been built live.
let p = proof(&v(&["linear_pattern.x+y"]), 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_eq!(t["variants"].as_array().unwrap().iter().filter(|r| r["liveOnThisInstall"] == true).count(), 32, "on 2021: the 32 variants the community run measured");
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());
}
}