app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
Drew Owens
0.9.0: bridge data moved out of the install cache (recovery survives updates), closed-in-place records, gates route thr…
5d02ca9
20d ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
//! ADVERSARIAL REVIEW OF 0.8.2 — pass thirty-two.
//!
//! WHAT CHECKS OUT
//!
//! * An unknown `route` is refused BEFORE any COM call — it is the first thing
//! `cmd_import` looks at, ahead of every other override (mock.rs:2556).
//! * The overrides do not bypass ownership: the refusal for `dissolve` is
//! evaluated after route selection and keyed on the TOGGLE, not the route,
//! so `route:"opendoc6"` cannot smuggle a dissolve past it.
//! * `interconnect_blocks_import` is keyed on `route == "auto"`, so an
//! explicit route still measures. That is the point of the overrides and it
//! is right.
//! * The `auto` refusal sits AFTER the `net > 0` ActiveDoc-recovery branch
//! returns (com.rs:3920 vs :3964), so a future edition whose LoadFile4
//! accepts the file returns through the recovery path and IS registered and
//! claimed. The coordinator's (3) is sound.
//! * S1's byte binding catches a file that GREW as well as one that shrank —
//! `was != now`, not `now < was`.
//! * The sweep-folder fix is real and it is the root cause of the 1-in-12
//! flake I reported in pass 30: two sweeps a fraction of a second apart
//! shared a folder, the second manifest overwrote the first, and a second
//! sweep that preserved nothing DELETED it. Good find.
//!
//! WHAT DOES NOT.
mod common;
use common::*;
use serde_json::json;
fn s(v: &serde_json::Value) -> String {
serde_json::to_string_pretty(v).unwrap_or_default()
}
/// SEVERITY 1, CONSENT — THE REFUSAL TELLS ITS READER TO CHANGE THE USER'S
/// SETTING, IN THE IMPERATIVE, WITH NO INSTRUCTION TO ASK FIRST.
///
/// `interconnect_blocks_import` carries:
///
/// > "Turn it off in SOLIDWORKS: Tools > Options > System Options > Import >
/// > uncheck "Enable 3D Interconnect", then import again … THIS BRIDGE DOES NOT
/// > CHANGE USER SETTINGS, not even temporarily."
///
/// The second sentence is the bridge's promise about ITSELF. The first is an
/// instruction, and the thing that reads it is an AI agent with a desktop bridge
/// and the ability to drive the SOLIDWORKS UI. "Turn it off in Tools > Options"
/// followed by the exact click path is a runbook, not a caveat, and the disclaimer
/// does not tell the reader not to perform it — it says the BRIDGE will not.
///
/// 3D Interconnect is a persistent, global CAD preference that changes how every
/// future import behaves, including imports the user does by hand long after this
/// session. The standing authorization on this machine covers closing documents
/// and quitting SOLIDWORKS; it does not cover reconfiguring Options. The
/// difference between "ask the user to turn it off" and "turn it off" is the
/// whole of consent here, and it is one word.
///
/// The fix is that word: address the instruction to the user via the caller —
/// "ASK THE USER to turn it off …" — and keep the click path, which is genuinely
/// useful for the human who will do it.
#[test]
fn the_interconnect_refusal_does_not_instruct_its_reader_to_change_the_setting() {
let scratch = TempDir::new("adv31-consent");
std::fs::write(scratch.join("mock-interconnect-blocks-import"), "").unwrap();
let file = scratch.join("smoke.step");
std::fs::write(&file, b"ISO-10303-21;\nHEADER;\nENDSEC;\nEND-ISO-10303-21;\n").unwrap();
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
let r = b.call("swx_import", json!({"filePath": file.to_string_lossy()}));
assert!(
r["errorCode"] == json!("interconnect_blocks_import"),
"precondition: the measured refusal: {}",
s(&r)
);
// THE REFUSAL'S OWN WORDS ONLY. `sidecar_error` staples a generic `_hint`
// onto every host failure and it happens to contain "(or ask the user to)"
// — about dismissing a modal dialog, a different subject. Matching the whole
// reply would pass on that, which is the wrong-reason failure this review
// exists to catch.
let own: String = [&r["error"], &r["detail"], &r["hostError"]]
.iter()
.map(|v| s(v))
.collect::<Vec<_>>()
.join(" ")
.to_ascii_lowercase();
assert!(
own.contains("ask the user") || own.contains("ask drew") || own.contains("the user must"),
"the refusal says \"Turn it off in SOLIDWORKS: Tools > Options > System Options > Import\" \
— an imperative with the exact click path, read by an agent that can drive the \
SOLIDWORKS UI. The disclaimer that follows is about what the BRIDGE does, not about what \
its reader should do. 3D Interconnect is a persistent global preference affecting every \
future import, including the user's own by hand; the standing authorization here covers \
closing documents, not reconfiguring Options. One word fixes it: ASK the user to turn it \
off. Reply: {}",
s(&r)
);
}
/// SEVERITY 1 — THE BYTE BINDING DISAPPEARS WHEN `recoveryBytes` IS ABSENT.
///
/// ```ignore
/// let changed = match (recorded, bytes) {
/// (Some(was), Some(now)) => was != now,
/// _ => false,
/// };
/// ```
///
/// `_ => false` means "no recorded size — nothing changed", so the manifest's
/// `verified:true` is taken at face value with nothing binding it to the bytes
/// on disk. That is the same trust S1 was added to remove, reachable through any
/// manifest that lacks the field: one written by a bridge that predates it, one
/// hand-edited, one truncated to null by a partial write, or a future
/// `manifestVersion` that renames it — and the listing never looks at
/// `manifestVersion` at all, only that `files` is an array.
///
/// The direction is decided elsewhere in this same release: an unknown must take
/// the strict answer. `reverified_entry` already returns `verified: null` for
/// exactly this situation — bytes that pass what can be checked, with facts the
/// verb cannot recover. A manifest entry with no recorded size is that situation
/// with extra steps, and should get the same `null`, not `true`.
#[test]
fn a_manifest_entry_without_a_recorded_size_is_not_taken_on_trust() {
let scratch = TempDir::new("adv31-nobytes");
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
let root = scratch.join("recovery").join("20260917-000000");
std::fs::create_dir_all(&root).unwrap();
let mut body = vec![0u8; 900];
body[4..8].copy_from_slice(&[0x00, 0x00, 0x00, 0x04]);
// 900 bytes: far under any floor. Whatever the sweep saw, it is not this.
std::fs::write(root.join("bracket.SLDPRT"), &body).unwrap();
std::fs::write(
root.join("manifest.json"),
json!({
"manifestVersion": 1,
"files": [{
"name": "bracket.SLDPRT",
"title": "bracket.SLDPRT",
"kind": "part",
// NO recoveryBytes — the field a older writer never wrote.
"verified": true,
"preserved": true,
"closed": true,
}],
})
.to_string(),
)
.unwrap();
let r = b.call("swx_recover_list", json!({}));
let f = r["sweeps"][0]["files"][0].clone();
assert!(f["source"] == json!("manifest"), "precondition: read from the manifest: {}", s(&f));
assert!(
f["verified"] != json!(true),
"a manifest entry with NO recoveryBytes is trusted completely: verified:true for a \
900-byte file, under every floor this bridge has, because `_ => false` reads a missing \
size as \"unchanged\". verified:true means the document was CLOSED. The listing does not \
check manifestVersion either, so any writer that omits or renames the field silently \
removes the binding S1 added. reverified_entry already answers this exact question with \
null; a manifest entry that cannot be bound deserves the same. Entry: {}",
s(&f)
);
}
/// SEVERITY 2 — `GATE PARTIAL` AND `GATE PASSED` ARE THE SAME EXIT CODE.
///
/// `tools/drain-gate.py` ends:
///
/// ```python
/// if FAILURES: … return 1
/// if not twin_open:
/// print("GATE PARTIAL — … The COLLISION half is UNTESTED this run …")
/// return 0
/// print("GATE PASSED — …")
/// return 0
/// ```
///
/// The gate proves two properties: that `CloseDoc(<full path>)` closes exactly
/// the document it was given, and that a same-titled document survives. When the
/// twin was never open the second is untested, the text says so clearly — and
/// the process exits 0, indistinguishable from the run that proved both. Any
/// caller reading `$?` instead of the prose treats a half-run gate as a pass,
/// which is precisely the vacuity the `--fixture new` redirect makes more
/// likely: that path exists because `interconnect_blocks_import` now blocks the
/// STEP fixture on this machine, so the fixture that cannot raise the collision
/// is becoming the usual one.
///
/// A distinct code — 3 — costs nothing and keeps the prose honest for the human
/// while making the gate honest for the script.
#[test]
fn the_drain_gate_does_not_exit_zero_on_an_untested_half() {
let src = std::fs::read_to_string(
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tools/drain-gate.py"),
)
.expect("tools/drain-gate.py");
let partial = src.find("GATE PARTIAL").expect("the partial verdict exists");
let after = &src[partial..];
let ret = after.find("return").expect("it returns something");
let line: String = after[ret..].chars().take(12).collect();
assert!(
!line.contains("return 0"),
"GATE PARTIAL returns 0, the same code as GATE PASSED. The partial verdict says the \
COLLISION half is untested — one of the two properties the gate exists to prove — and a \
caller reading the exit status cannot tell it from a full pass. The --fixture new \
redirect makes this the likely path now that interconnect_blocks_import blocks the STEP \
fixture on this machine. Found: {line:?}"
);
}
/// SEVERITY 2 — A CODED REFUSAL CARRIES A GENERIC HINT THAT CONTRADICTS IT.
///
/// The whole reply for `interconnect_blocks_import`:
///
/// > `_hint`: "The COM host reported an error. If SOLIDWORKS is showing a modal
/// > dialog, dismiss it (or ask the user to) and retry; swx_status shows whether
/// > SOLIDWORKS is running and attached at all."
/// > `detail`: "Turn it off in SOLIDWORKS: Tools > Options > … Import …"
///
/// There is no dialog. Nothing is hung. The import was refused by a setting that
/// was read successfully, and retrying changes nothing until the user acts. The
/// `_hint` `sidecar_error` staples onto every host failure sends the reader to
/// look for a modal that does not exist, and it is the field agents read first.
///
/// This is the defect 0.7.6 fixed in `recover_list` — a blanket instruction that
/// was wrong for the specific files in the list — in a second place. The fix is
/// the same one: when the host gave a `code`, the coded reason is the hint, and
/// the generic sentence should not compete with it.
#[test]
fn a_coded_refusal_is_not_given_a_hint_about_a_dialog_that_is_not_there() {
let scratch = TempDir::new("adv31-hint");
std::fs::write(scratch.join("mock-interconnect-blocks-import"), "").unwrap();
let file = scratch.join("smoke.step");
std::fs::write(&file, b"ISO-10303-21;\n").unwrap();
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
let r = b.call("swx_import", json!({"filePath": file.to_string_lossy()}));
assert!(
r["errorCode"] == json!("interconnect_blocks_import"),
"precondition: the measured refusal: {}",
s(&r)
);
let hint = r["_hint"].as_str().unwrap_or("").to_ascii_lowercase();
assert!(
!hint.contains("modal dialog") && !hint.contains("dismiss it"),
"a refusal whose cause is a USER SETTING, read successfully, is handed the generic \
host-failure hint: \"If SOLIDWORKS is showing a modal dialog, dismiss it … and retry\". \
There is no dialog, nothing is hung, and retrying changes nothing. `_hint` is the field \
a caller reads first and it contradicts the `detail` directly below it. When the host \
gave a code, the coded reason is the hint. Reply: {}",
s(&r)
);
}