main
Drew Owens 0.11.4 source 8889116 1d ago
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());
    }
}