← Commit history
.insiders-stage/fusion-bridge/version.json+4−4
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "fusion360",-  "version": "1.9.351",-  "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.351/adom-bridge-fusion-v1.9.351.zip",-  "sha256": "952c3fefe331f31cfb77b39d4d72cfc198ffce6f6440e60512a844b23738aac3",-  "size": 1662129,+  "version": "1.9.352",+  "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.352/adom-bridge-fusion-v1.9.352.zip",+  "sha256": "8a98eae5d5701d98df966ecc013f7a03cb02271fe96447933d60ae91d6ebb8af",+  "size": 2164901,   "verbPrefixes": [     "fusion_"   ],
.release-page-head+1−1
@@ -1 +1 @@-4a5d97ad1ce0388c66cecc3bf059fc7397bee45b\ No newline at end of file+71367bb995d9d638a8f1bd1a29712b530a8b094d\ No newline at end of file
page.json+26−10
@@ -11,10 +11,6 @@     "uninstall": "./uninstall.sh"   },   "tags": [-    "ab",-    "bridge",-    "fusion",-    "desktop-app",     "fusion360",     "autodesk",     "cad",@@ -79,10 +75,7 @@     "build a basic parts library",     "add a 3d model to a component",     "make a fusion 360 library with 3d",-    "set up the fusion bridge",-    "fusion dashboard",-    "fusion status page",-    "open the fusion launch page"+    "set up the fusion bridge"   ],   "discovery_pitch": "Use the Adom Bridge Fusion 360 bridge when the user wants to DRIVE Autodesk Fusion 360 from the cloud: open/search their team's Fusion cloud designs, lay out a PCB or do electronics, export STEP/IGES/STL/Gerbers/BOM, or build a component library with real rendering 3D. It runs Fusion on the user's own machine via Adom Bridge. NOT for web/browser/login/form-filling (that's the native-browser extension) and NOT for KiCad (that's the kicad bridge).",   "brief": "Drive Autodesk Fusion 360 from the cloud via Adom Bridge: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.",@@ -111,5 +104,28 @@       "label": "Part volume",       "prompt": "How much material does each printed part in this assembly use?"     }-  ]-}+  ],+  "agent_permissions": {+    "allow_bash": [],+    "allow": [+      "adom-desktop-fusion-bridge read-only verbs (via the adom-bridge CLI): fusion_readiness, fusion_get_app_state, fusion_addin_status, fusion_check_dialogs - state queries; they change nothing.",+      "adom-desktop-fusion-bridge read-only verbs: fusion_alerts (reads Fusion's own log for toasts), fusion_document_info, fusion_window_info, fusion_board_info, fusion_library_parts - inspect only.",+      "adom-desktop-fusion-bridge read-only verbs: fusion_prove_result (a persisted run report), fusion_plan_library_import (report-only preflight), fusion_list_cloud_projects, fusion_describe.",+      "adom-desktop-fusion-bridge evidence captures fusion_screenshot_fusion and fusion_capture_library_views read pixels and write local image files only."+    ],+    "soft_deny": [+      "adom-desktop-fusion-bridge lifecycle verbs fusion_stop, fusion_kill, fusion_restart_for_addin close or kill the Fusion application - keep the human prompt.",+      "adom-desktop-fusion-bridge fusion_install_fusion silently installs Autodesk Fusion on the user's machine.",+      "adom-desktop-fusion-bridge fusion_cleanup_cloud_files deletes files from the user's Autodesk cloud hub.",+      "adom-desktop-fusion-bridge managed-library verbs (fusion_managed_library_push/pull/create/link/unlink) publish to or overwrite the user's library.io libraries."+    ],+    "environment": [+      "adom-desktop-fusion-bridge is the Fusion 360 bridge for Adom Bridge; its fusion_* verbs are invoked through the adom-bridge CLI and drive Autodesk Fusion on the user's own desktop.",+      "adom-desktop-fusion-bridge desktop actions also pass Adom Bridge's own human-approval gate (caller identity, per-call reason, Activity Log) - a container approval is the first of two gates."+    ]+  },+  "author": {+    "name": "John Lauer",+    "email": "[email protected]"+  }+}\ No newline at end of file
rust/Cargo.lock+230−5
@@ -10,9 +10,11 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"  [[package]] name = "adom-fusion-bridge"-version = "1.9.351"+version = "1.9.352" dependencies = [+ "chrono",  "fusion-core",+ "fusion-lbr",  "fusion-os-mac",  "fusion-os-win",  "serde_json",@@ -29,6 +31,15 @@ dependencies = [  "memchr", ] +[[package]]+name = "android_system_properties"+version = "0.1.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc"+dependencies = [+ "libc",+]+ [[package]] name = "arbitrary" version = "1.4.2"@@ -44,6 +55,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" +[[package]]+name = "autocfg"+version = "1.5.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"+ [[package]] name = "base64" version = "0.22.1"@@ -72,12 +89,29 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]]+name = "chrono"+version = "0.4.45"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"+dependencies = [+ "iana-time-zone",+ "num-traits",+ "windows-link",+]+ [[package]] name = "chunked_transfer" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" +[[package]]+name = "core-foundation-sys"+version = "0.8.7"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"+ [[package]] name = "crc32fast" version = "1.5.1"@@ -149,7 +183,7 @@ dependencies = [  [[package]] name = "fusion-core"-version = "1.9.351"+version = "1.9.352" dependencies = [  "serde",  "serde_json",@@ -159,7 +193,7 @@ dependencies = [  [[package]] name = "fusion-lbr"-version = "1.9.351"+version = "1.9.352" dependencies = [  "regex",  "roxmltree",@@ -171,19 +205,43 @@ dependencies = [  [[package]] name = "fusion-os-mac"-version = "1.9.351"+version = "1.9.352" dependencies = [  "fusion-core", ]  [[package]] name = "fusion-os-win"-version = "1.9.351"+version = "1.9.352" dependencies = [  "fusion-core",  "windows-sys 0.59.0", ] +[[package]]+name = "futures-core"+version = "0.3.34"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"++[[package]]+name = "futures-task"+version = "0.3.34"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"++[[package]]+name = "futures-util"+version = "0.3.34"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"+dependencies = [+ "futures-core",+ "futures-task",+ "pin-project-lite",+ "slab",+]+ [[package]] name = "getrandom" version = "0.2.17"@@ -207,6 +265,30 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]]+name = "iana-time-zone"+version = "0.1.65"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"+dependencies = [+ "android_system_properties",+ "core-foundation-sys",+ "iana-time-zone-haiku",+ "js-sys",+ "log",+ "wasm-bindgen",+ "windows-core",+]++[[package]]+name = "iana-time-zone-haiku"+version = "0.1.2"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"+dependencies = [+ "cc",+]+ [[package]] name = "icu_collections" version = "2.3.0"@@ -327,6 +409,17 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]]+name = "js-sys"+version = "0.3.104"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a"+dependencies = [+ "cfg-if",+ "futures-util",+ "wasm-bindgen",+]+ [[package]] name = "libc" version = "0.2.189"@@ -361,6 +454,15 @@ dependencies = [  "simd-adler32", ] +[[package]]+name = "num-traits"+version = "0.2.19"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"+dependencies = [+ "autocfg",+]+ [[package]] name = "once_cell" version = "1.21.4"@@ -373,6 +475,12 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]]+name = "pin-project-lite"+version = "0.2.17"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"+ [[package]] name = "potential_utf" version = "0.1.6"@@ -484,6 +592,12 @@ dependencies = [  "untrusted", ] +[[package]]+name = "rustversion"+version = "1.0.23"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"+ [[package]] name = "serde" version = "1.0.229"@@ -520,6 +634,7 @@ version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [+ "indexmap",  "itoa",  "memchr",  "serde",@@ -539,6 +654,12 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" +[[package]]+name = "slab"+version = "0.4.12"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"+ [[package]] name = "smallvec" version = "1.16.0"@@ -706,6 +827,51 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]]+name = "wasm-bindgen"+version = "0.2.127"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70"+dependencies = [+ "cfg-if",+ "once_cell",+ "rustversion",+ "wasm-bindgen-macro",+ "wasm-bindgen-shared",+]++[[package]]+name = "wasm-bindgen-macro"+version = "0.2.127"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1"+dependencies = [+ "quote",+ "wasm-bindgen-macro-support",+]++[[package]]+name = "wasm-bindgen-macro-support"+version = "0.2.127"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284"+dependencies = [+ "bumpalo",+ "proc-macro2",+ "quote",+ "syn 2.0.119",+ "wasm-bindgen-shared",+]++[[package]]+name = "wasm-bindgen-shared"+version = "0.2.127"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf"+dependencies = [+ "unicode-ident",+]+ [[package]] name = "webpki-roots" version = "0.26.11"@@ -724,6 +890,65 @@ dependencies = [  "rustls-pki-types", ] +[[package]]+name = "windows-core"+version = "0.62.2"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"+dependencies = [+ "windows-implement",+ "windows-interface",+ "windows-link",+ "windows-result",+ "windows-strings",+]++[[package]]+name = "windows-implement"+version = "0.60.2"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"+dependencies = [+ "proc-macro2",+ "quote",+ "syn 2.0.119",+]++[[package]]+name = "windows-interface"+version = "0.59.3"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"+dependencies = [+ "proc-macro2",+ "quote",+ "syn 2.0.119",+]++[[package]]+name = "windows-link"+version = "0.2.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"++[[package]]+name = "windows-result"+version = "0.4.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"+dependencies = [+ "windows-link",+]++[[package]]+name = "windows-strings"+version = "0.5.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"+dependencies = [+ "windows-link",+]+ [[package]] name = "windows-sys" version = "0.52.0"
rust/lbr/tests/fixtures/syn_deadconnect.lbradded+155
@@ -0,0 +1,155 @@+<?xml version="1.0" encoding="utf-8"?>+<!DOCTYPE eagle SYSTEM "eagle.dtd">+<eagle version="9.6.2">+<drawing>+<settings><setting alwaysvectorfont="no"/></settings>+<grid distance="0.1" unitdist="inch" unit="inch" style="lines" multiple="1" display="no" altdistance="0.01" altunitdist="inch" altunit="inch"/>+<layers>+<layer number="1" name="Top" color="4" fill="1" visible="yes" active="yes"/>+<layer number="16" name="Bottom" color="1" fill="1" visible="yes" active="yes"/>+<layer number="17" name="Pads" color="2" fill="1" visible="yes" active="yes"/>+<layer number="18" name="Vias" color="2" fill="1" visible="yes" active="yes"/>+<layer number="19" name="Unrouted" color="6" fill="1" visible="yes" active="yes"/>+<layer number="20" name="Dimension" color="15" fill="1" visible="yes" active="yes"/>+<layer number="21" name="tPlace" color="7" fill="1" visible="yes" active="yes"/>+<layer number="22" name="bPlace" color="7" fill="1" visible="yes" active="yes"/>+<layer number="23" name="tOrigins" color="15" fill="1" visible="yes" active="yes"/>+<layer number="24" name="bOrigins" color="15" fill="1" visible="yes" active="yes"/>+<layer number="25" name="tNames" color="7" fill="1" visible="yes" active="yes"/>+<layer number="26" name="bNames" color="7" fill="1" visible="yes" active="yes"/>+<layer number="27" name="tValues" color="7" fill="1" visible="yes" active="yes"/>+<layer number="28" name="bValues" color="7" fill="1" visible="yes" active="yes"/>+<layer number="29" name="tStop" color="7" fill="3" visible="no" active="yes"/>+<layer number="30" name="bStop" color="7" fill="6" visible="no" active="yes"/>+<layer number="31" name="tCream" color="7" fill="4" visible="no" active="yes"/>+<layer number="32" name="bCream" color="7" fill="5" visible="no" active="yes"/>+<layer number="33" name="tFinish" color="6" fill="3" visible="no" active="yes"/>+<layer number="34" name="bFinish" color="6" fill="6" visible="no" active="yes"/>+<layer number="35" name="tGlue" color="7" fill="4" visible="no" active="yes"/>+<layer number="36" name="bGlue" color="7" fill="5" visible="no" active="yes"/>+<layer number="37" name="tTest" color="7" fill="1" visible="no" active="yes"/>+<layer number="38" name="bTest" color="7" fill="1" visible="no" active="yes"/>+<layer number="39" name="tKeepout" color="4" fill="11" visible="no" active="yes"/>+<layer number="40" name="bKeepout" color="1" fill="11" visible="no" active="yes"/>+<layer number="41" name="tRestrict" color="4" fill="10" visible="no" active="yes"/>+<layer number="42" name="bRestrict" color="1" fill="10" visible="no" active="yes"/>+<layer number="43" name="vRestrict" color="2" fill="10" visible="no" active="yes"/>+<layer number="44" name="Drills" color="7" fill="1" visible="no" active="yes"/>+<layer number="45" name="Holes" color="7" fill="1" visible="no" active="yes"/>+<layer number="46" name="Milling" color="3" fill="1" visible="no" active="yes"/>+<layer number="47" name="Measures" color="7" fill="1" visible="no" active="yes"/>+<layer number="48" name="Document" color="7" fill="1" visible="yes" active="yes"/>+<layer number="49" name="Reference" color="7" fill="1" visible="yes" active="yes"/>+<layer number="91" name="Nets" color="2" fill="1" visible="yes" active="yes"/>+<layer number="92" name="Busses" color="1" fill="1" visible="yes" active="yes"/>+<layer number="93" name="Pins" color="2" fill="1" visible="no" active="yes"/>+<layer number="94" name="Symbols" color="4" fill="1" visible="yes" active="yes"/>+<layer number="95" name="Names" color="7" fill="1" visible="yes" active="yes"/>+<layer number="96" name="Values" color="7" fill="1" visible="yes" active="yes"/>+<layer number="97" name="Info" color="7" fill="1" visible="yes" active="yes"/>+<layer number="98" name="Guide" color="6" fill="1" visible="yes" active="yes"/>+</layers>+<library>+<description>USB serial converter, UART, SOIC-16</description>+<packages>+<package name="SOIC-16_3.9x9.9mm_P1.27mm">+  <description>SOIC-16, 3.9 x 9.9 mm, 1.27 mm pitch</description>+  <smd name="1" x="-2.4750" y="4.4450" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="2" x="-2.4750" y="3.1750" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="3" x="-2.4750" y="1.9050" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="4" x="-2.4750" y="0.6350" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="5" x="-2.4750" y="-0.6350" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="6" x="-2.4750" y="-1.9050" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="7" x="-2.4750" y="-3.1750" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="8" x="-2.4750" y="-4.4450" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="9" x="2.4750" y="-4.4450" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="10" x="2.4750" y="-3.1750" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="11" x="2.4750" y="-1.9050" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="12" x="2.4750" y="-0.6350" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="13" x="2.4750" y="0.6350" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="14" x="2.4750" y="1.9050" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="15" x="2.4750" y="3.1750" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="16" x="2.4750" y="4.4450" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <polygon width="0.1200" layer="21">+    <vertex x="-2.6000" y="5.0100"/>+    <vertex x="-2.8400" y="5.3400"/>+    <vertex x="-2.3600" y="5.3400"/>+  </polygon>+  <polygon width="0.1000" layer="51">+    <vertex x="-0.9750" y="4.9500"/>+    <vertex x="1.9500" y="4.9500"/>+    <vertex x="1.9500" y="-4.9500"/>+    <vertex x="-1.9500" y="-4.9500"/>+    <vertex x="-1.9500" y="3.9750"/>+  </polygon>+  <wire x1="-2.0600" y1="5.0600" x2="2.0600" y2="5.0600" width="0.1200" layer="21"/>+  <wire x1="-2.0600" y1="5.0050" x2="-2.0600" y2="5.0600" width="0.1200" layer="21"/>+  <wire x1="-2.0600" y1="-5.0600" x2="-2.0600" y2="-5.0050" width="0.1200" layer="21"/>+  <wire x1="2.0600" y1="5.0600" x2="2.0600" y2="5.0050" width="0.1200" layer="21"/>+  <wire x1="2.0600" y1="-5.0050" x2="2.0600" y2="-5.0600" width="0.1200" layer="21"/>+  <wire x1="2.0600" y1="-5.0600" x2="-2.0600" y2="-5.0600" width="0.1200" layer="21"/>+  <text x="0" y="6.020" size="1.27" layer="25" align="bottom-center">&gt;NAME</text>+  <text x="0" y="-6.020" size="1.27" layer="27" align="top-center">&gt;VALUE</text>+</package>+</packages>+<symbols>+<symbol name="CH340G">+  <description>USB serial converter, UART, SOIC-16</description>+  <wire x1="-12.7" y1="15.239999999999998" x2="12.7" y2="15.239999999999998" width="0.254" layer="94"/>+  <wire x1="12.7" y1="15.239999999999998" x2="12.7" y2="-15.239999999999998" width="0.254" layer="94"/>+  <wire x1="12.7" y1="-15.239999999999998" x2="-12.7" y2="-15.239999999999998" width="0.254" layer="94"/>+  <wire x1="-12.7" y1="-15.239999999999998" x2="-12.7" y2="15.239999999999998" width="0.254" layer="94"/>+  <text x="-5.0800" y="13.9700" size="1.27" layer="95" align="center">&gt;NAME</text>+  <text x="1.2700" y="13.9700" size="1.27" layer="96" align="center">&gt;VALUE</text>+  <pin name="GND" x="-17.78" y="-7.620000000000001" length="middle" direction="pwr"/>+  <pin name="TXD" x="17.78" y="12.7" length="middle" direction="out" rot="R180"/>+  <pin name="RXD" x="-17.78" y="10.16" length="middle" direction="in"/>+  <pin name="V3" x="-17.78" y="-10.16" length="middle" direction="out"/>+  <pin name="UD+" x="17.78" y="0" length="middle" direction="io" rot="R180"/>+  <pin name="UD-" x="17.78" y="-2.540000000000001" length="middle" direction="io" rot="R180"/>+  <pin name="XI" x="-17.78" y="7.619999999999999" length="middle" direction="in"/>+  <pin name="XO" x="17.78" y="10.16" length="middle" direction="out" rot="R180"/>+  <pin name="CTS" x="-17.78" y="5.079999999999999" length="middle" direction="in"/>+  <pin name="DSR" x="-17.78" y="0" length="middle" direction="in"/>+  <pin name="RI" x="-17.78" y="-2.540000000000001" length="middle" direction="in"/>+  <pin name="DCD" x="-17.78" y="2.539999999999999" length="middle" direction="in"/>+  <pin name="DTR" x="17.78" y="7.619999999999999" length="middle" direction="out" rot="R180"/>+  <pin name="RTS" x="17.78" y="5.079999999999999" length="middle" direction="out" rot="R180"/>+  <pin name="R232" x="-17.78" y="12.7" length="middle" direction="in"/>+  <pin name="VCC" x="-17.78" y="-12.7" length="middle" direction="pwr"/>+</symbol>+</symbols>+<devicesets>+<deviceset name="CH340G" prefix="U">+  <description>USB serial converter, UART, SOIC-16</description>+  <gates>+    <gate name="G$1" symbol="CH340G" x="0" y="0"/>+  </gates>+  <devices>+    <device name="" package="SOIC-16_3.9x9.9mm_P1.27mm">+      <connects>+        <connect gate="G$1" pin="NOPE_GND" pad="1"/>+        <connect gate="G$1" pin="TXD" pad="2"/>+        <connect gate="G$1" pin="RXD" pad="3"/>+        <connect gate="G$1" pin="V3" pad="4"/>+        <connect gate="G$1" pin="UD+" pad="5"/>+        <connect gate="G$1" pin="UD-" pad="6"/>+        <connect gate="G$1" pin="XI" pad="7"/>+        <connect gate="G$1" pin="XO" pad="8"/>+        <connect gate="G$1" pin="CTS" pad="9"/>+        <connect gate="G$1" pin="DSR" pad="10"/>+        <connect gate="G$1" pin="RI" pad="11"/>+        <connect gate="G$1" pin="DCD" pad="12"/>+        <connect gate="G$1" pin="DTR" pad="13"/>+        <connect gate="G$1" pin="RTS" pad="14"/>+        <connect gate="G$1" pin="R232" pad="15"/>+        <connect gate="G$1" pin="VCC" pad="16"/>+      </connects>+      <technologies><technology name=""/></technologies>+    </device>+  </devices>+</deviceset>+</devicesets>+</library>+</drawing>+</eagle>\ No newline at end of file
rust/lbr/tests/fixtures/syn_duppad.lbradded+155
@@ -0,0 +1,155 @@+<?xml version="1.0" encoding="utf-8"?>+<!DOCTYPE eagle SYSTEM "eagle.dtd">+<eagle version="9.6.2">+<drawing>+<settings><setting alwaysvectorfont="no"/></settings>+<grid distance="0.1" unitdist="inch" unit="inch" style="lines" multiple="1" display="no" altdistance="0.01" altunitdist="inch" altunit="inch"/>+<layers>+<layer number="1" name="Top" color="4" fill="1" visible="yes" active="yes"/>+<layer number="16" name="Bottom" color="1" fill="1" visible="yes" active="yes"/>+<layer number="17" name="Pads" color="2" fill="1" visible="yes" active="yes"/>+<layer number="18" name="Vias" color="2" fill="1" visible="yes" active="yes"/>+<layer number="19" name="Unrouted" color="6" fill="1" visible="yes" active="yes"/>+<layer number="20" name="Dimension" color="15" fill="1" visible="yes" active="yes"/>+<layer number="21" name="tPlace" color="7" fill="1" visible="yes" active="yes"/>+<layer number="22" name="bPlace" color="7" fill="1" visible="yes" active="yes"/>+<layer number="23" name="tOrigins" color="15" fill="1" visible="yes" active="yes"/>+<layer number="24" name="bOrigins" color="15" fill="1" visible="yes" active="yes"/>+<layer number="25" name="tNames" color="7" fill="1" visible="yes" active="yes"/>+<layer number="26" name="bNames" color="7" fill="1" visible="yes" active="yes"/>+<layer number="27" name="tValues" color="7" fill="1" visible="yes" active="yes"/>+<layer number="28" name="bValues" color="7" fill="1" visible="yes" active="yes"/>+<layer number="29" name="tStop" color="7" fill="3" visible="no" active="yes"/>+<layer number="30" name="bStop" color="7" fill="6" visible="no" active="yes"/>+<layer number="31" name="tCream" color="7" fill="4" visible="no" active="yes"/>+<layer number="32" name="bCream" color="7" fill="5" visible="no" active="yes"/>+<layer number="33" name="tFinish" color="6" fill="3" visible="no" active="yes"/>+<layer number="34" name="bFinish" color="6" fill="6" visible="no" active="yes"/>+<layer number="35" name="tGlue" color="7" fill="4" visible="no" active="yes"/>+<layer number="36" name="bGlue" color="7" fill="5" visible="no" active="yes"/>+<layer number="37" name="tTest" color="7" fill="1" visible="no" active="yes"/>+<layer number="38" name="bTest" color="7" fill="1" visible="no" active="yes"/>+<layer number="39" name="tKeepout" color="4" fill="11" visible="no" active="yes"/>+<layer number="40" name="bKeepout" color="1" fill="11" visible="no" active="yes"/>+<layer number="41" name="tRestrict" color="4" fill="10" visible="no" active="yes"/>+<layer number="42" name="bRestrict" color="1" fill="10" visible="no" active="yes"/>+<layer number="43" name="vRestrict" color="2" fill="10" visible="no" active="yes"/>+<layer number="44" name="Drills" color="7" fill="1" visible="no" active="yes"/>+<layer number="45" name="Holes" color="7" fill="1" visible="no" active="yes"/>+<layer number="46" name="Milling" color="3" fill="1" visible="no" active="yes"/>+<layer number="47" name="Measures" color="7" fill="1" visible="no" active="yes"/>+<layer number="48" name="Document" color="7" fill="1" visible="yes" active="yes"/>+<layer number="49" name="Reference" color="7" fill="1" visible="yes" active="yes"/>+<layer number="91" name="Nets" color="2" fill="1" visible="yes" active="yes"/>+<layer number="92" name="Busses" color="1" fill="1" visible="yes" active="yes"/>+<layer number="93" name="Pins" color="2" fill="1" visible="no" active="yes"/>+<layer number="94" name="Symbols" color="4" fill="1" visible="yes" active="yes"/>+<layer number="95" name="Names" color="7" fill="1" visible="yes" active="yes"/>+<layer number="96" name="Values" color="7" fill="1" visible="yes" active="yes"/>+<layer number="97" name="Info" color="7" fill="1" visible="yes" active="yes"/>+<layer number="98" name="Guide" color="6" fill="1" visible="yes" active="yes"/>+</layers>+<library>+<description>USB serial converter, UART, SOIC-16</description>+<packages>+<package name="SOIC-16_3.9x9.9mm_P1.27mm">+  <description>SOIC-16, 3.9 x 9.9 mm, 1.27 mm pitch</description>+  <smd name="1" x="-2.4750" y="4.4450" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="1" x="-2.4750" y="3.1750" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="3" x="-2.4750" y="1.9050" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="4" x="-2.4750" y="0.6350" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="5" x="-2.4750" y="-0.6350" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="6" x="-2.4750" y="-1.9050" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="7" x="-2.4750" y="-3.1750" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="8" x="-2.4750" y="-4.4450" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="9" x="2.4750" y="-4.4450" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="10" x="2.4750" y="-3.1750" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="11" x="2.4750" y="-1.9050" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="12" x="2.4750" y="-0.6350" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="13" x="2.4750" y="0.6350" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="14" x="2.4750" y="1.9050" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="15" x="2.4750" y="3.1750" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <smd name="16" x="2.4750" y="4.4450" dx="1.9500" dy="0.6000" layer="1" roundness="50"/>+  <polygon width="0.1200" layer="21">+    <vertex x="-2.6000" y="5.0100"/>+    <vertex x="-2.8400" y="5.3400"/>+    <vertex x="-2.3600" y="5.3400"/>+  </polygon>+  <polygon width="0.1000" layer="51">+    <vertex x="-0.9750" y="4.9500"/>+    <vertex x="1.9500" y="4.9500"/>+    <vertex x="1.9500" y="-4.9500"/>+    <vertex x="-1.9500" y="-4.9500"/>+    <vertex x="-1.9500" y="3.9750"/>+  </polygon>+  <wire x1="-2.0600" y1="5.0600" x2="2.0600" y2="5.0600" width="0.1200" layer="21"/>+  <wire x1="-2.0600" y1="5.0050" x2="-2.0600" y2="5.0600" width="0.1200" layer="21"/>+  <wire x1="-2.0600" y1="-5.0600" x2="-2.0600" y2="-5.0050" width="0.1200" layer="21"/>+  <wire x1="2.0600" y1="5.0600" x2="2.0600" y2="5.0050" width="0.1200" layer="21"/>+  <wire x1="2.0600" y1="-5.0050" x2="2.0600" y2="-5.0600" width="0.1200" layer="21"/>+  <wire x1="2.0600" y1="-5.0600" x2="-2.0600" y2="-5.0600" width="0.1200" layer="21"/>+  <text x="0" y="6.020" size="1.27" layer="25" align="bottom-center">&gt;NAME</text>+  <text x="0" y="-6.020" size="1.27" layer="27" align="top-center">&gt;VALUE</text>+</package>+</packages>+<symbols>+<symbol name="CH340G">+  <description>USB serial converter, UART, SOIC-16</description>+  <wire x1="-12.7" y1="15.239999999999998" x2="12.7" y2="15.239999999999998" width="0.254" layer="94"/>+  <wire x1="12.7" y1="15.239999999999998" x2="12.7" y2="-15.239999999999998" width="0.254" layer="94"/>+  <wire x1="12.7" y1="-15.239999999999998" x2="-12.7" y2="-15.239999999999998" width="0.254" layer="94"/>+  <wire x1="-12.7" y1="-15.239999999999998" x2="-12.7" y2="15.239999999999998" width="0.254" layer="94"/>+  <text x="-5.0800" y="13.9700" size="1.27" layer="95" align="center">&gt;NAME</text>+  <text x="1.2700" y="13.9700" size="1.27" layer="96" align="center">&gt;VALUE</text>+  <pin name="GND" x="-17.78" y="-7.620000000000001" length="middle" direction="pwr"/>+  <pin name="TXD" x="17.78" y="12.7" length="middle" direction="out" rot="R180"/>+  <pin name="RXD" x="-17.78" y="10.16" length="middle" direction="in"/>+  <pin name="V3" x="-17.78" y="-10.16" length="middle" direction="out"/>+  <pin name="UD+" x="17.78" y="0" length="middle" direction="io" rot="R180"/>+  <pin name="UD-" x="17.78" y="-2.540000000000001" length="middle" direction="io" rot="R180"/>+  <pin name="XI" x="-17.78" y="7.619999999999999" length="middle" direction="in"/>+  <pin name="XO" x="17.78" y="10.16" length="middle" direction="out" rot="R180"/>+  <pin name="CTS" x="-17.78" y="5.079999999999999" length="middle" direction="in"/>+  <pin name="DSR" x="-17.78" y="0" length="middle" direction="in"/>+  <pin name="RI" x="-17.78" y="-2.540000000000001" length="middle" direction="in"/>+  <pin name="DCD" x="-17.78" y="2.539999999999999" length="middle" direction="in"/>+  <pin name="DTR" x="17.78" y="7.619999999999999" length="middle" direction="out" rot="R180"/>+  <pin name="RTS" x="17.78" y="5.079999999999999" length="middle" direction="out" rot="R180"/>+  <pin name="R232" x="-17.78" y="12.7" length="middle" direction="in"/>+  <pin name="VCC" x="-17.78" y="-12.7" length="middle" direction="pwr"/>+</symbol>+</symbols>+<devicesets>+<deviceset name="CH340G" prefix="U">+  <description>USB serial converter, UART, SOIC-16</description>+  <gates>+    <gate name="G$1" symbol="CH340G" x="0" y="0"/>+  </gates>+  <devices>+    <device name="" package="SOIC-16_3.9x9.9mm_P1.27mm">+      <connects>+        <connect gate="G$1" pin="GND" pad="1"/>+        <connect gate="G$1" pin="TXD" pad="2"/>+        <connect gate="G$1" pin="RXD" pad="3"/>+        <connect gate="G$1" pin="V3" pad="4"/>+        <connect gate="G$1" pin="UD+" pad="5"/>+        <connect gate="G$1" pin="UD-" pad="6"/>+        <connect gate="G$1" pin="XI" pad="7"/>+        <connect gate="G$1" pin="XO" pad="8"/>+        <connect gate="G$1" pin="CTS" pad="9"/>+        <connect gate="G$1" pin="DSR" pad="10"/>+        <connect gate="G$1" pin="RI" pad="11"/>+        <connect gate="G$1" pin="DCD" pad="12"/>+        <connect gate="G$1" pin="DTR" pad="13"/>+        <connect gate="G$1" pin="RTS" pad="14"/>+        <connect gate="G$1" pin="R232" pad="15"/>+        <connect gate="G$1" pin="VCC" pad="16"/>+      </connects>+      <technologies><technology name=""/></technologies>+    </device>+  </devices>+</deviceset>+</devicesets>+</library>+</drawing>+</eagle>\ No newline at end of file
rust/lbr/tests/fixtures/syn_noteagle.lbradded+2
@@ -0,0 +1,2 @@+<?xml version="1.0"?>+<html><body>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</body></html>\ No newline at end of file
rust/lbr/tests/fixtures/syn_truncated.lbradded+3
@@ -0,0 +1,3 @@+<?xml version="1.0" encoding="utf-8"?>+<!DOCTYPE eagle SYSTEM "eagle.dtd">+<e\ No newline at end of file
rust/lbr/tests/golden.rs+43
@@ -9,6 +9,8 @@ fn artifact_names_match_the_python_bridge() {     for e in fs::read_dir(dir).unwrap().flatten() {         let p = e.path();         if p.extension().and_then(|x| x.to_str()) != Some("lbr") { continue; }+        // syn_*.lbr are deliberately broken preflight fixtures+        if p.file_name().unwrap().to_string_lossy().starts_with("syn_") { continue; }         let xml = fs::read_to_string(&p).unwrap();         let got = fusion_lbr::artifact_names(&xml).unwrap();         let golden_path = format!("{}/tests/golden/{}.json", env!("CARGO_MANIFEST_DIR"), p.file_stem().unwrap().to_str().unwrap());@@ -119,3 +121,44 @@ fn bundle_staging_matches_python() {     assert!(!tmp.join("evil.lbr").exists());     let _ = fs::remove_dir_all(&tmp); }++#[test]+fn preflight_and_lints_match_the_python_bridge() {+    let root = env!("CARGO_MANIFEST_DIR");+    let g: serde_json::Value = serde_json::from_str(&fs::read_to_string(format!("{root}/tests/golden/preflight_and_parts.json")).unwrap()).unwrap();+    let mut checked = 0;+    for (name, case) in g.as_object().unwrap() {+        if name == "__missing__" { continue; }+        let path = std::path::PathBuf::from(format!("{root}/tests/fixtures/{name}"));+        let pf = fusion_lbr::preflight_lbr(&path);+        let want = &case["preflight"];+        assert_eq!(pf.ok, want["ok"].as_bool().unwrap(), "{name}: preflight ok");+        let want_checks = want["checks"].as_array().unwrap();+        assert_eq!(pf.checks.len(), want_checks.len(), "{name}: check count {:?}", pf.checks);+        for (mine, theirs) in pf.checks.iter().zip(want_checks) {+            assert_eq!(mine.check, theirs["check"].as_str().unwrap(), "{name}: check order");+            assert_eq!(mine.ok, theirs["ok"].as_bool().unwrap(), "{name}: {} ok", mine.check);+            // parser error strings legitimately differ between roxmltree and ElementTree+            if mine.ok || mine.check != "xml-parses" { assert_eq!(mine.detail, theirs["detail"].as_str().unwrap(), "{name}: {} detail", mine.check); }+        }+        assert_eq!(pf.metadata_ok, want["metadataOk"].as_bool(), "{name}: metadataOk");+        assert_eq!(pf.metadata_hint.as_deref(), want["_metadataHint"].as_str(), "{name}: metadata hint");+        if let Some(mw) = &pf.metadata_warnings {+            assert_eq!(serde_json::to_value(mw).unwrap(), want["metadataWarnings"], "{name}: preflight metadata warnings");+        }+        // the standalone lint+        if let Ok(xml) = fs::read_to_string(&path) {+            let lint = fusion_lbr::lint_part_metadata(&xml);+            let want_l = &case["lint"];+            assert_eq!(lint.ok, want_l["ok"].as_bool().unwrap(), "{name}: lint ok");+            if want_l["error"].is_null() {+                assert_eq!(serde_json::to_value(&lint.warnings).unwrap(), want_l["warnings"], "{name}: lint warnings");+                assert_eq!(serde_json::to_value(&lint.totals).unwrap(), want_l["totals"], "{name}: lint totals");+            }+        }+        checked += 1;+    }+    assert!(checked >= 10, "checked {checked}");+    let missing = fusion_lbr::preflight_lbr(std::path::Path::new("C:/nope/missing.lbr"));+    assert!(!missing.ok && missing.checks.len() == 1 && missing.checks[0].check == "file-readable");+}
rust/lbr/tests/golden/preflight_and_parts.jsonadded+2075
@@ -0,0 +1,2075 @@+{+ "ABM8-16.000MHZ-B2-T.lbr": {+  "library_parts": {+   "_hint": "1 deviceset(s), 1 orderable variant(s); 0 carry an MPN and 0 a distributor PN. 3 warning(s) in `warnings` - these do NOT stop the library opening, but an EE cannot order from it as-is. The full orderable identity is deviceset + device + technology (e.g. TJA1050 + T + -AUTO), because EAGLE puts package variants on <device> and grade/temperature variants on <technology> - which is how two variants share geometry and remain different parts.",+   "attributeNames": [+    "MPN",+    "MANUFACTURER",+    "MOUSER_PN",+    "DIGIKEY_PN",+    "LCSC_PN"+   ],+   "devicesets": [+    {+     "description": "Four pin crystal, GND on pins 2 and 4",+     "name": "ABM8-16.000MHZ-B2-T",+     "variants": [+      {+       "attributes": {},+       "device": "",+       "fullName": "ABM8-16.000MHZ-B2-T",+       "missing": [+        "MPN",+        "MANUFACTURER",+        "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+       ],+       "package": "Crystal_SMD_3225-4Pin_3.2x2.5mm",+       "technology": ""+      }+     ]+    }+   ],+   "libraryPath": "ABM8-16.000MHZ-B2-T.lbr",+   "metadataOk": false,+   "success": true,+   "totals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "ABM8-16.000MHZ-B2-T",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "symbol_pins_stacked",+     "detail": "pins 'G' and 'G@2' sit at the same point (0.0, -5.08); one hides the other",+     "symbol": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "generic_description",+     "detail": "description 'Four pin crystal, GND on pins 2 and 4' says the class, not the part (value, package, maker)",+     "deviceset": "ABM8-16.000MHZ-B2-T"+    }+   ]+  },+  "lint": {+   "ok": false,+   "totals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "ABM8-16.000MHZ-B2-T",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "symbol_pins_stacked",+     "detail": "pins 'G' and 'G@2' sit at the same point (0.0, -5.08); one hides the other",+     "symbol": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "generic_description",+     "detail": "description 'Four pin crystal, GND on pins 2 and 4' says the class, not the part (value, package, maker)",+     "deviceset": "ABM8-16.000MHZ-B2-T"+    }+   ]+  },+  "preflight": {+   "_metadataHint": "The library will open, but 3 part-metadata warning(s) mean an EE cannot order from it: MPN / MANUFACTURER / a distributor PN (MOUSER_PN, DIGIKEY_PN, LCSC_PN) are what this bridge's own BOM export reads, and variants that share geometry are told apart ONLY by these. See fusion_library_parts for the per-variant breakdown.",+   "checks": [+    {+     "check": "file-readable",+     "detail": "7565 bytes",+     "ok": true+    },+    {+     "check": "size-sane",+     "detail": "7565 bytes - a real EAGLE library is never this small (truncated transfer?)",+     "ok": true+    },+    {+     "check": "xml-parses",+     "detail": "",+     "ok": true+    },+    {+     "check": "root-is-eagle",+     "detail": "root is <eagle>",+     "ok": true+    },+    {+     "check": "packages-nonempty",+     "detail": "1 package(s)",+     "ok": true+    },+    {+     "check": "symbols-present",+     "detail": "1 symbol(s)",+     "ok": true+    },+    {+     "check": "text-align-valid",+     "detail": "all <text> align values are EAGLE values",+     "ok": true+    },+    {+     "check": "devicesets-present",+     "detail": "1 deviceset(s)",+     "ok": true+    },+    {+     "check": "connects-resolve",+     "detail": "4 connect(s), all resolving",+     "ok": true+    },+    {+     "check": "pads-unique",+     "detail": "all pad names unique per package",+     "ok": true+    }+   ],+   "errors": [],+   "metadataOk": false,+   "metadataWarnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "ABM8-16.000MHZ-B2-T",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "symbol_pins_stacked",+     "detail": "pins 'G' and 'G@2' sit at the same point (0.0, -5.08); one hides the other",+     "symbol": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "generic_description",+     "detail": "description 'Four pin crystal, GND on pins 2 and 4' says the class, not the part (value, package, maker)",+     "deviceset": "ABM8-16.000MHZ-B2-T"+    }+   ],+   "ok": true,+   "partTotals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   }+  }+ },+ "CH340G.lbr": {+  "library_parts": {+   "_hint": "1 deviceset(s), 1 orderable variant(s); 0 carry an MPN and 0 a distributor PN. 1 warning(s) in `warnings` - these do NOT stop the library opening, but an EE cannot order from it as-is. The full orderable identity is deviceset + device + technology (e.g. TJA1050 + T + -AUTO), because EAGLE puts package variants on <device> and grade/temperature variants on <technology> - which is how two variants share geometry and remain different parts.",+   "attributeNames": [+    "MPN",+    "MANUFACTURER",+    "MOUSER_PN",+    "DIGIKEY_PN",+    "LCSC_PN"+   ],+   "devicesets": [+    {+     "description": "USB serial converter, UART, SOIC-16",+     "name": "CH340G",+     "variants": [+      {+       "attributes": {},+       "device": "",+       "fullName": "CH340G",+       "missing": [+        "MPN",+        "MANUFACTURER",+        "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+       ],+       "package": "SOIC-16_3.9x9.9mm_P1.27mm",+       "technology": ""+      }+     ]+    }+   ],+   "libraryPath": "CH340G.lbr",+   "metadataOk": false,+   "success": true,+   "totals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "CH340G",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "CH340G"+    }+   ]+  },+  "lint": {+   "ok": false,+   "totals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "CH340G",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "CH340G"+    }+   ]+  },+  "preflight": {+   "_metadataHint": "The library will open, but 1 part-metadata warning(s) mean an EE cannot order from it: MPN / MANUFACTURER / a distributor PN (MOUSER_PN, DIGIKEY_PN, LCSC_PN) are what this bridge's own BOM export reads, and variants that share geometry are told apart ONLY by these. See fusion_library_parts for the per-variant breakdown.",+   "checks": [+    {+     "check": "file-readable",+     "detail": "9776 bytes",+     "ok": true+    },+    {+     "check": "size-sane",+     "detail": "9776 bytes - a real EAGLE library is never this small (truncated transfer?)",+     "ok": true+    },+    {+     "check": "xml-parses",+     "detail": "",+     "ok": true+    },+    {+     "check": "root-is-eagle",+     "detail": "root is <eagle>",+     "ok": true+    },+    {+     "check": "packages-nonempty",+     "detail": "1 package(s)",+     "ok": true+    },+    {+     "check": "symbols-present",+     "detail": "1 symbol(s)",+     "ok": true+    },+    {+     "check": "text-align-valid",+     "detail": "all <text> align values are EAGLE values",+     "ok": true+    },+    {+     "check": "devicesets-present",+     "detail": "1 deviceset(s)",+     "ok": true+    },+    {+     "check": "connects-resolve",+     "detail": "16 connect(s), all resolving",+     "ok": true+    },+    {+     "check": "pads-unique",+     "detail": "all pad names unique per package",+     "ok": true+    }+   ],+   "errors": [],+   "metadataOk": false,+   "metadataWarnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "CH340G",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "CH340G"+    }+   ],+   "ok": true,+   "partTotals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   }+  }+ },+ "Demo_Board.lbr": {+  "library_parts": {+   "_hint": "5 deviceset(s), 5 orderable variant(s); 0 carry an MPN and 0 a distributor PN. 21 warning(s) in `warnings` - these do NOT stop the library opening, but an EE cannot order from it as-is. The full orderable identity is deviceset + device + technology (e.g. TJA1050 + T + -AUTO), because EAGLE puts package variants on <device> and grade/temperature variants on <technology> - which is how two variants share geometry and remain different parts.",+   "attributeNames": [+    "MPN",+    "MANUFACTURER",+    "MOUSER_PN",+    "DIGIKEY_PN",+    "LCSC_PN"+   ],+   "devicesets": [+    {+     "description": "Microchip LAN7800 SuperSpeed USB 3.1 Gen1 to 10/100/1000 Ethernet Controller, QFN-48",+     "name": "LAN7800",+     "variants": [+      {+       "attributes": {},+       "device": "",+       "fullName": "LAN7800",+       "missing": [+        "MPN",+        "MANUFACTURER",+        "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+       ],+       "package": "QFN-48_7x7mm",+       "technology": ""+      }+     ]+    },+    {+     "description": "USB serial converter, UART, SOIC-16",+     "name": "CH340G",+     "variants": [+      {+       "attributes": {},+       "device": "",+       "fullName": "CH340G",+       "missing": [+        "MPN",+        "MANUFACTURER",+        "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+       ],+       "package": "SOIC-16_3.9x9.9mm_P1.27mm",+       "technology": ""+      }+     ]+    },+    {+     "description": "",+     "name": "STM32L431RCT6TR",+     "variants": [+      {+       "attributes": {},+       "device": "",+       "fullName": "STM32L431RCT6TR",+       "missing": [+        "MPN",+        "MANUFACTURER",+        "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+       ],+       "package": "LQFP-64_10x10mm_P0.5mm",+       "technology": ""+      }+     ]+    },+    {+     "description": "Four pin crystal, GND on pins 2 and 4",+     "name": "ABM8-16.000MHZ-B2-T",+     "variants": [+      {+       "attributes": {},+       "device": "",+       "fullName": "ABM8-16.000MHZ-B2-T",+       "missing": [+        "MPN",+        "MANUFACTURER",+        "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+       ],+       "package": "Crystal_SMD_3225-4Pin_3.2x2.5mm",+       "technology": ""+      }+     ]+    },+    {+     "description": "Light emitting diode",+     "name": "IN-S63BTG",+     "variants": [+      {+       "attributes": {},+       "device": "",+       "fullName": "IN-S63BTG",+       "missing": [+        "MPN",+        "MANUFACTURER",+        "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+       ],+       "package": "IN-S63BTG",+       "technology": ""+      }+     ]+    }+   ],+   "libraryPath": "Demo_Board.lbr",+   "metadataOk": false,+   "success": true,+   "totals": {+    "devicesets": 5,+    "variants": 5,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "LAN7800",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "LAN7800"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "CH340G",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "CH340G"+    },+    {+     "code": "deviceset_no_description",+     "detail": "No <description>. Fusion's Content Manager shows this to whoever places the part; without it they are picking on a symbol outline alone.",+     "deviceset": "STM32L431RCT6TR"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "STM32L431RCT6TR",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "STM32L431RCT6TR"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "ABM8-16.000MHZ-B2-T",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "IN-S63BTG",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "IN-S63BTG"+    },+    {+     "code": "symbol_pins_stacked",+     "detail": "pins 'G' and 'G@2' sit at the same point (0.0, -5.08); one hides the other",+     "symbol": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '1' and '48' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '12' and '13' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '24' and '25' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '36' and '37' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '7' and '8' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '9' and '10' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '23' and '24' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '25' and '26' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '39' and '40' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '41' and '42' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '55' and '56' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '57' and '58' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "generic_description",+     "detail": "description 'Four pin crystal, GND on pins 2 and 4' says the class, not the part (value, package, maker)",+     "deviceset": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "generic_description",+     "detail": "description 'Light emitting diode' says the class, not the part (value, package, maker)",+     "deviceset": "IN-S63BTG"+    }+   ]+  },+  "lint": {+   "ok": false,+   "totals": {+    "devicesets": 5,+    "variants": 5,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "LAN7800",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "LAN7800"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "CH340G",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "CH340G"+    },+    {+     "code": "deviceset_no_description",+     "detail": "No <description>. Fusion's Content Manager shows this to whoever places the part; without it they are picking on a symbol outline alone.",+     "deviceset": "STM32L431RCT6TR"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "STM32L431RCT6TR",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "STM32L431RCT6TR"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "ABM8-16.000MHZ-B2-T",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "IN-S63BTG",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "IN-S63BTG"+    },+    {+     "code": "symbol_pins_stacked",+     "detail": "pins 'G' and 'G@2' sit at the same point (0.0, -5.08); one hides the other",+     "symbol": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '1' and '48' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '12' and '13' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '24' and '25' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '36' and '37' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '7' and '8' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '9' and '10' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '23' and '24' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '25' and '26' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '39' and '40' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '41' and '42' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '55' and '56' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '57' and '58' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "generic_description",+     "detail": "description 'Four pin crystal, GND on pins 2 and 4' says the class, not the part (value, package, maker)",+     "deviceset": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "generic_description",+     "detail": "description 'Light emitting diode' says the class, not the part (value, package, maker)",+     "deviceset": "IN-S63BTG"+    }+   ]+  },+  "preflight": {+   "_metadataHint": "The library will open, but 21 part-metadata warning(s) mean an EE cannot order from it: MPN / MANUFACTURER / a distributor PN (MOUSER_PN, DIGIKEY_PN, LCSC_PN) are what this bridge's own BOM export reads, and variants that share geometry are told apart ONLY by these. See fusion_library_parts for the per-variant breakdown.",+   "checks": [+    {+     "check": "file-readable",+     "detail": "47513 bytes",+     "ok": true+    },+    {+     "check": "size-sane",+     "detail": "47513 bytes - a real EAGLE library is never this small (truncated transfer?)",+     "ok": true+    },+    {+     "check": "xml-parses",+     "detail": "",+     "ok": true+    },+    {+     "check": "root-is-eagle",+     "detail": "root is <eagle>",+     "ok": true+    },+    {+     "check": "packages-nonempty",+     "detail": "5 package(s)",+     "ok": true+    },+    {+     "check": "symbols-present",+     "detail": "5 symbol(s)",+     "ok": true+    },+    {+     "check": "text-align-valid",+     "detail": "all <text> align values are EAGLE values",+     "ok": true+    },+    {+     "check": "devicesets-present",+     "detail": "5 deviceset(s)",+     "ok": true+    },+    {+     "check": "connects-resolve",+     "detail": "135 connect(s), all resolving",+     "ok": true+    },+    {+     "check": "pads-unique",+     "detail": "all pad names unique per package",+     "ok": true+    }+   ],+   "errors": [],+   "metadataOk": false,+   "metadataWarnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "LAN7800",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "LAN7800"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "CH340G",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "CH340G"+    },+    {+     "code": "deviceset_no_description",+     "detail": "No <description>. Fusion's Content Manager shows this to whoever places the part; without it they are picking on a symbol outline alone.",+     "deviceset": "STM32L431RCT6TR"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "STM32L431RCT6TR",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "STM32L431RCT6TR"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "ABM8-16.000MHZ-B2-T",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "IN-S63BTG",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "IN-S63BTG"+    },+    {+     "code": "symbol_pins_stacked",+     "detail": "pins 'G' and 'G@2' sit at the same point (0.0, -5.08); one hides the other",+     "symbol": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '1' and '48' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '12' and '13' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '24' and '25' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '36' and '37' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '7' and '8' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '9' and '10' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '23' and '24' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '25' and '26' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '39' and '40' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '41' and '42' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '55' and '56' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '57' and '58' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "generic_description",+     "detail": "description 'Four pin crystal, GND on pins 2 and 4' says the class, not the part (value, package, maker)",+     "deviceset": "ABM8-16.000MHZ-B2-T"+    },+    {+     "code": "generic_description",+     "detail": "description 'Light emitting diode' says the class, not the part (value, package, maker)",+     "deviceset": "IN-S63BTG"+    }+   ],+   "ok": true,+   "partTotals": {+    "devicesets": 5,+    "variants": 5,+    "withDistributor": 0,+    "withMpn": 0+   }+  }+ },+ "IN-S63BTG.lbr": {+  "library_parts": {+   "_hint": "1 deviceset(s), 1 orderable variant(s); 0 carry an MPN and 0 a distributor PN. 2 warning(s) in `warnings` - these do NOT stop the library opening, but an EE cannot order from it as-is. The full orderable identity is deviceset + device + technology (e.g. TJA1050 + T + -AUTO), because EAGLE puts package variants on <device> and grade/temperature variants on <technology> - which is how two variants share geometry and remain different parts.",+   "attributeNames": [+    "MPN",+    "MANUFACTURER",+    "MOUSER_PN",+    "DIGIKEY_PN",+    "LCSC_PN"+   ],+   "devicesets": [+    {+     "description": "Light emitting diode",+     "name": "IN-S63BTG",+     "variants": [+      {+       "attributes": {},+       "device": "",+       "fullName": "IN-S63BTG",+       "missing": [+        "MPN",+        "MANUFACTURER",+        "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+       ],+       "package": "IN-S63BTG",+       "technology": ""+      }+     ]+    }+   ],+   "libraryPath": "IN-S63BTG.lbr",+   "metadataOk": false,+   "success": true,+   "totals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "IN-S63BTG",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "IN-S63BTG"+    },+    {+     "code": "generic_description",+     "detail": "description 'Light emitting diode' says the class, not the part (value, package, maker)",+     "deviceset": "IN-S63BTG"+    }+   ]+  },+  "lint": {+   "ok": false,+   "totals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "IN-S63BTG",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "IN-S63BTG"+    },+    {+     "code": "generic_description",+     "detail": "description 'Light emitting diode' says the class, not the part (value, package, maker)",+     "deviceset": "IN-S63BTG"+    }+   ]+  },+  "preflight": {+   "_metadataHint": "The library will open, but 2 part-metadata warning(s) mean an EE cannot order from it: MPN / MANUFACTURER / a distributor PN (MOUSER_PN, DIGIKEY_PN, LCSC_PN) are what this bridge's own BOM export reads, and variants that share geometry are told apart ONLY by these. See fusion_library_parts for the per-variant breakdown.",+   "checks": [+    {+     "check": "file-readable",+     "detail": "7259 bytes",+     "ok": true+    },+    {+     "check": "size-sane",+     "detail": "7259 bytes - a real EAGLE library is never this small (truncated transfer?)",+     "ok": true+    },+    {+     "check": "xml-parses",+     "detail": "",+     "ok": true+    },+    {+     "check": "root-is-eagle",+     "detail": "root is <eagle>",+     "ok": true+    },+    {+     "check": "packages-nonempty",+     "detail": "1 package(s)",+     "ok": true+    },+    {+     "check": "symbols-present",+     "detail": "1 symbol(s)",+     "ok": true+    },+    {+     "check": "text-align-valid",+     "detail": "all <text> align values are EAGLE values",+     "ok": true+    },+    {+     "check": "devicesets-present",+     "detail": "1 deviceset(s)",+     "ok": true+    },+    {+     "check": "connects-resolve",+     "detail": "2 connect(s), all resolving",+     "ok": true+    },+    {+     "check": "pads-unique",+     "detail": "all pad names unique per package",+     "ok": true+    }+   ],+   "errors": [],+   "metadataOk": false,+   "metadataWarnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "IN-S63BTG",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "IN-S63BTG"+    },+    {+     "code": "generic_description",+     "detail": "description 'Light emitting diode' says the class, not the part (value, package, maker)",+     "deviceset": "IN-S63BTG"+    }+   ],+   "ok": true,+   "partTotals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   }+  }+ },+ "LAN7800.lbr": {+  "library_parts": {+   "_hint": "1 deviceset(s), 1 orderable variant(s); 0 carry an MPN and 0 a distributor PN. 5 warning(s) in `warnings` - these do NOT stop the library opening, but an EE cannot order from it as-is. The full orderable identity is deviceset + device + technology (e.g. TJA1050 + T + -AUTO), because EAGLE puts package variants on <device> and grade/temperature variants on <technology> - which is how two variants share geometry and remain different parts.",+   "attributeNames": [+    "MPN",+    "MANUFACTURER",+    "MOUSER_PN",+    "DIGIKEY_PN",+    "LCSC_PN"+   ],+   "devicesets": [+    {+     "description": "Microchip LAN7800 SuperSpeed USB 3.1 Gen1 to 10/100/1000 Ethernet Controller, QFN-48",+     "name": "LAN7800",+     "variants": [+      {+       "attributes": {},+       "device": "",+       "fullName": "LAN7800",+       "missing": [+        "MPN",+        "MANUFACTURER",+        "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+       ],+       "package": "QFN-48_7x7mm",+       "technology": ""+      }+     ]+    }+   ],+   "libraryPath": "LAN7800.lbr",+   "metadataOk": false,+   "success": true,+   "totals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "LAN7800",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "LAN7800"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '1' and '48' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '12' and '13' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '24' and '25' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '36' and '37' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    }+   ]+  },+  "lint": {+   "ok": false,+   "totals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "LAN7800",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "LAN7800"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '1' and '48' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '12' and '13' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '24' and '25' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '36' and '37' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    }+   ]+  },+  "preflight": {+   "_metadataHint": "The library will open, but 5 part-metadata warning(s) mean an EE cannot order from it: MPN / MANUFACTURER / a distributor PN (MOUSER_PN, DIGIKEY_PN, LCSC_PN) are what this bridge's own BOM export reads, and variants that share geometry are told apart ONLY by these. See fusion_library_parts for the per-variant breakdown.",+   "checks": [+    {+     "check": "file-readable",+     "detail": "18585 bytes",+     "ok": true+    },+    {+     "check": "size-sane",+     "detail": "18585 bytes - a real EAGLE library is never this small (truncated transfer?)",+     "ok": true+    },+    {+     "check": "xml-parses",+     "detail": "",+     "ok": true+    },+    {+     "check": "root-is-eagle",+     "detail": "root is <eagle>",+     "ok": true+    },+    {+     "check": "packages-nonempty",+     "detail": "1 package(s)",+     "ok": true+    },+    {+     "check": "symbols-present",+     "detail": "1 symbol(s)",+     "ok": true+    },+    {+     "check": "text-align-valid",+     "detail": "all <text> align values are EAGLE values",+     "ok": true+    },+    {+     "check": "devicesets-present",+     "detail": "1 deviceset(s)",+     "ok": true+    },+    {+     "check": "connects-resolve",+     "detail": "49 connect(s), all resolving",+     "ok": true+    },+    {+     "check": "pads-unique",+     "detail": "all pad names unique per package",+     "ok": true+    }+   ],+   "errors": [],+   "metadataOk": false,+   "metadataWarnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "LAN7800",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "LAN7800"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '1' and '48' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '12' and '13' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '24' and '25' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '36' and '37' are only 0.11 mm apart",+     "package": "QFN-48_7x7mm"+    }+   ],+   "ok": true,+   "partTotals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   }+  }+ },+ "STM32L431RCT6TR.lbr": {+  "library_parts": {+   "_hint": "1 deviceset(s), 1 orderable variant(s); 0 carry an MPN and 0 a distributor PN. 10 warning(s) in `warnings` - these do NOT stop the library opening, but an EE cannot order from it as-is. The full orderable identity is deviceset + device + technology (e.g. TJA1050 + T + -AUTO), because EAGLE puts package variants on <device> and grade/temperature variants on <technology> - which is how two variants share geometry and remain different parts.",+   "attributeNames": [+    "MPN",+    "MANUFACTURER",+    "MOUSER_PN",+    "DIGIKEY_PN",+    "LCSC_PN"+   ],+   "devicesets": [+    {+     "description": "",+     "name": "STM32L431RCT6TR",+     "variants": [+      {+       "attributes": {},+       "device": "",+       "fullName": "STM32L431RCT6TR",+       "missing": [+        "MPN",+        "MANUFACTURER",+        "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+       ],+       "package": "LQFP-64_10x10mm_P0.5mm",+       "technology": ""+      }+     ]+    }+   ],+   "libraryPath": "STM32L431RCT6TR.lbr",+   "metadataOk": false,+   "success": true,+   "totals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "deviceset_no_description",+     "detail": "No <description>. Fusion's Content Manager shows this to whoever places the part; without it they are picking on a symbol outline alone.",+     "deviceset": "STM32L431RCT6TR"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "STM32L431RCT6TR",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "STM32L431RCT6TR"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '7' and '8' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '9' and '10' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '23' and '24' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '25' and '26' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '39' and '40' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '41' and '42' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '55' and '56' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '57' and '58' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    }+   ]+  },+  "lint": {+   "ok": false,+   "totals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "deviceset_no_description",+     "detail": "No <description>. Fusion's Content Manager shows this to whoever places the part; without it they are picking on a symbol outline alone.",+     "deviceset": "STM32L431RCT6TR"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "STM32L431RCT6TR",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "STM32L431RCT6TR"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '7' and '8' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '9' and '10' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '23' and '24' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '25' and '26' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '39' and '40' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '41' and '42' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '55' and '56' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '57' and '58' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    }+   ]+  },+  "preflight": {+   "_metadataHint": "The library will open, but 10 part-metadata warning(s) mean an EE cannot order from it: MPN / MANUFACTURER / a distributor PN (MOUSER_PN, DIGIKEY_PN, LCSC_PN) are what this bridge's own BOM export reads, and variants that share geometry are told apart ONLY by these. See fusion_library_parts for the per-variant breakdown.",+   "checks": [+    {+     "check": "file-readable",+     "detail": "20255 bytes",+     "ok": true+    },+    {+     "check": "size-sane",+     "detail": "20255 bytes - a real EAGLE library is never this small (truncated transfer?)",+     "ok": true+    },+    {+     "check": "xml-parses",+     "detail": "",+     "ok": true+    },+    {+     "check": "root-is-eagle",+     "detail": "root is <eagle>",+     "ok": true+    },+    {+     "check": "packages-nonempty",+     "detail": "1 package(s)",+     "ok": true+    },+    {+     "check": "symbols-present",+     "detail": "1 symbol(s)",+     "ok": true+    },+    {+     "check": "text-align-valid",+     "detail": "all <text> align values are EAGLE values",+     "ok": true+    },+    {+     "check": "devicesets-present",+     "detail": "1 deviceset(s)",+     "ok": true+    },+    {+     "check": "connects-resolve",+     "detail": "64 connect(s), all resolving",+     "ok": true+    },+    {+     "check": "pads-unique",+     "detail": "all pad names unique per package",+     "ok": true+    }+   ],+   "errors": [],+   "metadataOk": false,+   "metadataWarnings": [+    {+     "code": "deviceset_no_description",+     "detail": "No <description>. Fusion's Content Manager shows this to whoever places the part; without it they are picking on a symbol outline alone.",+     "deviceset": "STM32L431RCT6TR"+    },+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "STM32L431RCT6TR",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "STM32L431RCT6TR"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '7' and '8' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '9' and '10' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '23' and '24' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '25' and '26' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '39' and '40' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '41' and '42' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '55' and '56' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    },+    {+     "code": "pad_clearance",+     "detail": "pads '57' and '58' are only 0.20 mm apart",+     "package": "LQFP-64_10x10mm_P0.5mm"+    }+   ],+   "ok": true,+   "partTotals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   }+  }+ },+ "__missing__": {+  "library_parts": {+   "error": "[Errno 2] No such file or directory: 'C:/nope/missing.lbr'",+   "errorCode": "library_unreadable",+   "libraryPath": "C:/nope/missing.lbr",+   "success": false+  },+  "no_path": {+   "acceptedArgs": [+    "libraryPath",+    "lbrPath",+    "filePath",+    "path"+   ],+   "error": "No library path given; the file was never examined.",+   "errorCode": "no_path_argument",+   "success": false+  },+  "preflight": {+   "checks": [+    {+     "check": "file-readable",+     "detail": "[Errno 2] No such file or directory: 'C:/nope/missing.lbr'",+     "ok": false+    }+   ],+   "errors": [+    "file-readable: [Errno 2] No such file or directory: 'C:/nope/missing.lbr'"+   ],+   "ok": false+  }+ },+ "syn_deadconnect.lbr": {+  "library_parts": {+   "_hint": "1 deviceset(s), 1 orderable variant(s); 0 carry an MPN and 0 a distributor PN. 1 warning(s) in `warnings` - these do NOT stop the library opening, but an EE cannot order from it as-is. The full orderable identity is deviceset + device + technology (e.g. TJA1050 + T + -AUTO), because EAGLE puts package variants on <device> and grade/temperature variants on <technology> - which is how two variants share geometry and remain different parts.",+   "attributeNames": [+    "MPN",+    "MANUFACTURER",+    "MOUSER_PN",+    "DIGIKEY_PN",+    "LCSC_PN"+   ],+   "devicesets": [+    {+     "description": "USB serial converter, UART, SOIC-16",+     "name": "CH340G",+     "variants": [+      {+       "attributes": {},+       "device": "",+       "fullName": "CH340G",+       "missing": [+        "MPN",+        "MANUFACTURER",+        "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+       ],+       "package": "SOIC-16_3.9x9.9mm_P1.27mm",+       "technology": ""+      }+     ]+    }+   ],+   "libraryPath": "syn_deadconnect.lbr",+   "metadataOk": false,+   "success": true,+   "totals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "CH340G",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "CH340G"+    }+   ]+  },+  "lint": {+   "ok": false,+   "totals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "CH340G",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "CH340G"+    }+   ]+  },+  "preflight": {+   "_metadataHint": "The library will open, but 1 part-metadata warning(s) mean an EE cannot order from it: MPN / MANUFACTURER / a distributor PN (MOUSER_PN, DIGIKEY_PN, LCSC_PN) are what this bridge's own BOM export reads, and variants that share geometry are told apart ONLY by these. See fusion_library_parts for the per-variant breakdown.",+   "checks": [+    {+     "check": "file-readable",+     "detail": "9781 bytes",+     "ok": true+    },+    {+     "check": "size-sane",+     "detail": "9781 bytes - a real EAGLE library is never this small (truncated transfer?)",+     "ok": true+    },+    {+     "check": "xml-parses",+     "detail": "",+     "ok": true+    },+    {+     "check": "root-is-eagle",+     "detail": "root is <eagle>",+     "ok": true+    },+    {+     "check": "packages-nonempty",+     "detail": "1 package(s)",+     "ok": true+    },+    {+     "check": "symbols-present",+     "detail": "1 symbol(s)",+     "ok": true+    },+    {+     "check": "text-align-valid",+     "detail": "all <text> align values are EAGLE values",+     "ok": true+    },+    {+     "check": "devicesets-present",+     "detail": "1 deviceset(s)",+     "ok": true+    },+    {+     "check": "connects-resolve",+     "detail": "connects reference missing pins",+     "ok": false+    },+    {+     "check": "pads-unique",+     "detail": "all pad names unique per package",+     "ok": true+    }+   ],+   "errors": [+    "connects-resolve: connects reference missing pins"+   ],+   "metadataOk": false,+   "metadataWarnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "CH340G",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "CH340G"+    }+   ],+   "ok": false,+   "partTotals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   }+  }+ },+ "syn_duppad.lbr": {+  "library_parts": {+   "_hint": "1 deviceset(s), 1 orderable variant(s); 0 carry an MPN and 0 a distributor PN. 1 warning(s) in `warnings` - these do NOT stop the library opening, but an EE cannot order from it as-is. The full orderable identity is deviceset + device + technology (e.g. TJA1050 + T + -AUTO), because EAGLE puts package variants on <device> and grade/temperature variants on <technology> - which is how two variants share geometry and remain different parts.",+   "attributeNames": [+    "MPN",+    "MANUFACTURER",+    "MOUSER_PN",+    "DIGIKEY_PN",+    "LCSC_PN"+   ],+   "devicesets": [+    {+     "description": "USB serial converter, UART, SOIC-16",+     "name": "CH340G",+     "variants": [+      {+       "attributes": {},+       "device": "",+       "fullName": "CH340G",+       "missing": [+        "MPN",+        "MANUFACTURER",+        "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+       ],+       "package": "SOIC-16_3.9x9.9mm_P1.27mm",+       "technology": ""+      }+     ]+    }+   ],+   "libraryPath": "syn_duppad.lbr",+   "metadataOk": false,+   "success": true,+   "totals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "CH340G",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "CH340G"+    }+   ]+  },+  "lint": {+   "ok": false,+   "totals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "CH340G",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "CH340G"+    }+   ]+  },+  "preflight": {+   "_metadataHint": "The library will open, but 1 part-metadata warning(s) mean an EE cannot order from it: MPN / MANUFACTURER / a distributor PN (MOUSER_PN, DIGIKEY_PN, LCSC_PN) are what this bridge's own BOM export reads, and variants that share geometry are told apart ONLY by these. See fusion_library_parts for the per-variant breakdown.",+   "checks": [+    {+     "check": "file-readable",+     "detail": "9776 bytes",+     "ok": true+    },+    {+     "check": "size-sane",+     "detail": "9776 bytes - a real EAGLE library is never this small (truncated transfer?)",+     "ok": true+    },+    {+     "check": "xml-parses",+     "detail": "",+     "ok": true+    },+    {+     "check": "root-is-eagle",+     "detail": "root is <eagle>",+     "ok": true+    },+    {+     "check": "packages-nonempty",+     "detail": "1 package(s)",+     "ok": true+    },+    {+     "check": "symbols-present",+     "detail": "1 symbol(s)",+     "ok": true+    },+    {+     "check": "text-align-valid",+     "detail": "all <text> align values are EAGLE values",+     "ok": true+    },+    {+     "check": "devicesets-present",+     "detail": "1 deviceset(s)",+     "ok": true+    },+    {+     "check": "connects-resolve",+     "detail": "connects reference missing pads",+     "ok": false+    },+    {+     "check": "pads-unique",+     "detail": "duplicate pad names - Fusion's importer refuses the ENTIRE library over these: SOIC-16_3.9x9.9mm_P1.27mm/1 x2. Rename to e.g. 17, 17@2, 17@3...",+     "ok": false+    }+   ],+   "errors": [+    "connects-resolve: connects reference missing pads",+    "pads-unique: duplicate pad names - Fusion's importer refuses the ENTIRE library over these: SOIC-16_3.9x9.9mm_P1.27mm/1 x2. Rename to e.g. 17, 17@2, 17@3..."+   ],+   "metadataOk": false,+   "metadataWarnings": [+    {+     "code": "variant_missing_part_numbers",+     "detail": "Missing MPN, MANUFACTURER, a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN). This bridge's own BOM export reads these exact attribute names, so the BOM comes out blank for this part.",+     "deviceset": "CH340G",+     "missing": [+      "MPN",+      "MANUFACTURER",+      "a distributor PN (MOUSER_PN/DIGIKEY_PN/LCSC_PN)"+     ],+     "variant": "CH340G"+    }+   ],+   "ok": false,+   "partTotals": {+    "devicesets": 1,+    "variants": 1,+    "withDistributor": 0,+    "withMpn": 0+   }+  }+ },+ "syn_noteagle.lbr": {+  "library_parts": {+   "_hint": "0 deviceset(s), 0 orderable variant(s); 0 carry an MPN and 0 a distributor PN. No metadata warnings. The full orderable identity is deviceset + device + technology (e.g. TJA1050 + T + -AUTO), because EAGLE puts package variants on <device> and grade/temperature variants on <technology> - which is how two variants share geometry and remain different parts.",+   "attributeNames": [+    "MPN",+    "MANUFACTURER",+    "MOUSER_PN",+    "DIGIKEY_PN",+    "LCSC_PN"+   ],+   "devicesets": [],+   "libraryPath": "syn_noteagle.lbr",+   "metadataOk": true,+   "success": true,+   "totals": {+    "devicesets": 0,+    "variants": 0,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": []+  },+  "lint": {+   "ok": true,+   "totals": {+    "devicesets": 0,+    "variants": 0,+    "withDistributor": 0,+    "withMpn": 0+   },+   "warnings": []+  },+  "preflight": {+   "checks": [+    {+     "check": "file-readable",+     "detail": "348 bytes",+     "ok": true+    },+    {+     "check": "size-sane",+     "detail": "348 bytes - a real EAGLE library is never this small (truncated transfer?)",+     "ok": true+    },+    {+     "check": "xml-parses",+     "detail": "",+     "ok": true+    },+    {+     "check": "root-is-eagle",+     "detail": "root is <html>",+     "ok": false+    },+    {+     "check": "packages-nonempty",+     "detail": "EMPTY <packages> - Fusion opens this to a silent 'No Components' library",+     "ok": false+    },+    {+     "check": "symbols-present",+     "detail": "0 symbol(s)",+     "ok": false+    },+    {+     "check": "text-align-valid",+     "detail": "all <text> align values are EAGLE values",+     "ok": true+    },+    {+     "check": "devicesets-present",+     "detail": "no <deviceset> - EDIT <name>.dev will raise Fusion's Create-new prompt",+     "ok": false+    },+    {+     "check": "pads-unique",+     "detail": "all pad names unique per package",+     "ok": true+    }+   ],+   "errors": [+    "root-is-eagle: root is <html>",+    "packages-nonempty: EMPTY <packages> - Fusion opens this to a silent 'No Components' library",+    "symbols-present: 0 symbol(s)",+    "devicesets-present: no <deviceset> - EDIT <name>.dev will raise Fusion's Create-new prompt"+   ],+   "metadataOk": true,+   "metadataWarnings": [],+   "ok": false,+   "partTotals": {+    "devicesets": 0,+    "variants": 0,+    "withDistributor": 0,+    "withMpn": 0+   }+  }+ },+ "syn_truncated.lbr": {+  "library_parts": {+   "error": "unclosed token: line 3, column 0",+   "errorCode": "library_unreadable",+   "libraryPath": "syn_truncated.lbr",+   "success": false+  },+  "lint": {+   "error": "unclosed token: line 3, column 0",+   "ok": false,+   "warnings": []+  },+  "preflight": {+   "checks": [+    {+     "check": "file-readable",+     "detail": "77 bytes",+     "ok": true+    },+    {+     "check": "size-sane",+     "detail": "77 bytes - a real EAGLE library is never this small (truncated transfer?)",+     "ok": false+    }+   ],+   "errors": [+    "size-sane: 77 bytes - a real EAGLE library is never this small (truncated transfer?)"+   ],+   "ok": false+  }+ }+}\ No newline at end of file
tests/run_verb_tests.py+41−2
@@ -140,7 +140,6 @@ t("fusion_set_auto_update", "daily", {}, {"ok": True}, "no args = read-only") t("fusion_assembly_bom", "daily", {}, {"pred": "ok_or_budget"},   "either a BOM (small doc) or the fast bom_walk_budget_exceeded refusal (3D PCB) - both are the verb working as designed") t("fusion_dismiss_blocking_dialogs", "daily", {}, {"ok": True}, "no-op when nothing is blocking")-t("fusion_load_design_rules", "manual", note="mutates the board's DRC rules")  # ── stage 3: document flows on the canonical board ────────────────────────── # ORDER IS THE TEST: 2D-context verbs (Electron.*, gerbers/BOM/CPL, stackup,@@ -247,6 +246,36 @@ for v in ("fusion_stop", "fusion_kill", "fusion_signin", "fusion_signin_2fa", "f           "fusion_install_fusion", "fusion_notify_owner", "fusion_open_by_urn_note"):     t(v, "manual", note="lifecycle/human-wall verbs; manual tier") +# ── triage of 2026-09-03 (the verbs the daily run flagged as untriaged) ──────────────────+# Read-only bridge/state verbs run daily; anything that drives Fusion's GUI or writes to the+# cloud is weekly (exercised by the drills) or manual with the reason recorded.+t("fusion_action_ledger", "daily", {"limit": 5}, {"ok": True})+t("fusion_addin_status", "daily", {}, {"ok": True})+t("fusion_alerts", "daily", {"sinceSec": 300}, {"ok": True, "fields": ["alerts"]})+t("fusion_verb_times", "daily", {}, {"ok": True})+t("fusion_live_feed", "daily", {"action": "poll"}, {"ok": True}, "one poll of the feed; no waiting")+t("fusion_prove_result", "daily", {}, {}, "shape only: the last completed run, or an honest 'none yet'")+t("fusion_set_driving_badge", "daily", {}, {"ok": True}, "no-args form is a pure pref read")+t("fusion_set_window_sizing", "daily", {}, {"ok": True}, "no-args form is a pure pref read")+t("fusion_cancel_run", "negative", {"runId": "no-such-run"}, {"errorCode": None, "pred": None}, "expect an honest 'unknown runId'")+t("fusion_preflight_lbr", "negative", {"libraryPath": "C:/nope/missing.lbr"}, {"errorCode": None, "pred": None}, "expect a refusal naming the missing file")+t("fusion_library_parts", "negative", {"libraryPath": "C:/nope/missing.lbr"}, {"errorCode": None, "pred": None}, "expect a refusal naming the missing file")+t("fusion_take_screenshot", "weekly", {}, {"ok": True}, "needs Fusion up; hwnd-targeted background capture")+t("fusion_activate_document", "weekly", {"name": "Untitled"}, {}, "needs an open document; shape only")+t("fusion_library_manager", "weekly", {}, {"ok": True}, "drives the GUI (Library Manager), reads nothing destructive")+t("fusion_plan_library_import", "weekly", {"parts": []}, {}, "planner with no parts answers honestly; the drills cover the real thing")+t("fusion_create_library", "manual", note="the five-part drill covers it (fusion_install_library_bundle -> prove)")+t("fusion_install_library_bundle", "manual", note="the five-part drill covers it; needs a bundle zip on the box")+t("fusion_managed_library_create", "manual", note="creates a managed library on the hub (cloud write)")+t("fusion_managed_library_link", "manual", note="drives LIBRARY.IO dialogs against the hub")+t("fusion_managed_library_pull", "manual", note="drives LIBRARY.IO dialogs against the hub")+t("fusion_managed_library_push", "manual", note="drives LIBRARY.IO dialogs; cloud write")+t("fusion_managed_library_unlink", "manual", note="drives LIBRARY.IO dialogs against the hub")+t("fusion_reset_workspace", "manual", note="closes the user's documents")+t("fusion_restart_for_addin", "manual", note="restarts Fusion")+t("fusion_save_to_cloud", "manual", note="cloud write; exercised by the drills")+t("fusion_web_signin", "manual", note="opens a browser on the user's machine")+ PLAN = P  # -- Section map ------------------------------------------------------------@@ -267,6 +296,16 @@ PLAN = P SECTION_ORDER = ["gui-driving", "cad-3d-bom", "mcp-server", "python-addin",                  "aps", "auth-tokens", "exports", "bridge-lifecycle"] _SECTION_BY_VERB = {+    "fusion_action_ledger": "bridge-lifecycle", "fusion_addin_status": "python-addin", "fusion_alerts": "gui-driving",+    "fusion_verb_times": "bridge-lifecycle", "fusion_live_feed": "bridge-lifecycle", "fusion_prove_result": "cad-3d-bom",+    "fusion_set_driving_badge": "bridge-lifecycle", "fusion_set_window_sizing": "gui-driving", "fusion_cancel_run": "cad-3d-bom",+    "fusion_preflight_lbr": "cad-3d-bom", "fusion_library_parts": "cad-3d-bom", "fusion_take_screenshot": "gui-driving",+    "fusion_activate_document": "gui-driving", "fusion_library_manager": "gui-driving", "fusion_plan_library_import": "cad-3d-bom",+    "fusion_create_library": "cad-3d-bom", "fusion_install_library_bundle": "cad-3d-bom",+    "fusion_managed_library_create": "aps", "fusion_managed_library_link": "aps", "fusion_managed_library_pull": "aps",+    "fusion_managed_library_push": "aps", "fusion_managed_library_unlink": "aps",+    "fusion_reset_workspace": "gui-driving", "fusion_restart_for_addin": "bridge-lifecycle",+    "fusion_save_to_cloud": "aps", "fusion_web_signin": "auth-tokens",     "fusion_start": "gui-driving", "fusion_stop": "gui-driving", "fusion_kill": "gui-driving",     "fusion_get_app_state": "gui-driving", "fusion_dismiss_blocking_dialogs": "gui-driving",     "fusion_open_cloud_file": "gui-driving", "fusion_open_design": "gui-driving",@@ -299,7 +338,7 @@ _SECTION_BY_VERB = {     "fusion_export_cpl": "exports", "fusion_export_optimized_glb": "exports",     "fusion_fetch_optimized_glb": "exports", "fusion_board_stackup": "exports",     "fusion_web_export": "exports",-    "fusion_load_design_rules": "exports", "fusion_set_design_rules": "exports",+    "fusion_set_design_rules": "exports",     "fusion_readiness": "bridge-lifecycle", "fusion_describe": "bridge-lifecycle",     "fusion_uninstall": "bridge-lifecycle", "fusion_set_seat_takeover": "bridge-lifecycle", "fusion_set_auto_update": "bridge-lifecycle", }
tools/promotions.logadded+1
@@ -0,0 +1 @@+2026-09-03T16:09:23Z	promoted 1.9.351	attest: uh oh, we better get this out there and working